This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/WWMath/colmathaabtri.cpp

1184 lines
51 KiB
C++

/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WWMath *
* *
* $Archive:: /Commando/Code/wwmath/colmathaabtri.cpp $*
* *
* Author:: Greg Hjelstrom *
* *
* $Modtime:: 1/15/02 2:46p $*
* *
* $Revision:: 19 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* aabtri_separation_test -- test the projected extents for separation *
* aabtri_check_axis -- project the aab and tri onto an arbitrary axis *
* aabtri_check_cross_axis -- projects aab and tri onto a "cross" axis *
* aabtri_check_basis_axis -- projects the aab and tri onto a basis axis *
* aabtri_check_normal_axis -- project the box and tri onto the tri-normal *
* eval_side -- returns -1,0,+1 depending on the sign of val and side *
* aabtri_compute_contact_normal -- computes the normal of the collision *
* CollisionMath::Collide -- collide an aabox into a triangle *
* aabtri_intersect_cross_axis -- intersection check for a "cross-product" axis *
* aabtri_intersect_basis_axis -- intersection check for a basis axis *
* aabtri_intersect_normal_axis -- intersection check for the triangle normal *
* CollisionMath::Intersection_Test -- Intersection check for an AABox and a triangle *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#include "colmath.h"
#include "aabox.h"
#include "tri.h"
#include "wwdebug.h"
/*
** Separating Axes have to be rejected if their length is smaller than some epsilon.
** Otherwise, erroneous results can be reported.
*/
#define AXISLEN_EPSILON2 WWMATH_EPSILON * WWMATH_EPSILON // squared length of a separating axis must be larger than this
/*
** Axes used in Box-Tri intersection tests
** The axes of the box are A0,A1,A2. N is the normal of the triangle,
** E0,E1,E2 are direction vectors for the edges of the triangle.
** (the box axes are labeled A0,A1,A2 as a holdover from the obbox-obbox
** collision code which this was derived from where there are two boxes
** A and B)
*/
enum
{
INTERSECTION = 0,
AXIS_N, // normal of the triangle
AXIS_A0, // first basis vector of the box
AXIS_A1, // second basis vector of the box
AXIS_A2, // third basis vector of the box
AXIS_A0E0, // box0 x edge0...
AXIS_A1E0,
AXIS_A2E0,
AXIS_A0E1,
AXIS_A1E1,
AXIS_A2E1,
AXIS_A0E2,
AXIS_A1E2,
AXIS_A2E2
};
/******************************************************************************************
AABox->Triangle collision
This code is basically a special-case optimization of the OBBox->Triangle collision
detection code. There are many dot and cross products that can be simplified due
to the fact that we know the axes of the boxes are always the same and are aligned
with the world coordinate axes.
Each axis test will use the following logic:
Project D onto the axis being used, it is the separation distance. If the
projection of the extent of the box + the projection of the extent of the
tri is greater than D*axis then the two intersect
March 13, 2000 - Modified these routines to all use a static instance of
the BTCollisionStruct. The compiler was generating lots of extra code for the
constructor of this object and testing determined that re-using the same static
struct was slightly faster anyway.
NOTE: this makes the code not Thread-Safe!!!!
******************************************************************************************/
/*
** BoxTriColStruct
** Scratchpad variables for the AABox-Triangle collision detection functions. One instance
** of this structure will be used for all of the local variables and its pointer will be
** handed of to various inline functions for the axis tests.
** Note that much of the code needs the un-normalized triangle normal. For this reason,
** I have to compute N rather than copying it from the triangle. (commenting this to
** avoid re-generating a difficult to find bug that I had)
*/
struct BTCollisionStruct
{
BTCollisionStruct(void) {}
void Init(const AABoxClass &box,const Vector3 &move,const TriClass &tri,const Vector3 &trimove)
{
StartBad = true; // true until an axis clears it
MaxFrac = -0.01f; // maximum move allowed so far
AxisId = INTERSECTION; // axis that allowed the longest move
Point = 0; // index of triangle point that was closest to the box
Side = 0; // side of the interval
Box = &box;
Tri = &tri;
BoxMove = &move;
TriMove = &trimove;
Vector3::Subtract(*tri.V[0],box.Center,&D); // vector from center of box to vertex 0
Vector3::Subtract(move,trimove,&Move); // move vector relative to stationary triangle
Vector3::Subtract(*tri.V[1],*tri.V[0],&E[0]);
Vector3::Subtract(*tri.V[2],*tri.V[0],&E[1]);
Vector3::Subtract(E[1],E[0],&E[2]);
Vector3::Cross_Product(E[0],E[1],&N);
}
bool StartBad; // Inital configuration is intersecting?
float MaxFrac; // Longest move allowed so far
int AxisId; // Last separating axis
int Side; // which side of the interval
int Point; // Index of the "closest" triangle point (or one of them)
int TestAxisId; // Axis 'id' we're working on
int TestSide; // Was the axis we're working on flipped
int TestPoint; // Index of the closest vertex
Vector3 TestAxis; // Axis we're working on
Vector3 D; // Vector from the center of the box to v0
Vector3 Move; // Move vector relative to stationary triangle
float AE[3][3]; // Dot products of the Basis vectors and edges
float AN[3]; // Dot products of the Basis vectors and the normal
Vector3 AxE[3][3]; // Cross products of the Basis vectors and edges
Vector3 E[3]; // edge vectors for the triangle
Vector3 N; // normal (NOT normalized!!!)
Vector3 FinalD; // Vector from center of box to v0 at end of move
const AABoxClass * Box;
const TriClass * Tri;
const Vector3 * BoxMove;
const Vector3 * TriMove;
private:
// not implemented
BTCollisionStruct(const BTCollisionStruct &);
BTCollisionStruct & operator = (const BTCollisionStruct &);
};
static BTCollisionStruct CollisionContext;
/***********************************************************************************************
* aabtri_separation_test -- test the projected extents for separation *
* *
* Once the extents are projected onto the axis, this function contains *
* the logic that determines the allowed fraction. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
* 7/12/99 GTH : Converted original OBBox code to AABox *
*=============================================================================================*/
static inline bool aabtri_separation_test
(
float lp,float leb0,float leb1
)
{
/*
** - If (I'm no more than 'EPSILON' embedded in the wall)
** - not startbad
** - If (I'm moving towards)
** - fraction = How far I can move before embedding (can be negative if started embedded)
** - If (I can take entire move)
** - accept entire move
** - Else If (I can move more than I could have before; *negative always fails!)
** - update fraction
** - Else
** - Accept entire move since I'm not moving towards
*/
float eps = 0.0f;
if (lp - leb0 <= 0.0f) {
eps = COLLISION_EPSILON * CollisionContext.TestAxis.Length(); // trying to only compute epsilon if I have to
}
if (lp - leb0 > -eps) {
CollisionContext.StartBad = false;
if (leb1 - leb0 > 0.0f) {
float frac = (lp-leb0)/(leb1-leb0);
if (frac >= 1.0f) {
/* moving toward but not hitting triangle */
CollisionContext.AxisId = CollisionContext.TestAxisId;
CollisionContext.MaxFrac = 1.0f;
return true;
} else {
/* moving toward, hitting triangle */
if (frac > CollisionContext.MaxFrac) {
CollisionContext.MaxFrac = frac;
CollisionContext.AxisId = CollisionContext.TestAxisId;
CollisionContext.Side = CollisionContext.TestSide;
CollisionContext.Point = CollisionContext.TestPoint;
}
}
} else {
/* moving away or not moving */
CollisionContext.AxisId = CollisionContext.TestAxisId;
CollisionContext.MaxFrac = 1.0f;
return true;
}
}
return false;
}
/***********************************************************************************************
* aabtri_check_axis -- project the aab and tri onto an arbitrary axis *
* *
* projects the box and the triangle onto the given axis and calls *
* obbtri_separation_test. return true if separation was detected *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
* 7/12/99 GTH : converted to AABox *
*=============================================================================================*/
static inline bool aabtri_check_axis(void)
{
float dist; // separation along the axis
float axismove; // size of the move along the axis.
float leb0; // initial coordinate of the leading edge of the box
float leb1; // final coordinate of the leading edge of the box
float lp; // leading edge of the polygon.
float tmp; // temporary
dist = Vector3::Dot_Product(CollisionContext.D,CollisionContext.TestAxis);
axismove = Vector3::Dot_Product(CollisionContext.Move,CollisionContext.TestAxis);
// I want the axis centered at the box, pointing towards the triangle
if (dist < 0) {
dist = -dist;
axismove = -axismove;
CollisionContext.TestAxis = -CollisionContext.TestAxis;
CollisionContext.TestSide = -1.0f;
} else {
CollisionContext.TestSide = 1.0f;
}
// compute coordinates of the leading edge of the box at t0 and t1
leb0 = CollisionContext.Box->Extent.X * WWMath::Fabs(CollisionContext.TestAxis.X) +
CollisionContext.Box->Extent.Y * WWMath::Fabs(CollisionContext.TestAxis.Y) +
CollisionContext.Box->Extent.Z * WWMath::Fabs(CollisionContext.TestAxis.Z);
leb1 = leb0 + axismove;
// compute coordinate of "leading edge of the triangle" relative to the box center.
lp = 0;
tmp = Vector3::Dot_Product(CollisionContext.E[0],CollisionContext.TestAxis); if (tmp < lp) lp = tmp;
tmp = Vector3::Dot_Product(CollisionContext.E[1],CollisionContext.TestAxis); if (tmp < lp) lp = tmp;
lp = dist + lp;
return aabtri_separation_test(/*CollisionContext,*/lp,leb0,leb1);
}
/***********************************************************************************************
* aabtri_check_cross_axis -- projects aab and tri onto a "cross" axis *
* *
* Assumes that the axis given is one generated from a cross product of one of the edge and *
* basis vectors. In this case, the box extent can be optimized and only two triangle verts *
* need to be checked. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_check_cross_axis
(
float dp,
int dpi,
float leb0
)
{
float p0; // distance from box center to vertex 0
float axismove; // size of the move along the axis.
float leb1; // final coordinate of the leading edge of the box
float lp; // leading edge of the polygon.
p0 = Vector3::Dot_Product(CollisionContext.D,CollisionContext.TestAxis);
axismove = Vector3::Dot_Product(CollisionContext.Move,CollisionContext.TestAxis);
// I want the axis centered at the box, pointing towards the triangle
if (p0 < 0) {
p0 = -p0;
axismove = -axismove;
dp = -dp;
CollisionContext.TestAxis = -CollisionContext.TestAxis;
CollisionContext.TestSide = -1.0f;
} else {
CollisionContext.TestSide = 1.0f;
}
// compute coordinates of the leading edge of the box at t1
leb1 = leb0 + axismove;
// compute coordinate of "leading edge of the triangle" relative to the box center.
lp = 0; CollisionContext.TestPoint = 0;
if (dp < 0) { lp = dp; CollisionContext.TestPoint = dpi; }
lp = p0 + lp;
return aabtri_separation_test(/*CollisionContext,*/lp,leb0,leb1);
}
/***********************************************************************************************
* aabtri_check_basis_axis -- projects the aab and tri onto a basis axis *
* *
* Projects the box and triangle onto an axis that is assumed to be a basis *
* vector from the box. In this case, we can skip a dot-product and use the *
* corresponding extent of the box (which is passed in as leb0). *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_check_basis_axis
(
float leb0,
float dp1,
float dp2
)
{
float dist; // separation along the axis
float axismove; // size of the move along the axis.
float leb1; // final coordinate of the leading edge of the box
float lp; // leading edge of the polygon.
dist = Vector3::Dot_Product(CollisionContext.D,CollisionContext.TestAxis);
axismove = Vector3::Dot_Product(CollisionContext.Move,CollisionContext.TestAxis);
// we want the axis centered at the box, pointing towards the triangle
if (dist < 0) {
dist = -dist;
axismove = -axismove;
dp1 = -dp1;
dp2 = -dp2;
CollisionContext.TestAxis = -CollisionContext.TestAxis;
CollisionContext.TestSide = -1.0f;
} else {
CollisionContext.TestSide = 1.0f;
}
// this is the "optimization", leb0 = one of the extents
leb1 = leb0 + axismove;
// compute coordinate of "leading edge of the polygon" relative to the box center.
lp = 0; CollisionContext.TestPoint = 0;
if (dp1 < lp) { lp = dp1; CollisionContext.TestPoint = 1; }
if (dp2 < lp) { lp = dp2; CollisionContext.TestPoint = 2; }
lp = dist + lp;
return aabtri_separation_test(/*CollisionContext,*/lp,leb0,leb1);
}
/***********************************************************************************************
* aabtri_check_normal_axis -- project the box and tri onto the tri-normal *
* *
* Projects the box and triangle onto an axis that is assumed to be the normal *
* vector from the triangle. In this case, the triangle extents are zero. *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_check_normal_axis(void)
{
float dist; // separation along the axis
float axismove; // size of the move along the axis.
float leb0; // initial coordinate of the leading edge of the box
float leb1; // final coordinate of the leading edge of the box
float lp; // leading edge of the polygon.
dist = Vector3::Dot_Product(CollisionContext.D,CollisionContext.TestAxis);
axismove = Vector3::Dot_Product(CollisionContext.Move,CollisionContext.TestAxis);
// we want the axis centered at the box, pointing towards the triangle
if (dist < 0) {
dist = -dist;
axismove = -axismove;
CollisionContext.TestAxis = -CollisionContext.TestAxis;
CollisionContext.TestSide = -1.0f;
} else {
CollisionContext.TestSide = 1.0f;
}
leb0 = CollisionContext.Box->Extent.X * WWMath::Fabs(CollisionContext.AN[0]) +
CollisionContext.Box->Extent.Y * WWMath::Fabs(CollisionContext.AN[1]) +
CollisionContext.Box->Extent.Z * WWMath::Fabs(CollisionContext.AN[2]);
leb1 = leb0 + axismove;
CollisionContext.TestPoint = 0;
lp = dist; // this is the "optimization", don't have to find lp
return aabtri_separation_test(/*CollisionContext,*/lp,leb0,leb1);
}
/***********************************************************************************************
* eval_side -- returns -1,0,+1 depending on the sign of val and side *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
static inline float eval_side(float val,int side)
{
if (val > 0.0f) {
return side;
} else if (val < 0.0f) {
return -side;
} else {
return 0.0f;
}
}
/***********************************************************************************************
* aabtri_compute_contact_normal -- computes the normal of the collision *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
static inline void aabtri_compute_contact_normal
(
Vector3 & set_norm
)
{
#if 1
switch(CollisionContext.AxisId)
{
case INTERSECTION:
set_norm = CollisionContext.N;
set_norm.Normalize();
break;
case AXIS_N:
set_norm = -CollisionContext.Side * CollisionContext.N;
set_norm.Normalize();
break;
case AXIS_A0:
set_norm = -CollisionContext.Side * Vector3(1.0f,0.0f,0.0f);
break;
case AXIS_A1:
set_norm = -CollisionContext.Side * Vector3(0.0f,1.0f,0.0f);
break;
case AXIS_A2:
set_norm = -CollisionContext.Side * Vector3(0.0f,0.0f,1.0f);
break;
case AXIS_A0E0:
set_norm = -CollisionContext.Side * CollisionContext.AxE[0][0];
set_norm.Normalize();
break;
case AXIS_A1E0:
set_norm = -CollisionContext.Side * CollisionContext.AxE[1][0];
set_norm.Normalize();
break;
case AXIS_A2E0:
set_norm = -CollisionContext.Side * CollisionContext.AxE[2][0];
set_norm.Normalize();
break;
case AXIS_A0E1:
set_norm = -CollisionContext.Side * CollisionContext.AxE[0][1];
set_norm.Normalize();
break;
case AXIS_A1E1:
set_norm = -CollisionContext.Side * CollisionContext.AxE[1][1];
set_norm.Normalize();
break;
case AXIS_A2E1:
set_norm = -CollisionContext.Side * CollisionContext.AxE[2][1];
set_norm.Normalize();
break;
case AXIS_A0E2:
set_norm = -CollisionContext.Side * CollisionContext.AxE[0][2];
set_norm.Normalize();
break;
case AXIS_A1E2:
set_norm = -CollisionContext.Side * CollisionContext.AxE[1][2];
set_norm.Normalize();
break;
case AXIS_A2E2:
set_norm = -CollisionContext.Side * CollisionContext.AxE[2][2];
set_norm.Normalize();
break;
}
WWASSERT(set_norm.Length2() > 0.0f);
#else
set_norm = *CollisionContext.N;
set_norm.Normalize();
if (Vector3::Dot_Product(set_norm,CollisionContext.Move) > 0.0f) {
set_norm = -(set_norm);
}
#endif
}
inline void VERIFY_CROSS(const Vector3 & a, const Vector3 & b,const Vector3 & cross)
{
#ifdef WWDEBUG
Vector3 tmp_cross;
Vector3::Cross_Product(a,b,&tmp_cross);
Vector3 diff = cross - tmp_cross;
WWASSERT(WWMath::Fabs(diff.Length()) < 0.0001f);
#endif
}
/***********************************************************************************************
* CollisionMath::Collide -- collide an aabox into a triangle *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 4/8/99 GTH : Created. *
*=============================================================================================*/
bool CollisionMath::Collide
(
const AABoxClass & box,
const Vector3 & move,
const TriClass & tri,
CastResultStruct * result
)
{
TRACK_COLLISION_AABOX_TRI;
float dp,leb0;
CollisionContext.Init(box,move,tri,Vector3(0,0,0));
/*
** AXIS_N
*/
CollisionContext.TestAxis = CollisionContext.N;
CollisionContext.TestAxisId = AXIS_N;
CollisionContext.AN[0] = CollisionContext.N.X;
CollisionContext.AN[1] = CollisionContext.N.Y;
CollisionContext.AN[2] = CollisionContext.N.Z;
if (aabtri_check_normal_axis()) goto exit;
/*
** AXIS_A0
*/
CollisionContext.TestAxis.Set(1,0,0);
CollisionContext.TestAxisId = AXIS_A0;
CollisionContext.AE[0][0] = CollisionContext.E[0].X;
CollisionContext.AE[0][1] = CollisionContext.E[1].X;
if (aabtri_check_basis_axis(box.Extent.X,CollisionContext.AE[0][0],CollisionContext.AE[0][1])) goto exit;
/*
** AXIS_A1
*/
CollisionContext.TestAxis.Set(0,1,0);
CollisionContext.TestAxisId = AXIS_A1;
CollisionContext.AE[1][0] = CollisionContext.E[0].Y;
CollisionContext.AE[1][1] = CollisionContext.E[1].Y;
if (aabtri_check_basis_axis(box.Extent.Y,CollisionContext.AE[1][0],CollisionContext.AE[1][1])) goto exit;
/*
** AXIS_A2
*/
CollisionContext.TestAxis.Set(0,0,1);
CollisionContext.TestAxisId = AXIS_A2;
CollisionContext.AE[2][0] = CollisionContext.E[0].Z;
CollisionContext.AE[2][1] = CollisionContext.E[1].Z;
if (aabtri_check_basis_axis(box.Extent.Z,CollisionContext.AE[2][0],CollisionContext.AE[2][1])) goto exit;
/*
** AXIS_A0xE0
*/
CollisionContext.AxE[0][0].Set(0,-CollisionContext.E[0].Z,CollisionContext.E[0].Y);
VERIFY_CROSS(Vector3(1,0,0),CollisionContext.E[0],CollisionContext.AxE[0][0]);
CollisionContext.TestAxis = CollisionContext.AxE[0][0];
CollisionContext.TestAxisId = AXIS_A0E0;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = CollisionContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(CollisionContext.AE[2][0]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[1][0]);
if (aabtri_check_cross_axis(dp,2,leb0)) goto exit;
}
/*
** AXIS_A0xE1
*/
CollisionContext.AxE[0][1].Set(0,-CollisionContext.E[1].Z,CollisionContext.E[1].Y);
VERIFY_CROSS(Vector3(1,0,0),CollisionContext.E[1],CollisionContext.AxE[0][1]);
CollisionContext.TestAxis = CollisionContext.AxE[0][1];
CollisionContext.TestAxisId = AXIS_A0E1;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(CollisionContext.AE[2][1]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[1][1]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** AXIS_A0xE2
*/
CollisionContext.AE[0][2] = CollisionContext.E[2].X;
CollisionContext.AE[1][2] = CollisionContext.E[2].Y;
CollisionContext.AE[2][2] = CollisionContext.E[2].Z;
CollisionContext.AxE[0][2].Set(0,-CollisionContext.E[2].Z,CollisionContext.E[2].Y);
VERIFY_CROSS(Vector3(1,0,0),CollisionContext.E[2],CollisionContext.AxE[0][2]);
CollisionContext.TestAxis = CollisionContext.AxE[0][2];
CollisionContext.TestAxisId = AXIS_A0E2;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(CollisionContext.AE[2][2]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[1][2]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** AXIS_A1xE0
*/
CollisionContext.AxE[1][0].Set(CollisionContext.E[0].Z,0,-CollisionContext.E[0].X);
VERIFY_CROSS(Vector3(0,1,0),CollisionContext.E[0],CollisionContext.AxE[1][0]);
CollisionContext.TestAxis = CollisionContext.AxE[1][0];
CollisionContext.TestAxisId = AXIS_A1E0;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = CollisionContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[2][0]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[0][0]);
if (aabtri_check_cross_axis(dp,2,leb0)) goto exit;
}
/*
** AXIS_A1xE1
*/
CollisionContext.AxE[1][1].Set(CollisionContext.E[1].Z,0,-CollisionContext.E[1].X);
VERIFY_CROSS(Vector3(0,1,0),CollisionContext.E[1],CollisionContext.AxE[1][1]);
CollisionContext.TestAxis = CollisionContext.AxE[1][1];
CollisionContext.TestAxisId = AXIS_A1E1;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[2][1]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[0][1]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** AXIS_A1xE2
*/
CollisionContext.AxE[1][2].Set(CollisionContext.E[2].Z,0,-CollisionContext.E[2].X);
VERIFY_CROSS(Vector3(0,1,0),CollisionContext.E[2],CollisionContext.AxE[1][2]);
CollisionContext.TestAxis = CollisionContext.AxE[1][2];
CollisionContext.TestAxisId = AXIS_A1E2;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[2][2]) + box.Extent[2]*WWMath::Fabs(CollisionContext.AE[0][2]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** AXIS_A2xE0
*/
CollisionContext.AxE[2][0].Set(-CollisionContext.E[0].Y,CollisionContext.E[0].X,0);
VERIFY_CROSS(Vector3(0,0,1),CollisionContext.E[0],CollisionContext.AxE[2][0]);
CollisionContext.TestAxis = CollisionContext.AxE[2][0];
CollisionContext.TestAxisId = AXIS_A2E0;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = CollisionContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[1][0]) + box.Extent[1]*WWMath::Fabs(CollisionContext.AE[0][0]);
if (aabtri_check_cross_axis(dp,2,leb0)) goto exit;
}
/*
** AXIS_A2xE1
*/
CollisionContext.AxE[2][1].Set(-CollisionContext.E[1].Y,CollisionContext.E[1].X,0);
VERIFY_CROSS(Vector3(0,0,1),CollisionContext.E[1],CollisionContext.AxE[2][1]);
CollisionContext.TestAxis = CollisionContext.AxE[2][1];
CollisionContext.TestAxisId = AXIS_A2E1;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[1][1]) + box.Extent[1]*WWMath::Fabs(CollisionContext.AE[0][1]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** AXIS_A2xE2
*/
CollisionContext.AxE[2][2].Set(-CollisionContext.E[2].Y,CollisionContext.E[2].X,0);
VERIFY_CROSS(Vector3(0,0,1),CollisionContext.E[2],CollisionContext.AxE[2][2]);
CollisionContext.TestAxis = CollisionContext.AxE[2][2];
CollisionContext.TestAxisId = AXIS_A2E2;
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
dp = -CollisionContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(CollisionContext.AE[1][2]) + box.Extent[1]*WWMath::Fabs(CollisionContext.AE[0][2]);
if (aabtri_check_cross_axis(dp,1,leb0)) goto exit;
}
/*
** Last ditch effort, check an axis based on the move vector
*/
if (!CollisionContext.StartBad) {
CollisionContext.TestPoint = CollisionContext.Point;
CollisionContext.TestAxisId = CollisionContext.AxisId;
CollisionContext.TestAxis.Set(0,-CollisionContext.Move.Z,CollisionContext.Move.Y); // A0 X Move
VERIFY_CROSS(Vector3(1,0,0),CollisionContext.Move,CollisionContext.TestAxis);
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
if (aabtri_check_axis()) goto exit;
}
CollisionContext.TestAxis.Set(CollisionContext.Move.Z,0,-CollisionContext.Move.X); // A1 X Move
VERIFY_CROSS(Vector3(0,1,0),CollisionContext.Move,CollisionContext.TestAxis);
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
if (aabtri_check_axis()) goto exit;
}
CollisionContext.TestAxis.Set(-CollisionContext.Move.Y,CollisionContext.Move.X,0); // A2 X Move
VERIFY_CROSS(Vector3(0,0,1),CollisionContext.Move,CollisionContext.TestAxis);
if (CollisionContext.TestAxis.Length2() > AXISLEN_EPSILON2) {
if (aabtri_check_axis()) goto exit;
}
}
exit:
/*
** If MaxFrac is less than zero, clamp it to zero. Negative fractions can
** leak through this routine due to the epsilon in the separation test.
*/
if (CollisionContext.MaxFrac < 0.0f) {
CollisionContext.MaxFrac = 0.0f;
}
/*
** If the triangle and box are intersecting before the move, return that
** result.
*/
if (CollisionContext.StartBad) {
result->StartBad = true;
result->Fraction = 0.0f;
result->Normal = *tri.N;
TRACK_COLLISION_AABOX_TRI_HIT;
return true;
}
/*
** If the fraction allowed is basically equal to the fraction allowed by
** another polygon, try to pick the polygon which is least "edge-on" to the
** move.
*/
if ((CollisionContext.MaxFrac <= result->Fraction) && (CollisionContext.MaxFrac < 1.0f)) {
/*
** Reflect the normal if it is pointing the same way as our move
** (probably hitting the back side of a polygon)
*/
Vector3 tmp_norm(0.0f,0.0f,0.0f);
aabtri_compute_contact_normal(tmp_norm);
// if (Vector3::Dot_Product(tmp_norm,move) > 0.0f) {
// tmp_norm = -tmp_norm;
// }
/*
** If this polygon cuts off more of the move -OR- this polygon cuts
** of the same amount but has a "better" normal, then use this normal
*/
if ( (WWMath::Fabs(CollisionContext.MaxFrac - result->Fraction) > WWMATH_EPSILON) ||
(Vector3::Dot_Product(tmp_norm,move) < Vector3::Dot_Product(result->Normal,move)))
{
result->Normal = tmp_norm;
WWASSERT(WWMath::Fabs(result->Normal.Length() - 1.0f) < WWMATH_EPSILON);
}
result->Fraction = CollisionContext.MaxFrac;
TRACK_COLLISION_AABOX_TRI_HIT;
return true;
}
return false;
}
/*
** AABTIntersectStruct
** Scratchpad variables for the AABox-Triangle intersection functions. One instance
** of this structure will be used for all of the local variables and its pointer will be
** handed of to various inline functions for the axis tests.
** Note that much of the code needs the un-normalized triangle normal. For this reason,
** I have to compute N rather than copying it from the triangle. (commenting this to
** avoid re-generating a difficult to find bug that I had)
*/
struct AABTIntersectStruct
{
AABTIntersectStruct(void) :
Box(NULL),
Tri(NULL)
{
}
void Init(const AABoxClass &box,const TriClass &tri)
{
Box = &box;
Tri = &tri;
Vector3::Subtract(*tri.V[0],box.Center,&D); // vector from center of box to vertex 0
Vector3::Subtract(*tri.V[1],*tri.V[0],&E[0]);
Vector3::Subtract(*tri.V[2],*tri.V[0],&E[1]);
Vector3::Subtract(E[1],E[0],&E[2]);
Vector3::Cross_Product(E[0],E[1],&N);
}
Vector3 D; // Vector from the center of the box to v0
float AE[3][3]; // Dot products of the Basis vectors and edges
float AN[3]; // Dot products of the Basis vectors and the normal
Vector3 AxE[3][3]; // Cross produts of the Basis vectors and edges
Vector3 E[3]; // edge vectors for the triangle
Vector3 N; // normal (NOT normalized!!!)
const AABoxClass * Box;
const TriClass * Tri;
private:
// not implemented
AABTIntersectStruct(const AABTIntersectStruct &);
AABTIntersectStruct & operator = (const AABTIntersectStruct &);
};
static AABTIntersectStruct IntersectContext;
/***********************************************************************************************
* aabtri_intersect_cross_axis -- intersection check for a "cross-product" axis *
* *
* axis being checked is a cross product between a triangle edge and a box basis vector *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/4/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_intersect_cross_axis
(
Vector3 & axis,
float dp,
float leb0
)
{
float p0; // distance from box center to vertex 0
float lp; // leading edge of the polygon.
p0 = Vector3::Dot_Product(IntersectContext.D,axis);
// I want the axis centered at the box, pointing towards the triangle
if (p0 < 0) {
p0 = -p0;
axis = -axis;
dp = -dp;
}
// compute coordinate of "leading edge of the triangle" relative to the box center.
lp = 0;
if (dp < 0) { lp = dp; }
lp = p0 + lp;
return (lp - leb0 > -WWMATH_EPSILON);
}
/***********************************************************************************************
* aabtri_intersect_basis_axis -- intersection check for a basis axis *
* *
* axis being checked is one of the basis vectors for the box *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/4/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_intersect_basis_axis
(
Vector3 & axis,
float leb0,
float dp1,
float dp2
)
{
float dist; // separation along the axis
float lp; // leading edge of the polygon.
dist = Vector3::Dot_Product(IntersectContext.D,axis);
// we want the axis centered at the box, pointing towards the triangle
if (dist < 0) {
dist = -dist;
axis = -axis;
dp1 = -dp1;
dp2 = -dp2;
}
// compute coordinate of "leading edge of the polygon" relative to the box center.
lp = 0;
if (dp1 < lp) { lp = dp1; }
if (dp2 < lp) { lp = dp2; }
lp = dist + lp;
return (lp - leb0 > -WWMATH_EPSILON);
}
/***********************************************************************************************
* aabtri_intersect_normal_axis -- intersection check for the triangle normal *
* *
* axis being checked is the triangle's normal *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 5/4/99 GTH : Created. *
*=============================================================================================*/
static inline bool aabtri_intersect_normal_axis
(
Vector3 & axis
)
{
float dist; // separation along the axis
float leb0; // initial coordinate of the leading edge of the box
float lp; // leading edge of the polygon.
dist = Vector3::Dot_Product(IntersectContext.D,axis);
// we want the axis centered at the box, pointing towards the triangle
if (dist < 0) {
dist = -dist;
axis = -axis;
}
leb0 = IntersectContext.Box->Extent.X * WWMath::Fabs(IntersectContext.AN[0]) +
IntersectContext.Box->Extent.Y * WWMath::Fabs(IntersectContext.AN[1]) +
IntersectContext.Box->Extent.Z * WWMath::Fabs(IntersectContext.AN[2]);
lp = dist; // this is the "optimization", don't have to find lp
return (lp - leb0 > -WWMATH_EPSILON);
}
/***********************************************************************************************
* CollisionMath::Intersection_Test -- Intersection check for an AABox and a triangle *
* *
* INPUT: *
* *
* OUTPUT: *
* *
* WARNINGS: *
* *
* HISTORY: *
* 1/20/00 gth : copied from OBBox version and removed some Dot-products *
*=============================================================================================*/
bool CollisionMath::Intersection_Test(const AABoxClass & box,const TriClass & tri)
{
Vector3 axis;
float dp,leb0;
IntersectContext.Init(box,tri);
/*
** AXIS_N
*/
axis = IntersectContext.N;
IntersectContext.AN[0] = IntersectContext.N.X;
IntersectContext.AN[1] = IntersectContext.N.Y;
IntersectContext.AN[2] = IntersectContext.N.Z;
if (aabtri_intersect_normal_axis(axis)) return false;
/*
** AXIS_A0
*/
axis.Set(1,0,0);
IntersectContext.AE[0][0] = IntersectContext.E[0].X;
IntersectContext.AE[0][1] = IntersectContext.E[1].Y;
if (aabtri_intersect_basis_axis(axis,box.Extent.X,IntersectContext.AE[0][0],IntersectContext.AE[0][1])) return false;
/*
** AXIS_A1
*/
axis.Set(0,1,0);
IntersectContext.AE[1][0] = IntersectContext.E[0].Y;
IntersectContext.AE[1][1] = IntersectContext.E[1].Y;
if (aabtri_intersect_basis_axis(axis,box.Extent.Y,IntersectContext.AE[1][0],IntersectContext.AE[1][1])) return false;
/*
** AXIS_A2
*/
axis.Set(0,0,1);
IntersectContext.AE[2][0] = IntersectContext.E[0].Z;
IntersectContext.AE[2][1] = IntersectContext.E[1].Z;
if (aabtri_intersect_basis_axis(axis,box.Extent.Z,IntersectContext.AE[2][0],IntersectContext.AE[2][1])) return false;
/*
** AXIS_A0xE0
*/
Vector3::Cross_Product(Vector3(1,0,0),IntersectContext.E[0],&IntersectContext.AxE[0][0]);
axis = IntersectContext.AxE[0][0];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = IntersectContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(IntersectContext.AE[2][0]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[1][0]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A0xE1
*/
Vector3::Cross_Product(Vector3(1,0,0),IntersectContext.E[1],&IntersectContext.AxE[0][1]);
axis = IntersectContext.AxE[0][1];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(IntersectContext.AE[2][1]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[1][1]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A0xE2
*/
Vector3::Cross_Product(Vector3(1,0,0),IntersectContext.E[2],&IntersectContext.AxE[0][2]);
axis = IntersectContext.AxE[0][2];
IntersectContext.AE[1][2] = IntersectContext.E[2].Y;
IntersectContext.AE[2][2] = IntersectContext.E[2].Z;
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[0];
leb0 = box.Extent[1]*WWMath::Fabs(IntersectContext.AE[2][2]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[1][2]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A1xE0
*/
Vector3::Cross_Product(Vector3(0,1,0),IntersectContext.E[0],&IntersectContext.AxE[1][0]);
axis = IntersectContext.AxE[1][0];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = IntersectContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[2][0]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[0][0]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A1xE1
*/
Vector3::Cross_Product(Vector3(0,1,0),IntersectContext.E[1],&IntersectContext.AxE[1][1]);
axis = IntersectContext.AxE[1][1];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[2][1]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[0][1]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A1xE2
*/
Vector3::Cross_Product(Vector3(0,1,0),IntersectContext.E[2],&IntersectContext.AxE[1][2]);
axis = IntersectContext.AxE[1][2];
IntersectContext.AE[0][2] = IntersectContext.E[2].X;
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[1];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[2][2]) + box.Extent[2]*WWMath::Fabs(IntersectContext.AE[0][2]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A2xE0
*/
Vector3::Cross_Product(Vector3(0,0,1),IntersectContext.E[0],&IntersectContext.AxE[2][0]);
axis = IntersectContext.AxE[2][0];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = IntersectContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[1][0]) + box.Extent[1]*WWMath::Fabs(IntersectContext.AE[0][0]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A2xE1
*/
Vector3::Cross_Product(Vector3(0,0,1),IntersectContext.E[1],&IntersectContext.AxE[2][1]);
axis = IntersectContext.AxE[2][1];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[1][1]) + box.Extent[1]*WWMath::Fabs(IntersectContext.AE[0][1]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
/*
** AXIS_A2xE2
*/
Vector3::Cross_Product(Vector3(0,0,1),IntersectContext.E[2],&IntersectContext.AxE[2][2]);
axis = IntersectContext.AxE[2][2];
if (axis.Length2() > AXISLEN_EPSILON2) {
dp = -IntersectContext.AN[2];
leb0 = box.Extent[0]*WWMath::Fabs(IntersectContext.AE[1][2]) + box.Extent[1]*WWMath::Fabs(IntersectContext.AE[0][2]);
if (aabtri_intersect_cross_axis(axis,dp,leb0)) return false;
}
return true;
}