thug/Code/Gel/Collision/Collision.cpp

2699 lines
87 KiB
C++
Raw Normal View History

2016-02-13 21:39:12 +00:00
/*****************************************************************************
** **
** Neversoft Entertainment. **
** **
** Copyright (C) 1999 - All Rights Reserved **
** **
******************************************************************************
** **
** Project: PS2 **
** **
** Module: Nx **
** **
** File name: gel\collision\collide.cpp **
** **
** Created by: 02/20/02 - grj **
** **
** Description: **
** **
*****************************************************************************/
/*****************************************************************************
** Includes **
*****************************************************************************/
#include <core/defines.h>
#include <gel/collision/collision.h>
#include <gel/collision/colltridata.h>
#include <gel/collision/movcollman.h>
#include <gel/collision/collcache.h>
#include <gel/collision/batchtricoll.h>
#include <engine/SuperSector.h>
#include <gel/object/compositeobject.h>
#include <gfx/nx.h>
#include <gfx/nxflags.h> // for face flag stuff
#include <gfx/debuggfx.h>
/*****************************************************************************
** DBG Information **
*****************************************************************************/
namespace Nx
{
/*****************************************************************************
** Externals **
*****************************************************************************/
/*****************************************************************************
** Defines **
*****************************************************************************/
#ifdef __PLAT_NGPS__
#define USE_VU0_MICROMODE
#endif
#define PRINT_CACHE_HITS 0
/*****************************************************************************
** Private Types **
*****************************************************************************/
/*****************************************************************************
** Private Data **
*****************************************************************************/
Mth::Matrix CCollStatic::sOrient;
Mth::Vector CCollStatic::sWorldPos;
/*****************************************************************************
** Public Data **
*****************************************************************************/
/*****************************************************************************
** Private Prototypes **
*****************************************************************************/
/*****************************************************************************
** Private Functions **
*****************************************************************************/
static bool s_check_for_far = false; // if true then we return the farthest result
bool CCollObj::s_found_collision(const Mth::Line *p_is, CCollObj *p_coll_object, SCollSurface *p_collSurface,
float distance, CollData *p_collData)
{
Dbg_Assert( p_collData );
bool good = false;
bool updated = false;
if (s_check_for_far)
{
if( distance >= p_collData->dist )
{
good = true;
}
}
else
{
//Dbg_Message("Checking new dist %f against old dist %f", distance, p_collData->dist);
if( distance <= p_collData->dist )
{
good = true;
}
}
CollData old;
if (!good)
{
old = *p_collData;
}
// Get the user plugin offset
int index = p_collSurface->index; // the index of the surface in the object
// get the face flags from within this
// note that this uses the "index" field, which is
// an index of a face within the sector
uint32 flags = p_coll_object->GetFaceFlags(index);
// Test flags against the ignore_1 (ignore if a bit is set in flags)
// and against ignore_0 (ignore if the bit is clear in flags)
//Dbg_Message("*Flags %x ignore_0 %x ignore_1 %x", flags, p_collData->ignore_0, p_collData->ignore_1);
if (!(flags & p_collData->ignore_1) && !(~flags & p_collData->ignore_0))
{
// Setup the collData members
p_collData->coll_found = true; // found at least one (might be overrridden, but found at least one)
p_collData->surface = *p_collSurface; // Store the collision surface
p_collData->dist = distance; // set distance (will end up being the smallest dist)
p_collData->flags = flags; // Store face flags
p_collData->terrain = (ETerrainType) p_coll_object->GetFaceTerrainType(index);
p_collData->p_coll_object = p_coll_object;
p_collData->trigger = (flags& mFD_TRIGGER); // set flag if it's a trigger
p_collData->node_name = p_coll_object->GetChecksum();
// Determine the point of intersection on the triangle
// which will be in the direction of the line's vector at "distance" length
Mth::Vector coll_point, line_vec;
line_vec = p_is->m_end - p_is->m_start;
coll_point = line_vec.Scale(distance);
coll_point = p_is->m_start + coll_point;
coll_point[W] = 1.0f; // Hack to force into homogenious space. Even though the math is correct here,
// many times the line data fed to it isn't.
p_collData->surface.point = coll_point; // After looking at their implementation, I don't
// trust "point" so I recalculate it
// check for additional per-face callback
if (p_collData->callback)
{
// call the callback function, and pass it this col data
p_collData->callback(p_collData);
}
updated = true;
}
if (!good)
{
*p_collData = old;
}
return (good && updated);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollObj::sRayTriangleCollision(const Mth::Vector *rayStart, const Mth::Vector *rayDir,
Mth::Vector *v0, Mth::Vector *v1, Mth::Vector *v2, float *t)
{
#if 0
// #ifdef __PLAT_NGPS__
// Turned off assem versions to test correction to false positive issue as discussed in C code below. Dan - 1/21/3
#ifdef USE_VU0_MICROMODE
register bool result;
asm __volatile__(
"
.set noreorder
lqc2 vf06,%4 # v0
lqc2 vf08,%6 # v2
lqc2 vf07,%5 # v1
lqc2 vf04,%2 # rayStart
lqc2 vf05,%3 # rayDir
vcallms RayTriangleCollision # call microsubroutine
vnop # interlocking instruction, waits for vu0 completion
cfc2 %0,$vi02 # get boolean result from vu0
beq %0, $0, vu0RayTriDone # skip copy of t if not needed
nop
qmfc2 $12, $vf17 # move t to $8
sw $12, %1 # and write out
vu0RayTriDone:
.set reorder
": "=&r" (result), "=m" (*t) : "m" (*rayStart), "m" (*rayDir), "m" (*v0), "m" (*v1) , "m" (*v2) : "$12" );
return result;
#else
//*t = 2.0f;
bool result;
asm __volatile__(
"
.set noreorder
lqc2 vf06,0x0(%4) # v0
lqc2 vf08,0x0(%6) # v2
lqc2 vf07,0x0(%5) # v1
lqc2 vf04,0x0(%2) # rayStart
lqc2 vf05,0x0(%3) # rayDir
vsub.xyz vf10, vf08, vf06 # edge2 = v2 - v0
vsub.xyz vf09, vf07, vf06 # edge1 = v1 - v0
vsub.xyz vf13, vf04, vf06 # tvec = rayStart - v0
li.s $f4,0.00001 # EPSILON
vaddw.x vf02, vf00, vf00w # Load 1.0f to VF02x
vopmula.xyz ACC,vf05,vf10 # Cross product of ray normal and edge2 (pvec)
vopmsub.xyz vf12,vf10,vf05 # Second part of cross product
mfc1 $8,$f4
li %0,0 # store 0 for return value
#li $9,0x80 # Set the mask X sign flag
#li $10,0x10 # Set the mask W sign flag
vmul.xyz vf11,vf09,vf12 # det = edge1 * pvec [start]
vmulax.x ACC,vf09,vf12x
vmul.xyz vf15,vf13,vf12 # u = tvec * pvec [start]
qmtc2 $8, $vf03
vmadday.x ACC,vf02,vf11y
vmaddz.x vf11,vf02,vf11z # det = edge1 * pvec [ready]
vmulax.x ACC,vf02,vf15x
vmadday.x ACC,vf02,vf15y
vmaddz.x vf15,vf02,vf15z # u = tvec * pvec [ready]
vdiv Q,vf00w,vf11x # Q = 1.0f / det
vopmula.xyz ACC,vf13,vf09 # qvec = crossProd(tvec, edge1);
vopmsub.xyz vf14,vf09,vf13
vsub.x vf01,vf11,vf03 # If det < EPSILON
vnop
vnop
vnop
vnop
vnop
cfc2 $8,$vi17 # transfer if det result (MAC register)
vmulq.x vf15,vf15,Q # u = (tvec * pvec) / det
vnop
vnop
vnop
vnop
andi $8, $8, 0x80 # check result
cfc2 $9,$vi17 # transfer u result (MAC register)
bne $8, $0, vu0RayTriDone
vaddx.w vf03, vf00, vf03x # put 1 + EPSILON in VF03w
andi $9, $9, 0x80 # check result (u < 0) fail
bne $9, $0, vu0RayTriDone
#vaddq.x vf20,vf00,Q
vsubw.x vf00,vf15,vf03w # if u > 1 + EPSILON
vnop
vnop
vnop
vnop
vnop
cfc2 $8,$vi17 # transfer if u > 1 + EPSILON result (MAC register)
vmul.xyz vf16,vf05,vf14 # v = rayDir * qvec [start]
vmulax.x ACC,vf05,vf14x
andi $8, $8, 0x80 # check result (u > 1) fail
beq $8, $0, vu0RayTriDone
vnop
vmadday.x ACC,vf02,vf16
vmaddz.x vf16,vf02,vf16z # v = rayDir * qvec [ready]
vnop
vmul.xyz vf17,vf10,vf14 # t = edge2 * qvec [start]
vmulax.x ACC,vf10,vf14x
vmulq.x vf16,vf16,Q # v = (rayDir * qvec) / det;
vnop
vnop
vnop
vnop
vnop
cfc2 $8,$vi17 # transfer if v < 0 result (MAC register)
vmadday.x ACC,vf02,vf17
vmaddz.x vf17,vf02,vf17z # t = edge2 * qvec [ready]
vadd.x vf01,vf15,vf16
andi $8, $8, 0x80 # check result (v < 0) fail
bne $8, $0, vu0RayTriDone
vmulq.x vf17,vf17,Q # t = (edge2 * qvec) / det
vsubw.x vf31,vf01,vf03w # if (u+v) > 1 + EPSILON [start]
vnop
vnop
vnop
vnop
vnop
cfc2 $8,$vi17 # transfer if u+v > 1 + EPSILON result (MAC register)
vsub.x vf00,vf17,vf00
vnop
vnop
vnop
vnop
andi $8, $8, 0x80 # check result (u+v > 1 + EPSILON) fail
cfc2 $9,$vi17 # transfer if t < 0 result (MAC register)
vsubx.w vf00,vf00,vf17 # if t > 1 [start]
vnop
vnop
#vnop
#vnop
#vnop
beq $8, $0, vu0RayTriDone
andi $9, $9, 0x80 # check result (t < 0) fail
bne $9, $0, vu0RayTriDone
cfc2 $8,$vi17 # transfer if t > 1 result (MAC register)
andi $8, $8, 0x10 # check result (t > 1) fail
bne $8, $0, vu0RayTriDone
qmfc2 $9, $vf17 # move t to $9
sw $9, 0(%1) # and write out
li %0,1 # store 1 for return value
vu0RayTriDone:
.set reorder
": "=r" (result), "+r" (t) : "r" (rayStart), "r" (rayDir), "r" (v0), "r" (v1) , "r" (v2) : "$8", "$9");
//return *t <= 1.0f;
return result;
#endif // USE_VU0_MICROMODE
#else
const float EPSILON = 0.00001f;
/* find vectors for two edges sharing vert0 */
Mth::Vector edge1 = *v1 - *v0;
Mth::Vector edge2 = *v2 - *v0;
/* begin calculating determinant - also used to calculate U parameter */
Mth::Vector pvec = Mth::CrossProduct(*rayDir, edge2);
/* if determinant is near zero, ray lies in plane of triangle */
const float det = Mth::DotProduct(edge1, pvec);
#if 1 // Dan - 1/21/3
// Moved division below several false returns and reduced mult count. This probably removed the need for adding EPSILON to 1.0f for the u and v test,
// but I don't know this for sure, so left it in.
// Also, added EPSILON_2. EPSILON is of appropriate scale for comparing floats of order one to zero, but is too small for comparing floats
// on the order of the cube of a typical vertex edge. In certain special cases the dot product in the calculation of det will require the
// cancelation of very large floats, resulting in a zero which is larger than EPSILON. This was causing false positives.
// It is possible that this new larger EPSILON_2 will cause false negatives. I suppose we shall see.
// These changes HAVE NOT BEEN MADE to the assembler code versions. They continue to suffer from the over-small EPSILON issue.
const float EPSILON_2 = 0.03f;
// We are back-facing, so also throw away negative
if (det < EPSILON_2)
{
return false;
}
const float adjusted_det = (1.0f + EPSILON) * det;
/* calculate distance from vert0 to ray origin */
const Mth::Vector tvec = *rayStart - *v0;
/* calculate U parameter and test bounds */
float u = Mth::DotProduct(tvec, pvec);
if (u < 0.0f || u > adjusted_det) {
return false;
}
/* prepare to test V parameter */
const Mth::Vector qvec = Mth::CrossProduct(tvec, edge1);
/* calculate V parameter and test bounds */
float v = Mth::DotProduct(*rayDir, qvec);
if (v < 0.0f || u + v > adjusted_det) {
return false;
}
const float inv_det = 1.0f / det;
/* calculate t, ray intersects triangle */
*t = Mth::DotProduct(edge2, qvec) * inv_det;
// And update u & v, although we don't seem to use it now.
//u *= inv_det;
//v *= inv_det;
#else
// We are back-facing, so also throw away negative
if (det < EPSILON)
{
return false;
}
const float inv_det = 1.0f / det;
/* calculate distance from vert0 to ray origin */
const Mth::Vector tvec = *rayStart - *v0;
/* calculate U parameter and test bounds */
float u = Mth::DotProduct(tvec, pvec) * inv_det;
if (u < 0.0f || u > (1.0f + EPSILON)) {
return false;
}
/* prepare to test V parameter */
const Mth::Vector qvec = Mth::CrossProduct(tvec, edge1);
/* calculate V parameter and test bounds */
float v = Mth::DotProduct(*rayDir, qvec) * inv_det;
if (v < 0.0f || u + v > (1.0f + EPSILON)) {
return false;
}
/* calculate t, ray intersects triangle */
*t = Mth::DotProduct(edge2, qvec) * inv_det;
// And update u & v, although we don't seem to use it now.
//u *= inv_det;
//v *= inv_det;
#endif
return (*t <= 1.0f) && (*t >= 0.0f);
#endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollObj::s2DLineLineCollision ( float p_start_X, float p_start_Y, float p_delta_X, float p_delta_Y, float q_start_X, float q_start_Y, float q_delta_X, float q_delta_Y, float *s )
{
// determines collision between two lines (p and q) in 2D; reports the interval along p at which the collision occurs
// Antonio's method; Real-Time Rendering, p 337
const float EPSILON = 0.00000000001f;
float a_X = q_delta_X;
float a_Y = q_delta_Y;
float b_X = p_delta_X;
float b_Y = p_delta_Y;
float c_X = p_start_X - q_start_X;
float c_Y = p_start_Y - q_start_Y;
float d = c_X * -a_Y + c_Y * a_X;
float e = c_X * -b_Y + c_Y * b_X;
float f = a_X * -b_Y + a_Y * b_X;
if (f > 0.0f)
{
if (d < 0.0f || d > f) return false;
}
else
{
if (d > 0.0f || d < f) return false;
}
if (f > 0.0f)
{
if (e < 0.0f || e > f) return false;
}
else
{
if (e > 0.0f || e < f) return false;
}
if (Mth::Abs(f) < EPSILON)
{
*s = 0.0f;
return true;
}
*s = d / f;
return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollObj::sRectangleTriangleCollision(const Mth::Rectangle *rect, const Mth::Vector *v0, const Mth::Vector *v1, const Mth::Vector *v2, Mth::Vector p_collision_endpoints[2], ETriangleEdgeType p_collision_triangle_edges[2] )
{
// ERIT method; Real-Time Rendering, p 317
const float EPSILON = 0.00001f;
// calculate the plane equation of the rectangle
Mth::Vector rectangle_n = Mth::CrossProduct(rect->m_first_edge, rect->m_second_edge);
float rectangle_d = -Mth::DotProduct(rectangle_n, rect->m_corner);
// calculate the distance (times rectangle_n squared) of the triangle vertices from the rectangle's plane
float d0 = Mth::DotProduct(rectangle_n, *v0) + rectangle_d;
float d1 = Mth::DotProduct(rectangle_n, *v1) + rectangle_d;
float d2 = Mth::DotProduct(rectangle_n, *v2) + rectangle_d;
// if all vertices are on the same side of the plane, we reject
if (d0 > 0.0f && d1 > 0.0f && d2 > 0.0f) return false;
if (d0 < 0.0f && d1 < 0.0f && d2 < 0.0f) return false;
// snap verticies near the plane to the plane
if (Mth::Abs(d0) < EPSILON)
{
d0 = 0.0f;
}
if (Mth::Abs(d1) < EPSILON)
{
d1 = 0.0f;
}
if (Mth::Abs(d2) < EPSILON)
{
d2 = 0.0f;
}
// find the two points (p and q) on the plane of the rectangle which are the endpoints of the triangle's interception with the rectangle's plane
Mth::Vector p, q;
ETriangleEdgeType p_edge, q_edge;
if ((d0 <= 0.0f && d1 >= 0.0f) || (d0 >= 0.0f && d1 <= 0.0f))
{
p_edge = TRIANGLE_EDGE_V0_V1;
if (Mth::Abs(d0 - d1) > EPSILON)
{
p = *v0 + (*v1 - *v0) * d0 / (d0 - d1);
}
else
{
p = *v0;
}
if ((d1 <= 0.0f && d2 >= 0.0f) || (d1 >= 0.0f && d2 <= 0.0f))
{
q_edge = TRIANGLE_EDGE_V1_V2;
if (Mth::Abs(d1 - d2) > EPSILON)
{
q = *v1 + (*v2 - *v1) * d1 / (d1 - d2);
}
else
{
q = *v1;
}
}
else
{
q_edge = TRIANGLE_EDGE_V2_V0;
if (Mth::Abs(d2 - d0) > EPSILON)
{
q = *v2 + (*v0 - *v2) * d2 / (d2 - d0);
}
else
{
q = *v2;
}
}
}
else
{
p_edge = TRIANGLE_EDGE_V1_V2;
if (Mth::Abs(d1 - d2) > EPSILON)
{
p = *v1 + (*v2 - *v1) * d1 / (d1 - d2);
}
else
{
p = *v1;
}
q_edge = TRIANGLE_EDGE_V2_V0;
if (Mth::Abs(d0 - d2) > EPSILON)
{
q = *v0 + (*v2 - *v0) * d0 / (d0 - d2);
}
else
{
q = *v0;
}
}
// project the rectangle and the intersection endpoints into the coordinate plane which maximizes the area of the rectangle's projection
float r_corner_S, r_corner_T;
float r_first_edge_S, r_first_edge_T;
float r_second_edge_S, r_second_edge_T;
float p_S, p_T;
float q_S, q_T;
if (Mth::Abs(rectangle_n[X]) < Mth::Abs(rectangle_n[Z]) && Mth::Abs(rectangle_n[Y]) < Mth::Abs(rectangle_n[Z]))
{
// project onto XY plane
r_corner_S = rect->m_corner[X];
r_corner_T = rect->m_corner[Y];
r_first_edge_S = rect->m_first_edge[X];
r_first_edge_T = rect->m_first_edge[Y];
r_second_edge_S = rect->m_second_edge[X];
r_second_edge_T = rect->m_second_edge[Y];
p_S = p[X];
p_T = p[Y];
q_S = q[X];
q_T = q[Y];
}
else if (Mth::Abs(rectangle_n[X]) < Mth::Abs(rectangle_n[Y]) && Mth::Abs(rectangle_n[Z]) < Mth::Abs(rectangle_n[Y]))
{
// project onto XZ plane
r_corner_S = rect->m_corner[X];
r_corner_T = rect->m_corner[Z];
r_first_edge_S = rect->m_first_edge[X];
r_first_edge_T = rect->m_first_edge[Z];
r_second_edge_S = rect->m_second_edge[X];
r_second_edge_T = rect->m_second_edge[Z];
p_S = p[X];
p_T = p[Z];
q_S = q[X];
q_T = q[Z];
}
else
{
// project onto YZ plane
r_corner_S = rect->m_corner[Y];
r_corner_T = rect->m_corner[Z];
r_first_edge_S = rect->m_first_edge[Y];
r_first_edge_T = rect->m_first_edge[Z];
r_second_edge_S = rect->m_second_edge[Y];
r_second_edge_T = rect->m_second_edge[Z];
p_S = p[Y];
p_T = p[Z];
q_S = q[Y];
q_T = q[Z];
}
// test p and q against the four edges of the rectangle; if both lie outside any edge, we reject
// these variables define a rectangle's edge in 2D; as + bt + c = 0
float a, b, c;
float pd, qd;
float inside_sign;
// not really convinced that this bitmap method of faster
// char within = 0;
// enum
// {
// P_TRIANGLE_EDGE_0_MASK = 1 << 0,
// Q_TRIANGLE_EDGE_0_MASK = 1 << 1,
// P_TRIANGLE_EDGE_1_MASK = 1 << 2,
// Q_TRIANGLE_EDGE_1_MASK = 1 << 3,
// P_TRIANGLE_EDGE_2_MASK = 1 << 4,
// Q_TRIANGLE_EDGE_2_MASK = 1 << 5,
// P_TRIANGLE_EDGE_3_MASK = 1 << 6,
// Q_TRIANGLE_EDGE_3_MASK = 1 << 7,
// ALL_P_MASKS = P_TRIANGLE_EDGE_0_MASK | P_TRIANGLE_EDGE_1_MASK | P_TRIANGLE_EDGE_2_MASK | P_TRIANGLE_EDGE_3_MASK,
// ALL_Q_MASKS = Q_TRIANGLE_EDGE_0_MASK | Q_TRIANGLE_EDGE_1_MASK | Q_TRIANGLE_EDGE_2_MASK | Q_TRIANGLE_EDGE_3_MASK
// };
bool p_within[4] = { false, false, false, false };
bool q_within[4] = { false, false, false, false };
// edge 0: corner to corner + first_edge
// second_edge defines inside direction
a = r_first_edge_T;
b = -r_first_edge_S;
c = r_corner_T * r_first_edge_S - r_corner_S * r_first_edge_T;
pd = a * p_S + b * p_T + c;
qd = a * q_S + b * q_T + c;
inside_sign = a * r_second_edge_S + b * r_second_edge_T;
if (inside_sign > 0.0f)
{
// within = (P_TRIANGLE_EDGE_0_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_0_MASK * (qd >= 0.0f));
p_within[0] = pd >= 0.0f;
q_within[0] = qd >= 0.0f;
}
else
{
// within = (P_TRIANGLE_EDGE_0_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_0_MASK * (qd <= 0.0f));
p_within[0] = pd <= 0.0f;
q_within[0] = qd <= 0.0f;
}
// if (!(within & (P_TRIANGLE_EDGE_0_MASK | Q_TRIANGLE_EDGE_0_MASK))) return false;
if (!p_within[0] && !q_within[0]) return false;
// edge 1: corner + second_edge to corner + first_edge + second_edge
// second_edge defines outside direction
c = (r_corner_T + r_second_edge_T) * r_first_edge_S - (r_corner_S + r_second_edge_S) * r_first_edge_T;
pd = a * p_S + b * p_T + c;
qd = a * q_S + b * q_T + c;
if (inside_sign < 0.0f)
{
// within |= (P_TRIANGLE_EDGE_1_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_1_MASK * (qd >= 0.0f));
p_within[1] = pd >= 0.0f;
q_within[1] = qd >= 0.0f;
}
else
{
// within |= (P_TRIANGLE_EDGE_1_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_1_MASK * (qd <= 0.0f));
p_within[1] = pd <= 0.0f;
q_within[1] = qd <= 0.0f;
}
// if (!(within & (P_TRIANGLE_EDGE_1_MASK | Q_TRIANGLE_EDGE_1_MASK))) return false;
if (!p_within[1] && !q_within[1]) return false;
// edge 2: corner to corner + second_edge
// first_edge defines inside direction
a = r_second_edge_T;
b = -r_second_edge_S;
c = r_corner_T * r_second_edge_S - r_corner_S * r_second_edge_T;
pd = a * p_S + b * p_T + c;
qd = a * q_S + b * q_T + c;
inside_sign = a * r_first_edge_S + b * r_first_edge_T;
if (inside_sign > 0.0f)
{
// within |= (P_TRIANGLE_EDGE_2_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_2_MASK * (qd >= 0.0f));
p_within[2] = pd >= 0.0f;
q_within[2] = qd >= 0.0f;
}
else
{
// within |= (P_TRIANGLE_EDGE_2_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_2_MASK * (qd <= 0.0f));
p_within[2] = pd <= 0.0f;
q_within[2] = qd <= 0.0f;
}
// if (!(within & (P_TRIANGLE_EDGE_2_MASK | Q_TRIANGLE_EDGE_2_MASK))) return false;
if (!p_within[2] && !q_within[2]) return false;
// edge 3: corner + first_edge to corner + first_edge + second_edge
// first_edge defines outside direction
c = (r_corner_T + r_first_edge_T) * r_second_edge_S - (r_corner_S + r_first_edge_S) * r_second_edge_T;
pd = a * p_S + b * p_T + c;
qd = a * q_S + b * q_T + c;
if (inside_sign < 0.0f)
{
// within |= (P_TRIANGLE_EDGE_3_MASK * (pd >= 0.0f)) | (Q_TRIANGLE_EDGE_3_MASK * (qd >= 0.0f));
p_within[3] = pd >= 0.0f;
q_within[3] = qd >= 0.0f;
}
else
{
// within |= (P_TRIANGLE_EDGE_3_MASK * (pd <= 0.0f)) | (Q_TRIANGLE_EDGE_3_MASK * (qd <= 0.0f));
p_within[3] = pd <= 0.0f;
q_within[3] = qd <= 0.0f;
}
// if (!(within & (P_TRIANGLE_EDGE_3_MASK | Q_TRIANGLE_EDGE_3_MASK))) return false;
if (!p_within[3] && !q_within[3]) return false;
int next_collision_endpoint_idx;
// if p or q is within all edges, it is the endpoint of the collision
// if ((within & ALL_P_MASKS) == ALL_P_MASKS)
if (p_within[0] && p_within[1] && p_within[2] && p_within[3])
{
p_collision_endpoints[0] = p;
p_collision_triangle_edges[0] = p_edge;
// if ((within & ALL_Q_MASKS) == ALL_Q_MASKS)
if (q_within[0] && q_within[1] && q_within[2] && q_within[3])
{
p_collision_endpoints[1] = q;
p_collision_triangle_edges[1] = q_edge;
return true;
}
next_collision_endpoint_idx = 1;
}
// else if ((within & ALL_Q_MASKS) == ALL_Q_MASKS)
else if (q_within[0] && q_within[1] && q_within[2] && q_within[3])
{
p_collision_endpoints[0] = q;
p_collision_triangle_edges[0] = q_edge;
next_collision_endpoint_idx = 1;
}
else
{
next_collision_endpoint_idx = 0;
}
// we've found any endpoints of the collision which are within the rectangle; further collision endpoints will lie on the edges of the rectangle;
// we check each edge of the rectangle for intersection with the triangle's plane-intersection line segment
float s;
// edge 0: corner to corner + first_edge
if (s2DLineLineCollision(r_corner_S, r_corner_T, r_first_edge_S, r_first_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
{
p_collision_endpoints[next_collision_endpoint_idx] = rect->m_first_edge;
p_collision_endpoints[next_collision_endpoint_idx] *= s;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
if (next_collision_endpoint_idx == 1) return true;
next_collision_endpoint_idx = 1;
}
// edge 1: corner + second_edge to corner + first_edge + second_edge
if (s2DLineLineCollision(r_corner_S + r_second_edge_S, r_corner_T + r_second_edge_T, r_first_edge_S, r_first_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
{
p_collision_endpoints[next_collision_endpoint_idx] = rect->m_first_edge;
p_collision_endpoints[next_collision_endpoint_idx] *= s;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_second_edge;
p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
if (next_collision_endpoint_idx == 1) return true;
next_collision_endpoint_idx = 1;
}
// edge 2: corner to corner + second_edge
if (s2DLineLineCollision(r_corner_S, r_corner_T, r_second_edge_S, r_second_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
{
p_collision_endpoints[next_collision_endpoint_idx] = rect->m_second_edge;
p_collision_endpoints[next_collision_endpoint_idx] *= s;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
if (next_collision_endpoint_idx == 1) return true;
next_collision_endpoint_idx = 1;
}
// edge 3: corner + first_edge to corner + first_edge + second_edge
if (s2DLineLineCollision(r_corner_S + r_first_edge_S, r_corner_T + r_first_edge_T, r_second_edge_S, r_second_edge_T, p_S, p_T, q_S - p_S, q_T - p_T, &s))
{
if (next_collision_endpoint_idx == 1)
{
p_collision_endpoints[next_collision_endpoint_idx] = rect->m_second_edge;
p_collision_endpoints[next_collision_endpoint_idx] *= s;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_corner;
p_collision_endpoints[next_collision_endpoint_idx] += rect->m_first_edge;
p_collision_triangle_edges[next_collision_endpoint_idx] = NO_TRIANGLE_EDGE;
return true;
}
}
// no collision could be found
return false;
}
////////////////////////////////////////////////////////////////////
// CCollObj member functions
//
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj::CCollObj() : m_Flags(0), mp_coll_tri_data(NULL)
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj::~CCollObj()
{
// this should only be done on clones
if (mp_coll_tri_data && (m_Flags & mSD_CLONE))
{
delete mp_coll_tri_data;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollObj::SetChecksum(uint32 checksum)
{
m_checksum = checksum;
if (mp_coll_tri_data)
{
mp_coll_tri_data->SetChecksum(checksum);
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollObj::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
Dbg_MsgAssert(0, ("Can't call SetMovingObject() on a non-moving collision type"));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Obj::CCompositeObject * CCollObj::GetMovingObject() const
{
return NULL;
}
/*****************************************************************************
** Public Functions **
*****************************************************************************/
#define PRINT_RECT_COLLISION_DEBUG
const float CCollObj::sLINE_BOX_EXTENT = 0.5f; // Extent of box around collision line
bool CCollObj::sFindRectangleStaticCollision ( Mth::Rectangle& rect, S2DCollData* p_2D_coll_data, CCollCache *p_cache )
{
#if PRINT_CACHE_HITS
static uint s_rect_cache_hits = 0, s_num_rect_collisions = 0;
#endif
Dbg_Assert(p_2D_coll_data);
Mth::CBBox rect_bbox(rect.m_corner);
rect_bbox.AddPoint(rect.m_corner + rect.m_first_edge);
rect_bbox.AddPoint(rect.m_corner + rect.m_second_edge);
rect_bbox.AddPoint(rect.m_corner + rect.m_first_edge + rect.m_second_edge);
bool use_cache = p_cache;
if (use_cache)
{
use_cache = p_cache->Contains(rect_bbox);
#if PRINT_CACHE_HITS
if (use_cache)
{
s_rect_cache_hits++;
}
if (++s_num_rect_collisions >= 500)
{
Dbg_Message("Static Rect Cache Hits %d/500", s_rect_cache_hits);
s_rect_cache_hits = s_num_rect_collisions = 0;
}
#endif
}
CCollStatic** p_coll_obj_list = NULL;
if (!use_cache)
{
SSec::Manager* ss_man = Nx::CEngine::sGetNearestSuperSectorManager(rect.m_corner);
if (!ss_man) return false;
// get a list of sectors within the super sectors cut by the rectangle
p_coll_obj_list = ss_man->GetIntersectingCollSectors(rect_bbox);
}
// bloat the rectangle bounding box for comparison to sector bounding boxes
Mth::Vector extension(sLINE_BOX_EXTENT, sLINE_BOX_EXTENT, sLINE_BOX_EXTENT);
rect_bbox.Set(rect_bbox.GetMin() - extension, rect_bbox.GetMax() + extension);
p_2D_coll_data->num_surfaces = 0;
if (use_cache)
{
int num_collisions = p_cache->GetNumStaticCollisions();
const SCollCacheNode* p_coll_node = p_cache->GetStaticCollisionArray();
// loop over sectors in the cache
for (int i = num_collisions; i--; )
{
// check to see if the sector's bounding box intersects with the rectangle's bounding box
if (rect_bbox.Intersect(*(p_coll_node->mp_bbox)))
{
// check the sector's trangle's for collision with the rectangle
p_coll_node->mp_coll_obj->CollisionWithRectangle(rect, rect_bbox, p_2D_coll_data);
}
p_coll_node++;
} // END loop over sectors in the cache
}
else
{
// loop over potentially collided with sectors
CCollStatic* p_coll_obj;
while ((p_coll_obj = *p_coll_obj_list))
{
Dbg_Assert(p_coll_obj->GetGeometry());
// check to see if the sector's bounding box intersects with the rectangle's bounding box
if (p_coll_obj->WithinBBox(rect_bbox))
{
// check the sector's trangle's for collision with the rectangle
p_coll_obj->CollisionWithRectangle(rect, rect_bbox, p_2D_coll_data);
}
p_coll_obj_list++;
} // END loop over potentially collided with sectors
}
return p_2D_coll_data->num_surfaces > 0;
}
bool GPrintCollisionData = false;
bool CCollObj::sFindNearestStaticCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
#if PRINT_CACHE_HITS
static uint s_cache_hits = 0, s_num_collisions = 0;
#endif
Dbg_Assert(p_data);
Dbg_Assert(p_data->coll_found == false);
p_data->callback = (void (*)( CollData*)) p_callback;
// Make initial line bounding box
Mth::CBBox line_bbox(is.m_start);
line_bbox.AddPoint(is.m_end);
if (GPrintCollisionData)
{
Dbg_Message("Inited line BBox (%f, %f, %f), (%f, %f, %f)",
line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
}
// Check to see if we can use the cache
bool use_cache = p_cache != NULL;
if (use_cache)
{
use_cache = p_cache->Contains(line_bbox);
if (CCollCacheManager::sGetAssertOnCacheMiss())
{
Dbg_MsgAssert(use_cache, ("Collision cache miss"));
}
#if PRINT_CACHE_HITS
if (use_cache) s_cache_hits++;
if (++s_num_collisions >= 5000)
{
Dbg_Message("Static Cache Hits %d/5000", s_cache_hits);
s_cache_hits = s_num_collisions = 0;
}
#endif
}
// initialize starting "max" distance to be max collision distance
CCollStatic** p_coll_obj_list = NULL;
if (!use_cache)
{
SSec::Manager *ss_man;
ss_man = Nx::CEngine::sGetNearestSuperSectorManager(is);
if (!ss_man)
return NULL;
p_coll_obj_list = ss_man->GetIntersectingCollSectors( is );
}
// For a line, the dist is returned as a fraction of the line length
// so we set the dist field to just over 1.0, any found collision
// will be less than this
if (s_check_for_far)
{
p_data->dist = -0.01f; // but if checking in the other direction, we need to to reverse the check
}
else
{
p_data->dist = 1.01f;
}
// Bloat the collision line only for the purposes of deciding against which world
// sectors to test the actual collision line
Mth::Vector extend;
extend = line_bbox.GetMax();
extend[X] += sLINE_BOX_EXTENT;
extend[Y] += sLINE_BOX_EXTENT;
extend[Z] += sLINE_BOX_EXTENT;
line_bbox.AddPoint(extend);
extend = line_bbox.GetMin();
extend[X] -= sLINE_BOX_EXTENT;
extend[Y] -= sLINE_BOX_EXTENT;
extend[Z] -= sLINE_BOX_EXTENT;
line_bbox.AddPoint(extend);
// Calculate ray
Mth::Vector line_dir(is.m_end - is.m_start);
if (GPrintCollisionData)
{
Dbg_Message("Starting collision check with line BBox (%f, %f, %f), (%f, %f, %f)",
line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
Dbg_Message("Line start (%f, %f, %f)", is.m_start[X], is.m_start[Y], is.m_start[Z]);
Dbg_Message("Line end (%f, %f, %f)", is.m_end[X], is.m_end[Y], is.m_end[Z]);
}
bool new_coll_found = false;
if (use_cache)
{
int num_collisions = p_cache->GetNumStaticCollisions();
const SCollCacheNode *p_coll_node = p_cache->GetStaticCollisionArray();
for (int i = 0; i < num_collisions; i++)
{
if(line_bbox.Intersect(*(p_coll_node->mp_bbox)))
{
/* It's a world sector, call the callback */
if (p_coll_node->mp_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
{
new_coll_found = true;
}
}
p_coll_node++;
}
} else {
CCollStatic* p_coll_obj;
#ifdef BATCH_TRI_COLLISION
// Init batch manager
bool do_batch;
do_batch = CBatchTriCollMan::sInit(p_data);
#endif
/* Start at the top */
while((p_coll_obj = *p_coll_obj_list))
{
// TODO: Come up with a cleaner BBox check
Dbg_Assert(p_coll_obj->GetGeometry());
//if(line_bbox.Intersect(p_coll_obj->GetGeometry()->GetBBox()))
if (p_coll_obj->WithinBBox(line_bbox))
{
// this line will display the bounding box of the potential collidable object
// useful to make sure we are only colliding with the minimum necessary objects
// p_coll_obj->mp_coll_tri_data->GetBBox().DebugRender(0xfffff,1);
/* It's a world sector, call the callback */
if (p_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
{
new_coll_found = true;
}
}
p_coll_obj_list++;
}
#ifdef BATCH_TRI_COLLISION
// Wait for tri collision to finish first
if (do_batch)
{
new_coll_found = CBatchTriCollMan::sWaitTriCollisions();
}
CBatchTriCollMan::sFinish(); // This should really be in the CFeeler code
#endif
}
/* All done */
if (p_data->coll_found)
{
if ((p_data->dist > 1.0f) || (p_data->dist < 0.0f))
{
Dbg_Message("Static Collision distance too big: %f", p_data->dist);
}
}
// return true if we found something
return( new_coll_found );
}
bool CCollObj::sFindFarStaticCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
s_check_for_far = true;
bool result = sFindNearestStaticCollision(is,p_data,p_callback,p_cache);
s_check_for_far = false;
return result;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
// Note, p_data is assumed to be already initialized
// either it is set to default values, or we have
// come here after using it to check for static collision
// and the results of that are in there
// - see below, we don't set "dist" if we've previously found a collision
// (stops you snapping through walls onto cars)
Obj::CCompositeObject * CCollObj::sFindNearestMovableCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
#if PRINT_CACHE_HITS
static uint s_cache_hits = 0, s_num_collisions = 0;
#endif
Dbg_Assert(p_data);
Obj::CCompositeObject *p_collision_obj = NULL;
p_data->callback = (void (*)( CollData*)) p_callback;
// initialize starting "max" distance to be max collision distance
// actually this
if (!p_data->coll_found)
{
// For a line, the dist is returned as a fraction of the line length
// so we set the dist field to just over 1.0, any found collision
// will be less than this
if (s_check_for_far)
{
p_data->dist = -0.01f; // but if checking in the other direction, we need to to reverse the check
}
else
{
p_data->dist = 1.01f;
}
}
// Make initial line bounding box
Mth::CBBox line_bbox(is.m_start);
line_bbox.AddPoint(is.m_end);
// Check to see if we can use the cache
bool use_cache = p_cache != NULL;
if (use_cache)
{
use_cache = p_cache->Contains(line_bbox);
#if PRINT_CACHE_HITS
if (use_cache) s_cache_hits++;
if (++s_num_collisions >= 5000)
{
Dbg_Message("Movable Cache Hits %d/5000", s_cache_hits);
s_cache_hits = s_num_collisions = 0;
}
#endif
}
if (0 && GPrintCollisionData)
{
Dbg_Message("Inited line BBox (%f, %f, %f), (%f, %f, %f)",
line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
}
// Bloat the collision line only for the purposes of deciding against which world
// sectors to test the actual collision line
Mth::Vector extend;
extend = line_bbox.GetMax();
extend[X] += sLINE_BOX_EXTENT;
extend[Y] += sLINE_BOX_EXTENT;
extend[Z] += sLINE_BOX_EXTENT;
line_bbox.AddPoint(extend);
extend = line_bbox.GetMin();
extend[X] -= sLINE_BOX_EXTENT;
extend[Y] -= sLINE_BOX_EXTENT;
extend[Z] -= sLINE_BOX_EXTENT;
line_bbox.AddPoint(extend);
// Calculate ray
Mth::Vector line_dir(is.m_end - is.m_start);
if (0 && GPrintCollisionData)
{
Dbg_Message("Starting collision check with line BBox (%f, %f, %f), (%f, %f, %f)",
line_bbox.GetMin()[X], line_bbox.GetMin()[Y], line_bbox.GetMin()[Z],
line_bbox.GetMax()[X], line_bbox.GetMax()[Y], line_bbox.GetMax()[Z]);
Dbg_Message("Line start (%f, %f, %f)", is.m_start[X], is.m_start[Y], is.m_start[Z]);
Dbg_Message("Line end (%f, %f, %f)", is.m_end[X], is.m_end[Y], is.m_end[Z]);
}
// Dbg_Message("Size of movable list %d", Nx::CEngine::sGetMovableObjects().CountItems());
// Dbg_Message("Size of movable collision list %d", CMovableCollMan::sGetCollisionList()->CountItems());
// Dbg_Assert(0);
if (use_cache)
{
int num_collisions = p_cache->GetNumMovableCollisions();
const SCollCacheNode *p_coll_node = p_cache->GetMovableCollisionArray();
for (int i = 0; i < num_collisions; i++)
{
if(p_coll_node->mp_bbox && line_bbox.Intersect(*(p_coll_node->mp_bbox)))
{
// Set the callback_object, so any callback will know
// what object we were colliding with
p_data->mp_callback_object = p_coll_node->mp_coll_obj->GetMovingObject();
/* It's a world sector, call the callback */
if (p_coll_node->mp_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
{
p_collision_obj = p_coll_node->mp_coll_obj->GetMovingObject();
Dbg_Assert(p_collision_obj);
}
// the callback object is only valid for the duration of a possible callback
p_data->mp_callback_object = NULL;
}
p_coll_node++;
}
} else {
#if 0 //def BATCH_TRI_COLLISION
// Init batch manager
bool do_batch;
do_batch = CBatchTriCollMan::sInit(p_data);
#endif
Lst::Node< CCollObj > *p_movable_node = CMovableCollMan::sGetCollisionList()->GetNext();
//CCollObj **p_movable_node = CMovableCollMan::sGetCollisionArray();
while(p_movable_node)
{
CCollObj *p_coll_obj = p_movable_node->GetData();
//CCollObj *p_coll_obj = *p_movable_node;
if (p_coll_obj && !(p_coll_obj->m_Flags & (mSD_NON_COLLIDABLE | mSD_KILLED )))
{
/* It's a collision object, call the callback */
//if (p_coll_obj->CollisionWithLine( is, line_dir, p_data , &line_bbox))
if (p_coll_obj->WithinBBox(line_bbox))
{
// Set the callback_object, so any callback will know
// what object we were colliding with
p_data->mp_callback_object = p_coll_obj->GetMovingObject();
if (p_coll_obj->CollisionWithLine( is, line_dir, p_data, &line_bbox ))
{
p_collision_obj = p_coll_obj->GetMovingObject();
Dbg_Assert(p_collision_obj);
}
// the callback object is only valid for the duration of a possible callback
p_data->mp_callback_object = NULL;
}
}
p_movable_node = p_movable_node->GetNext();
//p_movable_node++;
}
#if 0 //def BATCH_TRI_COLLISION
// Wait for tri collision to finish first
if (do_batch)
{
new_coll_found = CBatchTriCollMan::sWaitTriCollisions();
}
CBatchTriCollMan::sFinish(); // This should really be in the CFeeler code
#endif
}
/* All done */
if (p_collision_obj)
{
if ((p_data->dist > 1.0f) || (p_data->dist < 0.0f))
{
Dbg_Message("Movable Collision distance too big: %f", p_data->dist);
}
}
return p_collision_obj;
}
Obj::CCompositeObject * CCollObj::sFindFarMovableCollision( Mth::Line &is, CollData *p_data, void *p_callback, CCollCache *p_cache)
{
s_check_for_far = true;
Obj::CCompositeObject *result = sFindNearestMovableCollision(is,p_data,p_callback,p_cache);
s_check_for_far = false;
return result;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CollType CCollObj::sConvertTypeChecksum(uint32 checksum)
{
CollType c_type;
switch (checksum)
{
case 0x6aadf154: // Geometry
c_type = vCOLL_TYPE_GEOM;
break;
case 0xe460abde: // BoundingBox
case 0x2ba71070: // Spherical
case 0xdd42aa66: // Cylindrical
c_type = vCOLL_TYPE_BBOX;
break;
case 0x806fff30: // None
default:
c_type = vCOLL_TYPE_NONE;
break;
}
return c_type;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj * CCollObj::sCreateMovableCollision(CollType type, Nx::CCollObjTriData *p_coll_obj_data, int num_coll_objects, Obj::CCompositeObject *p_object)
{
CCollObj *p_collision;
switch (type)
{
case vCOLL_TYPE_BBOX:
{
CCollMovBBox *p_coll_bbox = new Nx::CCollMovBBox;
// TODO: determine if we should make more than one collision object
if (num_coll_objects >= 1) {
Mth::CBBox bbox/*(Mth::Vector(0.0f, 0.0f, 0.0f))*/; // Add origin for now
for (int i = 0; i < num_coll_objects; i++)
{
bbox.AddPoint(p_coll_obj_data[i].GetBBox().GetMin());
bbox.AddPoint(p_coll_obj_data[i].GetBBox().GetMax());
}
p_coll_bbox->SetBoundingBox(bbox);
}
p_collision = p_coll_bbox;
break;
}
case vCOLL_TYPE_GEOM:
if (num_coll_objects == 1) // one node
{
p_collision = new CCollMovTri(p_coll_obj_data);
} else if (num_coll_objects > 1) { // multi nodes
CCollMulti *p_coll_multi = new Nx::CCollMulti;
for (int i = 0; i < num_coll_objects; i++)
{
CCollObj *p_coll_tri;
p_coll_tri = new CCollMovTri(&(p_coll_obj_data[i]));
p_coll_multi->AddCollision(p_coll_tri);
}
p_collision = p_coll_multi;
} else {
p_collision = NULL;
}
break;
default:
p_collision = NULL; // default to no collision
break;
}
// Add to movable collision manager
if (p_collision)
{
p_collision->SetMovingObject(p_object);
CMovableCollMan::sAddCollision(p_collision, p_object);
}
return p_collision;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMulti::CCollMulti()
{
mp_collision_list = new Lst::Head< CCollObj >;
m_movement_changed = true;
mp_movable_object = NULL;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMulti::~CCollMulti()
{
if (mp_collision_list)
{
Lst::Node< CCollObj > *obj_node, *next;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = next)
{
next = obj_node->GetNext();
delete obj_node->GetData();
delete obj_node;
}
delete mp_collision_list;
}
// Take out of caches
CCollCacheManager::sDeleteCollision(this);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::AddCollision(CCollObj *p_collision)
{
Lst::Node< CCollObj > *node = new Lst::Node< CCollObj > (p_collision);
mp_collision_list->AddToTail(node);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::SetWorldPosition(const Mth::Vector & pos)
{
Lst::Node< CCollObj > *obj_node;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
obj_node->GetData()->SetWorldPosition(pos);
}
m_movement_changed = true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::SetOrientation(const Mth::Matrix & orient)
{
Lst::Node< CCollObj > *obj_node;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
obj_node->GetData()->SetOrientation(orient);
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::SetGeometry(CCollObjTriData *p_geom_data)
{
Lst::Node< CCollObj > *obj_node;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
obj_node->GetData()->SetGeometry(p_geom_data);
}
m_movement_changed = true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
mp_movable_object = p_movable_object;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Obj::CCompositeObject * CCollMulti::GetMovingObject() const
{
return mp_movable_object;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj * CCollMulti::Clone(bool instance)
{
Dbg_MsgAssert(!instance, ("CCollMulti::Clone() with instances not implemented yet"));
Dbg_Assert(mp_coll_tri_data == NULL);
CCollMulti *m_new_coll = new CCollMulti(*this);
m_new_coll->m_Flags |= mSD_CLONE;
Lst::Node< CCollObj > *obj_node;
// Clone List
m_new_coll->mp_collision_list = new Lst::Head< CCollObj >;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
Lst::Node< CCollObj > *node = new Lst::Node< CCollObj > (obj_node->GetData()->Clone(instance));
m_new_coll->mp_collision_list->AddToTail(node);
}
return m_new_coll;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMulti::IsTriangleCollision() const
{
Lst::Node< CCollObj > *obj_node;
bool ret_val = true;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
ret_val = ret_val && obj_node->GetData()->IsTriangleCollision();
}
return ret_val;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::update_world_bbox()
{
Lst::Node< CCollObj > *obj_node;
m_world_bbox_valid = true;
m_world_bbox.Reset();
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
Mth::CBBox *obj_bbox;
if ((obj_bbox = obj_node->GetData()->get_bbox()))
{
m_world_bbox.AddPoint(obj_bbox->GetMin());
m_world_bbox.AddPoint(obj_bbox->GetMax());
} else {
m_world_bbox_valid = false;
break;
}
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Mth::CBBox *CCollMulti::get_bbox()
{
if (m_movement_changed)
{
update_world_bbox();
m_movement_changed = false;
}
if (m_world_bbox_valid)
{
return &m_world_bbox;
} else {
return NULL;
}
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMulti::WithinBBox(const Mth::CBBox & testBBox)
{
if (m_movement_changed)
{
update_world_bbox();
m_movement_changed = false;
}
return !m_world_bbox_valid || testBBox.Intersect(m_world_bbox);
//return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMulti::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox * p_bbox)
{
Lst::Node< CCollObj > *obj_node;
bool ret_val = false;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
ret_val = obj_node->GetData()->CollisionWithLine(testLine, lineDir, p_data, p_bbox) || ret_val;
}
return ret_val;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMulti::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)
{
Dbg_MsgAssert(false, ("CCollMulti::CollisionWithRecangle is not implemented"));
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMulti::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
Lst::Node< CCollObj > *obj_node;
for(obj_node = mp_collision_list->GetNext(); obj_node; obj_node = obj_node->GetNext())
{
obj_node->GetData()->DebugRender(ignore_1, ignore_0);
}
}
// Don't call these!
const Mth::Vector & CCollMulti::GetWorldPosition() const
{
Dbg_MsgAssert(0, ("Don't call CCollMulti::GetWorldPosition()"));
return mp_collision_list->GetNext()->GetData()->GetWorldPosition();
}
const Mth::Matrix & CCollMulti::GetOrientation() const
{
Dbg_MsgAssert(0, ("Don't call CCollMulti::GetOrientation()"));
return mp_collision_list->GetNext()->GetData()->GetOrientation();
}
uint32 CCollMulti::GetFaceFlags(int face_idx) const
{
Dbg_MsgAssert(0, ("Don't call CCollMulti::GetFaceFlags()"));
return 0;
}
uint16 CCollMulti::GetFaceTerrainType(int face_idx) const
{
Dbg_MsgAssert(0, ("Don't call CCollMulti::GetFaceTerrainType()"));
return 0;
}
Mth::Vector CCollMulti::GetVertexPos(int vert_idx) const
{
Dbg_MsgAssert(0, ("Don't call CCollMulti::GetVertexPos()"));
return Mth::Vector(0, 0, 0, 0);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovable::CCollMovable() :
m_world_pos(0, 0, 0, 1),
m_orient(0, 0, 0),
m_orient_transpose(0, 0, 0),
mp_movable_object(NULL)
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovable::~CCollMovable()
{
// Take out of caches
CCollCacheManager::sDeleteMovableCollision(this);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Mth::Vector CCollMovable::GetVertexPos(int vert_idx) const
{
Dbg_Assert(mp_coll_tri_data);
Mth::Vector pos(mp_coll_tri_data->GetRawVertexPos(vert_idx));
pos[W] = 0.0f; // start as vector
pos.Rotate(m_orient);
pos += m_world_pos;
return pos;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovable::SetMovingObject(Obj::CCompositeObject *p_movable_object)
{
mp_movable_object = p_movable_object;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Obj::CCompositeObject * CCollMovable::GetMovingObject() const
{
return mp_movable_object;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovable::convert_line_to_local(const Mth::Line &world_line, Mth::Line &local_line)
{
local_line = world_line;
// First, translate back
local_line -= m_world_pos;
// And then do a reverse rotation
local_line.m_start.Rotate(m_orient_transpose);
local_line.m_end.Rotate(m_orient_transpose);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollStatic::CCollStatic() //:
// m_world_pos(0, 0, 0, 1)//,
// m_orient(0, 0, 0)
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollStatic::~CCollStatic()
{
// Take out of caches
//CCollCacheManager::sDeleteStaticCollision(this);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
Mth::Vector CCollStatic::GetVertexPos(int vert_idx) const
{
Dbg_Assert(mp_coll_tri_data);
Mth::Vector pos(mp_coll_tri_data->GetRawVertexPos(vert_idx));
pos[W] = 1.0f; // make a point
return pos;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovBBox::CCollMovBBox() :
m_flags(0),
m_terrain_type(0)
{
// Sets a default bounding box, if nothing loaded
//m_bbox.Set(Mth::Vector(-60, -20, -60, 1), Mth::Vector(60, 36, 60, 1));
m_bbox.Set(Mth::Vector(-50, -20, -90, 1), Mth::Vector(30, 36, 90, 1)); // size of car
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovBBox::CCollMovBBox(CCollObjTriData *p_geom_data) :
m_flags(0),
m_terrain_type(0)
{
SetGeometry(p_geom_data);
Dbg_Assert(mp_coll_tri_data);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovBBox::~CCollMovBBox()
{
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovBBox::SetGeometry(CCollObjTriData *p_geom_data)
{
CCollObj::SetGeometry(p_geom_data);
Dbg_Assert(mp_coll_tri_data);
SetBoundingBox(mp_coll_tri_data->GetBBox());
}
// Temp functions
uint32 CCollMovBBox::GetFaceFlags(int face_idx) const
{
return m_flags;
}
uint16 CCollMovBBox::GetFaceTerrainType(int face_idx) const
{
return m_terrain_type;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj * CCollMovBBox::Clone(bool instance)
{
Dbg_MsgAssert(!instance, ("CCollMovBBox::Clone() with instances not implemented yet"));
CCollMovBBox *m_new_coll = new CCollMovBBox(*this);
m_new_coll->m_Flags |= mSD_CLONE;
m_new_coll->mp_coll_tri_data = NULL; // We don't need to clone the data since we have the bbox
return m_new_coll;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMovBBox::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
Dbg_Assert(p_data);
// Find line in local space
Mth::Line local_line;
convert_line_to_local(testLine, local_line);
if (0 && GPrintCollisionData)
{
Dbg_Message("Testing ray with start (%f, %f, %f)", testLine.m_start[X], testLine.m_start[Y], testLine.m_start[Z]);
Dbg_Message("and end (%f, %f, %f)", testLine.m_end[X], testLine.m_end[Y], testLine.m_end[Z]);
Dbg_Message("with the direction (%f, %f, %f)", lineDir[X], lineDir[Y], lineDir[Z]);
}
// Mick: temp optimization, generate a bounding box for the line
// so it can be tested against the collision bounding box
Mth::CBBox line_bbox(local_line.m_start);
line_bbox.AddPoint(local_line.m_end);
// Do collision
if (m_bbox.Intersect(line_bbox))
{
Mth::Vector local_point, local_normal;
if (m_bbox.LineIntersect(local_line, local_point, local_normal))
{
float distance = Mth::Vector(local_point - local_line.m_start).Length() / // gotta be a faster way
Mth::Vector(local_line.m_end - local_line.m_start).Length();
SCollSurface collisionSurface;
if (GPrintCollisionData)
{
Dbg_Message("\n*Start point (%f, %f, %f)", testLine.m_start[X], testLine.m_start[Y], testLine.m_start[Z]);
Dbg_Message("*End point (%f, %f, %f)", testLine.m_end[X], testLine.m_end[Y], testLine.m_end[Z]);
Dbg_Message("*Start local point (%f, %f, %f)", local_line.m_start[X], local_line.m_start[Y], local_line.m_start[Z]);
Dbg_Message("*End local point (%f, %f, %f)", local_line.m_end[X], local_line.m_end[Y], local_line.m_end[Z]);
Dbg_Message("*World pos (%f, %f, %f)", m_world_pos[X], m_world_pos[Y], m_world_pos[Z]);
Dbg_Message("*Local collision pos (%f, %f, %f)", local_point[X], local_point[Y], local_point[Z]);
Dbg_Message("*Local Normal (%f, %f, %f)", local_normal[X], local_normal[Y], local_normal[Z]);
Dbg_Message("*Orientation [%f, %f, %f]", m_orient[X][X], m_orient[X][Y], m_orient[X][Z]);
Dbg_Message("* [%f, %f, %f]", m_orient[Y][X], m_orient[Y][Y], m_orient[Y][Z]);
Dbg_Message("* [%f, %f, %f]", m_orient[Z][X], m_orient[Z][Y], m_orient[Z][Z]);
}
// Convert to world coordinates
collisionSurface.point = local_point.Rotate(m_orient);
collisionSurface.point += m_world_pos;
collisionSurface.normal = local_normal.Rotate(m_orient);
if (GPrintCollisionData)
{
Dbg_Message("*Collision pos (%f, %f, %f)", collisionSurface.point[X], collisionSurface.point[Y], collisionSurface.point[Z]);
Dbg_Message("*Normal (%f, %f, %f)", collisionSurface.normal[X], collisionSurface.normal[Y], collisionSurface.normal[Z]);
Dbg_Message("*distance %f", distance);
}
return s_found_collision(&testLine, this, &collisionSurface, distance, p_data);
}
}
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMovBBox::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)
{
Dbg_MsgAssert(false, ("CCollMovBBox::CollisionWithRecangle is not implemented"));
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovBBox::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
// Debug box
Mth::Matrix total_mat = m_orient;
total_mat[W] = m_world_pos;
m_bbox.DebugRender(total_mat, 0x80808080, 1);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollStaticTri::CCollStaticTri()
{
// This instance can't be used until someone sets the geometry data
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollStaticTri::~CCollStaticTri()
{
// Don't delete the Tri Data
}
// Temp functions
uint32 CCollStaticTri::GetFaceFlags(int face_idx) const
{
Dbg_Assert(mp_coll_tri_data);
return mp_coll_tri_data->GetFaceFlags(face_idx);
}
uint16 CCollStaticTri::GetFaceTerrainType(int face_idx) const
{
Dbg_Assert(mp_coll_tri_data);
return mp_coll_tri_data->GetFaceTerrainType(face_idx);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::SetWorldPosition(const Mth::Vector & pos)
{
Dbg_Assert(mp_coll_tri_data);
mp_coll_tri_data->Translate(pos - GetWorldPosition());
CCollStatic::SetWorldPosition(pos);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::SetOrientation(const Mth::Matrix & orient)
{
CCollStatic::SetOrientation(orient);
Dbg_MsgAssert(0, ("CCollStaticTri::SetOrientation() not implemented yet"));
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj * CCollStaticTri::Clone(bool instance)
{
Dbg_MsgAssert(!instance, ("CCollStaticTri::Clone() with instances not implemented yet"));
// Clone only if there is geometry
CCollObjTriData *p_new_tri_data = mp_coll_tri_data->Clone(instance, true);
if (!p_new_tri_data)
{
return NULL;
}
CCollStaticTri *p_new_coll = new CCollStaticTri(*this);
p_new_coll->m_Flags |= mSD_CLONE;
p_new_coll->mp_coll_tri_data = p_new_tri_data;
return p_new_coll;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::RotateY(const Mth::Vector & world_origin, Mth::ERot90 rot_y)
{
Dbg_Assert(mp_coll_tri_data);
mp_coll_tri_data->RotateY(world_origin, rot_y);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::Scale(const Mth::Vector & world_origin, const Mth::Vector & scale)
{
Dbg_Assert(mp_coll_tri_data);
mp_coll_tri_data->Scale(world_origin, scale);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollStaticTri::WithinBBox(const Mth::CBBox & testBBox)
{
Dbg_Assert(mp_coll_tri_data);
return testBBox.Intersect(mp_coll_tri_data->GetBBox());
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollStaticTri::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
Dbg_Assert(p_data);
Dbg_Assert(mp_coll_tri_data);
bool best_collision = false;
// Generate a bounding box for the line
// so it can be tested against the BSP tree.
// (Should we just pass along the one we had before, even though it won't work with movables?)
// Mth::CBBox line_bbox(testLine.m_start);
// line_bbox.AddPoint(testLine.m_end);
uint num_faces;
FaceIndex *p_face_indexes;
p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(*p_bbox, num_faces);
Dbg_Assert(p_face_indexes);
#ifdef BATCH_TRI_COLLISION
bool do_batch = !CBatchTriCollMan::sIsNested();
#endif
for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
{
#ifdef BATCH_TRI_COLLISION
if (do_batch)
{
CBatchTriCollMan::sAddTriCollision(testLine, lineDir, this, *p_face_indexes);
continue;
}
#endif // BATCH_TRI_COLLISION
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
Mth::Vector v0, v1, v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
}
#else
Mth::Vector *v0, *v1, *v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
}
#endif // FIXED_POINT_VERTICES
#if 0
uint32 coll_color = (uint32) MAKE_RGB(200, 200, 200);
if (coll_color != (uint32) MAKE_RGB( 0, 200, 0 ))
{
Gfx::AddDebugLine(*v0,*v1,coll_color,1);
Gfx::AddDebugLine(*v1,*v2,coll_color,1);
Gfx::AddDebugLine(*v2,*v0,coll_color,1);
}
#endif
float distance;
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
if (sRayTriangleCollision(&testLine.m_start, &lineDir, &v0, &v1, &v2, &distance))
#else
if (sRayTriangleCollision(&testLine.m_start, &lineDir, v0, v1, v2, &distance))
#endif
{
SCollSurface collisionSurface;
/* We've got one */
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
collisionSurface.point = v0;
collisionSurface.index = *p_face_indexes;
// Find normal
Mth::Vector vTmp1(v1 - v0);
Mth::Vector vTmp2(v2 - v0);
#else
collisionSurface.point = (*v0);
collisionSurface.index = *p_face_indexes;
// Find normal
Mth::Vector vTmp1(*v1 - *v0);
Mth::Vector vTmp2(*v2 - *v0);
#endif // FIXED_POINT_VERTICES
collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
collisionSurface.normal.Normalize();
if (s_found_collision(&testLine, this, &collisionSurface, distance, p_data))
{
best_collision = true;
}
}
}
#ifdef BATCH_TRI_COLLISION
if (do_batch)
{
CBatchTriCollMan::sStartNewTriCollisions();
}
#endif // BATCH_TRI_COLLISION
return best_collision;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollStaticTri::CollisionWithRectangle(const Mth::Rectangle& testRect, const Mth::CBBox& testRectBBox, S2DCollData *p_coll_data)
{
Dbg_Assert(p_coll_data);
uint num_faces;
FaceIndex* p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(testRectBBox, num_faces);
Dbg_Assert(p_face_indexes);
// loop over the relavent faces of the sector
for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
{
uint32 face_flags = GetFaceFlags(fidx);
if ((face_flags & p_coll_data->ignore_1) || (~face_flags & p_coll_data->ignore_0)) continue;
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
Mth::Vector v0, v1, v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
}
#else
Mth::Vector v0, v1, v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
}
#endif // FIXED_POINT_VERTICES
Mth::Vector p_coll_point[2];
ETriangleEdgeType p_triangle_edge[2];
if (sRectangleTriangleCollision(&testRect, &v0, &v1, &v2, p_coll_point, p_triangle_edge))
{
if (p_coll_data->num_surfaces == MAX_NUM_2D_COLLISIONS_REPORTED)
{
Dbg_Message("Number of faces colliding with rectangle exceeds static maximum; dropping additional collisions");
return true;
}
// add the face to the collided surface list
S2DCollSurface& surface = p_coll_data->p_surfaces[p_coll_data->num_surfaces];
surface.normal = Mth::CrossProduct(v1 - v0, v2 - v0).Normalize();
// ignore triangles with zero area
if (surface.normal.LengthSqr() <= 0.0f) continue;
surface.trigger = (face_flags & mFD_TRIGGER);
surface.node_name = this->GetChecksum();
surface.ends[0].point = p_coll_point[0];
surface.ends[1].point = p_coll_point[1];
for (int end_idx = 2; end_idx--; )
{
surface.ends[end_idx].tangent_exists = p_triangle_edge[end_idx] != NO_TRIANGLE_EDGE;
switch (p_triangle_edge[end_idx])
{
case TRIANGLE_EDGE_V0_V1:
surface.ends[end_idx].tangent = (v1 - v0).Normalize();
break;
case TRIANGLE_EDGE_V1_V2:
surface.ends[end_idx].tangent = (v2 - v1).Normalize();
break;
case TRIANGLE_EDGE_V2_V0:
surface.ends[end_idx].tangent = (v0 - v2).Normalize();
break;
case NO_TRIANGLE_EDGE:
break;
}
}
p_coll_data->num_surfaces++;
}
}
return p_coll_data->num_surfaces > 0;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
Dbg_Assert(mp_coll_tri_data);
mp_coll_tri_data->DebugRender(ignore_1, ignore_0);
}
void CCollStaticTri::ProcessOcclusion()
{
Dbg_Assert(mp_coll_tri_data);
mp_coll_tri_data->ProcessOcclusion();
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollStaticTri::CheckForHoles()
{
Dbg_Assert(mp_coll_tri_data);
#ifdef __DEBUG_CODE__
mp_coll_tri_data->CheckForHoles();
#endif
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovTri::CCollMovTri()
{
// This instance can't be used until someone sets the geometry data
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovTri::CCollMovTri(CCollObjTriData *p_coll_data) :
CCollMovable()
{
SetGeometry(p_coll_data);
Dbg_Assert(mp_coll_tri_data);
//m_movement_changed = false;
m_movement_changed = true; // to update m_world_bbox
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollMovTri::~CCollMovTri()
{
// Don't delete the Tri Data
}
// Temp functions
uint32 CCollMovTri::GetFaceFlags(int face_idx) const
{
Dbg_Assert(mp_coll_tri_data);
return mp_coll_tri_data->GetFaceFlags(face_idx);
}
uint16 CCollMovTri::GetFaceTerrainType(int face_idx) const
{
Dbg_Assert(mp_coll_tri_data);
return mp_coll_tri_data->GetFaceTerrainType(face_idx);
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovTri::SetGeometry(CCollObjTriData *p_geom_data)
{
CCollObj::SetGeometry(p_geom_data);
Dbg_Assert(mp_coll_tri_data);
// Re-calculate radius
float new_length;
m_max_radius = 0.0f;
for (int i = 0; i < mp_coll_tri_data->m_num_verts; i++)
{
new_length = mp_coll_tri_data->GetRawVertexPos(i).Length();
if (new_length > m_max_radius)
{
m_max_radius = new_length;
}
}
m_movement_changed = true; // to update m_world_bbox
}
/******************************************************************/
/* */
/* */
/******************************************************************/
CCollObj * CCollMovTri::Clone(bool instance)
{
Dbg_MsgAssert(!instance, ("CCollMovTri::Clone() with instances not implemented yet"));
CCollMovTri *m_new_coll = new CCollMovTri(*this);
m_new_coll->m_Flags |= mSD_CLONE;
m_new_coll->mp_coll_tri_data = mp_coll_tri_data->Clone(instance);
return m_new_coll;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMovTri::WithinBBox(const Mth::CBBox & testBBox)
{
Dbg_Assert(mp_coll_tri_data);
if (m_movement_changed)
{
update_world_bbox();
m_movement_changed = false;
}
return testBBox.Intersect(m_world_bbox);
//return true;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMovTri::CollisionWithLine(const Mth::Line & testLine, const Mth::Vector & lineDir, CollData *p_data, Mth::CBBox *p_bbox)
{
Dbg_Assert(p_data);
Dbg_Assert(mp_coll_tri_data);
bool best_collision = false;
// Find line in local space
Mth::Line local_line;
convert_line_to_local(testLine, local_line);
Mth::Vector local_line_dir(local_line.m_end - local_line.m_start);
// Generate a bounding box for the line
// so it can be tested against the BSP tree
Mth::CBBox line_bbox(local_line.m_start);
line_bbox.AddPoint(local_line.m_end);
uint num_faces;
FaceIndex *p_face_indexes;
p_face_indexes = mp_coll_tri_data->FindIntersectingFaces(line_bbox, num_faces);
for (uint fidx = 0; fidx < num_faces; fidx++, p_face_indexes++)
{
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
Mth::Vector v0, v1, v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[0], v0);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[1], v1);
mp_coll_tri_data->GetRawVertexPos(face->m_vertex_index[2], v2);
}
#else
Mth::Vector *v0, *v1, *v2;
if (mp_coll_tri_data->m_use_face_small)
{
CCollObjTriData::SFaceSmall *face = &(mp_coll_tri_data->mp_face_small[*p_face_indexes]);
v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
} else {
CCollObjTriData::SFace *face = &(mp_coll_tri_data->mp_faces[*p_face_indexes]);
v0 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[0]];
v1 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[1]];
v2 = &mp_coll_tri_data->mp_vert_pos[face->m_vertex_index[2]];
}
#endif // FIXED_POINT_VERTICES
float distance;
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
if (sRayTriangleCollision(&local_line.m_start, &local_line_dir, &v0, &v1, &v2, &distance))
#else
if (sRayTriangleCollision(&local_line.m_start, &local_line_dir, v0, v1, v2, &distance))
#endif
{
SCollSurface collisionSurface;
/* We've got one */
#if defined(FIXED_POINT_VERTICES) || defined(__PLAT_NGC__)
collisionSurface.point = v0;
collisionSurface.index = *p_face_indexes;
// Find normal
Mth::Vector vTmp1(v1 - v0);
Mth::Vector vTmp2(v2 - v0);
#else
collisionSurface.point = (*v0);
collisionSurface.index = *p_face_indexes;
// Find normal
Mth::Vector vTmp1(*v1 - *v0);
Mth::Vector vTmp2(*v2 - *v0);
#endif // FIXED_POINT_VERTICES
collisionSurface.normal = Mth::CrossProduct(vTmp1, vTmp2);
collisionSurface.normal.Normalize();
// Convert to world coordinates
collisionSurface.point.Rotate(m_orient);
collisionSurface.point += m_world_pos;
collisionSurface.normal.Rotate(m_orient);
if (s_found_collision(&testLine, this, &collisionSurface, distance, p_data))
{
best_collision = true;
}
}
}
return best_collision;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
bool CCollMovTri::CollisionWithRectangle(const Mth::Rectangle& rect, const Mth::CBBox& rect_bbox, S2DCollData* p_data)
{
Dbg_MsgAssert(false, ("CCollMovTri::CollisionWithRecangle is not implemented"));
return false;
}
/******************************************************************/
/* */
/* */
/******************************************************************/
void CCollMovTri::DebugRender(uint32 ignore_1, uint32 ignore_0)
{
// Debug box
Mth::Matrix total_mat = m_orient;
total_mat[W] = m_world_pos;
mp_coll_tri_data->DebugRender(total_mat, ignore_1, ignore_0);
}
} // namespace Nx