| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510 |
- /*
- ** Command & Conquer Generals Zero Hour(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/colmathobbtri.cpp $*
- * *
- * Author:: Greg Hjelstrom *
- * *
- * $Modtime:: 11/28/01 5:56p $*
- * *
- * $Revision:: 16 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * obbtri_collision_separation_test -- test the projected extents for separation *
- * obbtri_check_collision_axis -- project the obb and tri onto an arbitrary axis *
- * obbtri_check_collision_cross_axis -- intersection check for a "cross-product" axis *
- * obbtri_check_collision_basis_axis -- intersection check for a basis axis *
- * obbtri_check_collision_normal_axis -- intersection check for the triangle normal *
- * eval_side -- returns -1,0,+1 depending on the signe of val and side *
- * obbtri_compute_contact_normal -- computes the normal of the collision *
- * eval_A0_point -- contact point parameters for A0xEx *
- * eval_A1_point -- contact point parameters for A1xEx *
- * eval_A2_point -- contact point parameters for A2xEx *
- * obbtri_compute_contact_point -- compute the contact point *
- * CollisionMath::Collide -- collide an obbox into a triangle *
- * obbtri_intersection_separation_test -- test the projected extents for intersection *
- * obbtri_check_intersection_cross_axis -- intersection check for a "cross-product" axis *
- * obbtri_check_intersection_basis_axis -- intersection check for a basis axis *
- * obbtri_check_intersection_normal_axis -- intersection check for the triangle normal *
- * CollisionMath::Intersection_Test -- Intersection check for an OBBox and a triangle *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "colmath.h"
- #include "obbox.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
- };
- /******************************************************************************************
-
- OBBox->Triangle collision
- As with most of the collision detection functions, this code is based on the theorem
- that given any two non-intersecting convex polyhedra, a separating plane/axis
- can be found that will be defined by one of the face normals of one of the polyhedra
- or the cross product of an edge from each polyhedra. The key optimization for
- boxes is that many of these axes are the same. The normal to a face is the same
- as the axis for four of the edges of the box, etc.
- This test must be done using the tri normal, the three basis vectors
- for the box, and three vectors defined by the cross products of the basis
- and edge vectors. When testing the basis vectors, there will be two extents for
- the tri (the other two vectors projected onto the axis being tested).
- The appropriate extent must be used in the test (largest value which is pointing
- towards the direction of the box). In addition, I've found that I must test axes
- defined by the cross products of the move vector and the box axes. These tests
- catch "false collisions" where the box passes very close to the triangle but does
- not actually hit it.
- 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
- ******************************************************************************************/
- /**
- ** BoxTriColStruct
- ** Scratchpad variables for the OBBox-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)
- **
- ** Note that the MaxFrac variable starts negative so that we can accept slightly negative
- ** fractions (interpenetrations). But we clamp the returned result to 0.0 so that we never
- ** allow an object to get more embedded, just to possibly break itself free if it is within
- ** the epsilon. This is important because sometimes objects seem to intersect simply due to
- ** floating point roundoff error...
- */
- struct BTCollisionStruct
- {
- BTCollisionStruct(const OBBoxClass &box,const Vector3 &move,const TriClass &tri,const Vector3 &trimove) :
- Box(box),
- Tri(tri),
- BoxMove(move),
- TriMove(trimove)
- {
- Reset();
- }
- void Reset(void)
- {
- StartBad = true; // true until an axis clears it
- MaxFrac = -1.0f; // maximum move allowed so far (accept slightly negative but clamp to zero at end)
- 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
- Vector3::Subtract(*Tri.V[0],Box.Center,&D); // vector from center of box to vertex 0
- Vector3::Subtract(BoxMove,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]);
- A[0].Set(Box.Basis[0][0],Box.Basis[1][0],Box.Basis[2][0]);
- A[1].Set(Box.Basis[0][1],Box.Basis[1][1],Box.Basis[2][1]);
- A[2].Set(Box.Basis[0][2],Box.Basis[1][2],Box.Basis[2][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 Point; // Index of the "closest" triangle point (or one of them)
- int Side; // which side of the interval
-
- int TestSide; // Was the axis we're working on flipped
- int TestAxisId; // Axis 'id' we're working on
- 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 produts of the Basis vectors and edges
- Vector3 A[3]; // basis vectors for the box
- 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 OBBoxClass & Box;
- const TriClass & Tri;
- const Vector3 & BoxMove;
- const Vector3 & TriMove;
- private:
-
- // not implemented
- BTCollisionStruct(const BTCollisionStruct &);
- BTCollisionStruct & operator = (const BTCollisionStruct &);
- };
- /***********************************************************************************************
- * obbtri_collision_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: *
- * true = objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_collision_separation_test
- (
- BTCollisionStruct & context,
- float lp,float leb0,float leb1
- )
- {
- /*
- ** - compute 'EPSILON' normalized to the length of the axis
- ** - If (I'm no more than 'EPSILON' embedded in the polygon)
- ** - 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 the polygon
- */
- float eps = 0.0f;
- if (lp - leb0 <= 0.0f) {
- eps = COLLISION_EPSILON * context.TestAxis.Length(); // trying to only compute epsilon if I have to
- }
- if (lp - leb0 > -eps) {
- context.StartBad = false;
- if (leb1 - leb0 > 0.0f) {
- float frac = (lp-leb0)/(leb1-leb0);
- if (frac >= 1.0f) {
- /* moving toward but not hitting triangle */
- context.AxisId = context.TestAxisId;
- context.MaxFrac = 1.0f;
- return true;
- } else {
- /* moving toward, hitting triangle */
- if (frac > context.MaxFrac) {
- context.MaxFrac = frac;
- context.AxisId = context.TestAxisId;
- context.Side = context.TestSide;
- context.Point = context.TestPoint;
- }
- }
- } else {
- /* moving away or not moving */
- context.AxisId = context.TestAxisId;
- context.MaxFrac = 1.0f;
- return true;
- }
- }
- return false;
- }
- /***********************************************************************************************
- * obbtri_check_collision_axis -- project the obb 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: *
- * true = objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_collision_axis(BTCollisionStruct & context)
- {
- 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(context.D,context.TestAxis);
- axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
- // I want the axis centered at the box, pointing towards the triangle
- if (dist < 0) {
- dist = -dist;
- axismove = -axismove;
- context.TestAxis = -context.TestAxis;
- context.TestSide = -1.0f;
- } else {
- context.TestSide = 1.0f;
- }
- // compute coordinates of the leading edge of the box at t0 and t1
- leb0 = context.Box.Extent.X * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[0])) +
- context.Box.Extent.Y * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[1])) +
- context.Box.Extent.Z * WWMath::Fabs(Vector3::Dot_Product(context.TestAxis,context.A[2]));
- leb1 = leb0 + axismove;
-
- // compute coordinate of "leading edge of the triangle" relative to the box center.
- lp = 0;
- tmp = Vector3::Dot_Product(context.E[0],context.TestAxis); if (tmp < lp) lp = tmp;
- tmp = Vector3::Dot_Product(context.E[1],context.TestAxis); if (tmp < lp) lp = tmp;
- lp = dist + lp;
- return obbtri_collision_separation_test(context,lp,leb0,leb1);
- }
- /***********************************************************************************************
- * obbtri_check_collision_cross_axis -- projects obb 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: *
- * true = objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_collision_cross_axis
- (
- BTCollisionStruct & context,
- 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(context.D,context.TestAxis);
- axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
- // I want the axis centered at the box, pointing towards the triangle
- if (p0 < 0) {
- p0 = -p0;
- axismove = -axismove;
- dp = -dp;
- context.TestAxis = -context.TestAxis;
- context.TestSide = -1.0f;
- } else {
- context.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; context.TestPoint = 0;
- if (dp < 0) { lp = dp; context.TestPoint = dpi; }
- lp = p0 + lp;
- return obbtri_collision_separation_test(context,lp,leb0,leb1);
- }
- /***********************************************************************************************
- * obbtri_check_collision_basis_axis -- projects the obb 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: *
- * true = objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_collision_basis_axis
- (
- BTCollisionStruct & context,
- 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(context.D,context.TestAxis);
- axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
- // we want the axis centered at the box, pointing towards the triangle
- if (dist < 0) {
- dist = -dist;
- axismove = -axismove;
- dp1 = -dp1;
- dp2 = -dp2;
- context.TestAxis = -context.TestAxis;
- context.TestSide = -1.0f;
- } else {
- context.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; context.TestPoint = 0;
- if (dp1 < lp) { lp = dp1; context.TestPoint = 1; }
- if (dp2 < lp) { lp = dp2; context.TestPoint = 2; }
- lp = dist + lp;
- return obbtri_collision_separation_test(context,lp,leb0,leb1);
- }
- /***********************************************************************************************
- * obbtri_check_collision_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: *
- * true = objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_collision_normal_axis(BTCollisionStruct & context)
- {
- 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(context.D,context.TestAxis);
- axismove = Vector3::Dot_Product(context.Move,context.TestAxis);
- // we want the axis centered at the box, pointing towards the triangle
- if (dist < 0) {
- dist = -dist;
- axismove = -axismove;
- context.TestAxis = -context.TestAxis;
- context.TestSide = -1.0f;
- } else {
- context.TestSide = 1.0f;
- }
- leb0 = context.Box.Extent.X * WWMath::Fabs(context.AN[0]) +
- context.Box.Extent.Y * WWMath::Fabs(context.AN[1]) +
- context.Box.Extent.Z * WWMath::Fabs(context.AN[2]);
- leb1 = leb0 + axismove;
- context.TestPoint = 0;
- lp = dist; // this is the "optimization", don't have to find lp
- return obbtri_collision_separation_test(context,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;
- }
- }
- /***********************************************************************************************
- * obbtri_compute_contact_normal -- computes the normal of the collision *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline void obbtri_compute_contact_normal
- (
- const BTCollisionStruct & context,
- Vector3 * set_normal
- )
- {
- switch(context.AxisId)
- {
- case INTERSECTION:
- // WWASSERT(0);
- break;
- case AXIS_N:
- *set_normal = -context.Side * *context.Tri.N;
- break;
- case AXIS_A0:
- *set_normal = -context.Side * context.A[0];
- break;
- case AXIS_A1:
- *set_normal = -context.Side * context.A[1];
- break;
- case AXIS_A2:
- *set_normal = -context.Side * context.A[2];
- break;
- case AXIS_A0E0:
- *set_normal = -context.Side * context.AxE[0][0];
- set_normal->Normalize();
- break;
- case AXIS_A1E0:
- *set_normal = -context.Side * context.AxE[1][0];
- set_normal->Normalize();
- break;
- case AXIS_A2E0:
- *set_normal = -context.Side * context.AxE[2][0];
- set_normal->Normalize();
- break;
- case AXIS_A0E1:
- *set_normal = -context.Side * context.AxE[0][1];
- set_normal->Normalize();
- break;
- case AXIS_A1E1:
- *set_normal = -context.Side * context.AxE[1][1];
- set_normal->Normalize();
- break;
- case AXIS_A2E1:
- *set_normal = -context.Side * context.AxE[2][1];
- set_normal->Normalize();
- break;
- case AXIS_A0E2:
- *set_normal = -context.Side * context.AxE[0][2];
- set_normal->Normalize();
- break;
- case AXIS_A1E2:
- *set_normal = -context.Side * context.AxE[1][2];
- set_normal->Normalize();
- break;
- case AXIS_A2E2:
- *set_normal = -context.Side * context.AxE[2][2];
- set_normal->Normalize();
- break;
- }
- WWASSERT(set_normal->Length2() > 0.0f);
- }
- /***********************************************************************************************
- * eval_A0_point -- contact point parameters for A0xEx *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline void eval_A0_point(const BTCollisionStruct & context,float * x,int edge)
- {
- float yval,den;
- Vector3 DxE;
-
- x[1] = -eval_side(context.AE[2][edge],context.Side) * context.Box.Extent[1];
- x[2] = eval_side(context.AE[1][edge],context.Side) * context.Box.Extent[2];
- if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
- den = Vector3::Dot_Product(context.N,context.AxE[0][edge]);
- if (WWMath::Fabs(den) > 0.0f) {
- Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
- x[0] = Vector3::Dot_Product(context.N,DxE);
- if (edge == 0) {
- x[0] -= context.N.Length2() * yval;
- } else {
- x[0] += context.N.Length2() * yval;
- }
- x[0] -= Vector3::Dot_Product(context.N,context.AxE[1][edge]) * x[1];
- x[0] -= Vector3::Dot_Product(context.N,context.AxE[2][edge]) * x[2];
- x[0] /= den;
- } else {
- x[0] = 0.0f;
- }
- }
- /***********************************************************************************************
- * eval_A1_point -- contact point parameters for A1xEx *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline void eval_A1_point(const BTCollisionStruct & context,float * x,int edge)
- {
- float yval,den;
- Vector3 DxE;
- x[0] = eval_side(context.AE[2][edge],context.Side) * context.Box.Extent[0];
- x[2] = -eval_side(context.AE[0][edge],context.Side) * context.Box.Extent[2];
- if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
-
- den = Vector3::Dot_Product(context.N,context.AxE[1][edge]);
- if (WWMath::Fabs(den) > 0.0f) {
- Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
- x[1] = Vector3::Dot_Product(context.N,DxE);
- if (edge == 0) {
- x[1] -= context.N.Length2() * yval;
- } else {
- x[1] += context.N.Length2() * yval;
- }
- x[1] -= Vector3::Dot_Product(context.N,context.AxE[0][edge]) * x[0];
- x[1] -= Vector3::Dot_Product(context.N,context.AxE[2][edge]) * x[2];
- x[1] /= den;
-
- } else {
- x[1] = 0.0f;
-
- }
- }
- /***********************************************************************************************
- * eval_A2_point -- contact point parameters for A2xEx *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline void eval_A2_point(const BTCollisionStruct & context,float * x,int edge)
- {
- float yval,den;
- Vector3 DxE;
- x[0] = -eval_side(context.AE[1][edge],context.Side) * context.Box.Extent[0];
- x[1] = eval_side(context.AE[0][edge],context.Side) * context.Box.Extent[1];
- if (context.Point == 0) { yval = 0.0f; } else { yval = 1.0f; }
-
- den = Vector3::Dot_Product(context.N,context.AxE[2][edge]);
- if (WWMath::Fabs(den) > 0.0f) {
- Vector3::Cross_Product(context.FinalD,context.E[edge],&DxE);
- x[2] = Vector3::Dot_Product(context.N,DxE);
- if (edge == 0) {
- x[2] -= context.N.Length2() * yval;
- } else {
- x[2] += context.N.Length2() * yval;
- }
- x[2] -= Vector3::Dot_Product(context.N,context.AxE[0][edge]) * x[0];
- x[2] -= Vector3::Dot_Product(context.N,context.AxE[1][edge]) * x[1];
- x[2] /= den;
-
- } else {
- x[2] = 0.0f;
-
- }
- }
- /***********************************************************************************************
- * obbtri_compute_contact_point -- compute the contact point *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- static inline void obbtri_compute_contact_point
- (
- BTCollisionStruct & context,
- CastResultStruct * result
- )
- {
- int i;
- float x[3];
- if (context.AxisId >= AXIS_A0E0) {
- Vector3 newc = context.Box.Center + result->Fraction * context.BoxMove;
- Vector3 newv0 = *context.Tri.V[0] + result->Fraction * context.TriMove;
- context.FinalD = newv0 - newc;
- }
- switch (context.AxisId)
- {
-
- case INTERSECTION:
- WWASSERT(0);
- return;
- case AXIS_N: // part of the box is touching the face of the triangle
- for (i = 0; i < 3; i++) {
- x[i] = eval_side(context.AN[i],context.Side) * context.Box.Extent[i];
- }
- break;
-
- case AXIS_A0: // part of the triangle is touching a face of the box
- case AXIS_A1:
- case AXIS_A2:
-
- if (context.Point == 0) {
- result->ContactPoint = *context.Tri.V[0];
- } else if (context.Point == 1) {
- result->ContactPoint = *context.Tri.V[1];
- } else {
- result->ContactPoint = *context.Tri.V[2];
- }
- Vector3::Add(result->ContactPoint,result->Fraction * context.TriMove,&(result->ContactPoint));
- return;
- case AXIS_A0E0: // one of the x-aligned edges of the box is contacting edge0
- eval_A0_point(context,x,0);
- break;
-
- case AXIS_A0E1: // one of the x-aligned edges of the box is contacting edge1
- eval_A0_point(context,x,1);
- break;
- case AXIS_A0E2: // one of the x-aligned edges of the box is contacting edge2
- eval_A0_point(context,x,2);
- break;
- case AXIS_A1E0: // one of the y-aligned edges of the box is contacting edge0
- eval_A1_point(context,x,0);
- break;
- case AXIS_A1E1: // one of the y-aligned edges of the box is contacting edge1
- eval_A1_point(context,x,1);
- break;
- case AXIS_A1E2: // one of the y-aligned edges of the box is contacting edge2
- eval_A1_point(context,x,2);
- break;
- case AXIS_A2E0: // one of the z-aligned edges of the box is contacting edge0
- eval_A2_point(context,x,0);
- break;
- case AXIS_A2E1: // one of the z-aligned edges of the box is contacting edge1
- eval_A2_point(context,x,1);
- break;
-
- case AXIS_A2E2: // one of the z-aligned edges of the box is contacting edge3
- eval_A2_point(context,x,2);
- break;
- }
- /*
- ** In certain circumstances, the x's computed above are outside the size of
- ** the box. I have tracked at least one of these cases to a situation where the
- ** separating axis is the triangle normal but one of the AxE axes also lines up
- ** with the normal. In this case, the Normal was 0,0,25 and the A0xE0 axis
- ** was 0,0,5. When the fraction was calculated for both of these, they were
- ** the same up to the six decimal places that MSVC will show me in the debugger.
- ** However, the fraction computed for the 0,0,5 axis was larger by some very small
- ** amount. This causes it to be used as the "separating axis". Looks like floating
- ** point roundoff error to me. The problem is that since the axis was perpendicular
- ** to the triangle, the "nearest-point" logic chose V0 which resulted in the
- ** calculation for x[0] being way off. The equations for finding the contact point
- ** in the AxE axis case assume that the axis is not normal to the triangle since
- ** that should be handled in the simpler routine for the N separating axis...
- ** Since I am at a loss for how to deal with this problem, I'm going to clamp all
- ** of the x's to be within the box here...
- */
- if (x[0] > context.Box.Extent.X) {
- x[0] = context.Box.Extent.X;
- } else if (x[0] < -context.Box.Extent.X) {
- x[0] = -context.Box.Extent.X;
- }
- if (x[1] > context.Box.Extent.Y) {
- x[1] = context.Box.Extent.Y;
- } else if (x[1] < -context.Box.Extent.Y) {
- x[1] = -context.Box.Extent.Y;
- }
-
- if (x[2] > context.Box.Extent.Z) {
- x[2] = context.Box.Extent.Z;
- } else if (x[2] < -context.Box.Extent.Z) {
- x[2] = -context.Box.Extent.Z;
- }
- /*
- ** Now, compute the point
- */
- result->ContactPoint.X = context.Box.Center.X +
- x[0]*context.A[0].X +
- x[1]*context.A[1].X +
- x[2]*context.A[2].X;
- result->ContactPoint.Y = context.Box.Center.Y +
- x[0]*context.A[0].Y +
- x[1]*context.A[1].Y +
- x[2]*context.A[2].Y;
- result->ContactPoint.Z = context.Box.Center.Z +
- x[0]*context.A[0].Z +
- x[1]*context.A[1].Z +
- x[2]*context.A[2].Z;
-
- Vector3::Add(result->ContactPoint,result->Fraction * context.BoxMove,&(result->ContactPoint));
- }
- /***********************************************************************************************
- * CollisionMath::Collide -- collide an obbox into a triangle *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 4/8/99 GTH : Created. *
- *=============================================================================================*/
- bool CollisionMath::Collide
- (
- const OBBoxClass & box,
- const Vector3 & move,
- const TriClass & tri,
- const Vector3 & trimove,
- CastResultStruct * result
- )
- {
- TRACK_COLLISION_OBBOX_TRI;
- float dp,leb0;
- BTCollisionStruct context(box,move,tri,trimove);
- /*
- ** AXIS_N
- */
- context.TestAxis = context.N;
- context.TestAxisId = AXIS_N;
- context.AN[0] = Vector3::Dot_Product(context.A[0],context.N);
- context.AN[1] = Vector3::Dot_Product(context.A[1],context.N);
- context.AN[2] = Vector3::Dot_Product(context.A[2],context.N);
- if (obbtri_check_collision_normal_axis(context)) goto exit;
- /*
- ** AXIS_A0
- */
- context.TestAxis = context.A[0];
- context.TestAxisId = AXIS_A0;
- context.AE[0][0] = Vector3::Dot_Product(context.A[0],context.E[0]);
- context.AE[0][1] = Vector3::Dot_Product(context.A[0],context.E[1]);
- if (obbtri_check_collision_basis_axis(context,box.Extent.X,context.AE[0][0],context.AE[0][1])) goto exit;
- /*
- ** AXIS_A1
- */
- context.TestAxis = context.A[1];
- context.TestAxisId = AXIS_A1;
- context.AE[1][0] = Vector3::Dot_Product(context.A[1],context.E[0]);
- context.AE[1][1] = Vector3::Dot_Product(context.A[1],context.E[1]);
- if (obbtri_check_collision_basis_axis(context,box.Extent.Y,context.AE[1][0],context.AE[1][1])) goto exit;
- /*
- ** AXIS_A2
- */
- context.TestAxis = context.A[2];
- context.TestAxisId = AXIS_A2;
- context.AE[2][0] = Vector3::Dot_Product(context.A[2],context.E[0]);
- context.AE[2][1] = Vector3::Dot_Product(context.A[2],context.E[1]);
- if (obbtri_check_collision_basis_axis(context,box.Extent.Z,context.AE[2][0],context.AE[2][1])) goto exit;
- /*
- ** AXIS_A0xE0
- */
- Vector3::Cross_Product(context.A[0],context.E[0],&context.AxE[0][0]);
- context.TestAxis = context.AxE[0][0];
- context.TestAxisId = AXIS_A0E0;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[1][0]);
- if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
- }
- /*
- ** AXIS_A0xE1
- */
- Vector3::Cross_Product(context.A[0],context.E[1],&context.AxE[0][1]);
- context.TestAxis = context.AxE[0][1];
- context.TestAxisId = AXIS_A0E1;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[1][1]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
- /*
- ** AXIS_A0xE2
- */
- Vector3::Cross_Product(context.A[0],context.E[2],&context.AxE[0][2]);
- context.TestAxis = context.AxE[0][2];
- context.TestAxisId = AXIS_A0E2;
- context.AE[1][2] = Vector3::Dot_Product(context.A[1],context.E[2]);
- context.AE[2][2] = Vector3::Dot_Product(context.A[2],context.E[2]);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[1][2]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
- /*
- ** AXIS_A1xE0
- */
- Vector3::Cross_Product(context.A[1],context.E[0],&context.AxE[1][0]);
- context.TestAxis = context.AxE[1][0];
- context.TestAxisId = AXIS_A1E0;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[0][0]);
- if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
- }
- /*
- ** AXIS_A1xE1
- */
- Vector3::Cross_Product(context.A[1],context.E[1],&context.AxE[1][1]);
- context.TestAxis = context.AxE[1][1];
- context.TestAxisId = AXIS_A1E1;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[0][1]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
- /*
- ** AXIS_A1xE2
- */
- Vector3::Cross_Product(context.A[1],context.E[2],&context.AxE[1][2]);
- context.TestAxis = context.AxE[1][2];
- context.TestAxisId = AXIS_A1E2;
- context.AE[0][2] = Vector3::Dot_Product(context.A[0],context.E[2]);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[0][2]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
- /*
- ** AXIS_A2xE0
- */
- Vector3::Cross_Product(context.A[2],context.E[0],&context.AxE[2][0]);
- context.TestAxis = context.AxE[2][0];
- context.TestAxisId = AXIS_A2E0;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][0]) + box.Extent[1]*WWMath::Fabs(context.AE[0][0]);
- if (obbtri_check_collision_cross_axis(context,dp,2,leb0)) goto exit;
- }
- /*
- ** AXIS_A2xE1
- */
- Vector3::Cross_Product(context.A[2],context.E[1],&context.AxE[2][1]);
- context.TestAxis = context.AxE[2][1];
- context.TestAxisId = AXIS_A2E1;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][1]) + box.Extent[1]*WWMath::Fabs(context.AE[0][1]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
-
- /*
- ** AXIS_A2xE2
- */
- Vector3::Cross_Product(context.A[2],context.E[2],&context.AxE[2][2]);
- context.TestAxis = context.AxE[2][2];
- context.TestAxisId = AXIS_A2E2;
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][2]) + box.Extent[1]*WWMath::Fabs(context.AE[0][2]);
- if (obbtri_check_collision_cross_axis(context,dp,1,leb0)) goto exit;
- }
- /*
- ** Last ditch effort, check an axis based on the move vector
- */
- if (!context.StartBad) {
- context.TestPoint = context.Point;
- context.TestAxisId = context.AxisId;
- Vector3::Cross_Product(context.Move,context.A[0],&context.TestAxis);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- if (obbtri_check_collision_axis(context)) goto exit;
- }
- Vector3::Cross_Product(context.Move,context.A[1],&context.TestAxis);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- if (obbtri_check_collision_axis(context)) goto exit;
- }
- Vector3::Cross_Product(context.Move,context.A[2],&context.TestAxis);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- if (obbtri_check_collision_axis(context)) goto exit;
- }
- }
- exit:
- #pragma message ("(gth) disabling an assert in obb->tri collision, investigate later\n")
- #if 0
- WWASSERT((context.AxisId != INTERSECTION) || (context.StartBad));
- #else
- if (context.AxisId == INTERSECTION) {
- context.StartBad = true;
- }
- #endif
- /*
- ** If the triangle and box are intersecting before the move, return that
- ** result.
- */
- if (context.StartBad) {
- result->StartBad = true;
- result->Fraction = 0.0f;
- result->Normal = *tri.N;
- TRACK_COLLISION_OBBOX_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 (context.MaxFrac < 0.0f) {
- context.MaxFrac = 0.0f;
- }
- if ((context.MaxFrac < 1.0f) && (context.MaxFrac <= result->Fraction)) {
- Vector3 normal;
- obbtri_compute_contact_normal(context,&normal);
- if ( (WWMath::Fabs(context.MaxFrac - result->Fraction) > WWMATH_EPSILON) ||
- (Vector3::Dot_Product(normal,move) < Vector3::Dot_Product(result->Normal,move)) )
- {
- result->Normal = normal; //obbtri_compute_contact_normal(context,result);
- }
-
- result->Fraction = context.MaxFrac;
- if (result->ComputeContactPoint) {
- obbtri_compute_contact_point(context,result);
- }
- TRACK_COLLISION_OBBOX_TRI_HIT;
- return true;
- }
-
- return false;
- }
- /***********************************************************************************************
-
- OBBox-Triangle Intersection
- The following code implements a simple boolean intersection check. It uses the same
- algorithm as the collision function but can avoid some of the calculations. For a very
- simple implementation of this algorithm, see Oriented_Box_Intersects_Tri in obbox.h
- ***********************************************************************************************/
- /**
- ** BTIntersectStruct
- ** Scratchpad variables for the OBBox-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 BTIntersectStruct
- {
- BTIntersectStruct(const OBBoxClass &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]);
- A[0].Set(box.Basis[0][0],box.Basis[1][0],box.Basis[2][0]);
- A[1].Set(box.Basis[0][1],box.Basis[1][1],box.Basis[2][1]);
- A[2].Set(box.Basis[0][2],box.Basis[1][2],box.Basis[2][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 A[3]; // basis vectors for the box
- Vector3 E[3]; // edge vectors for the triangle
- Vector3 N; // normal (NOT normalized!!!)
- Vector3 TestAxis; // separating axis currently being tested
- const OBBoxClass & Box;
- const TriClass & Tri;
- private:
-
- // not implemented
- BTIntersectStruct(const BTIntersectStruct &);
- BTIntersectStruct & operator = (const BTIntersectStruct &);
- };
- /***********************************************************************************************
- * obbtri_intersection_separation_test -- test the projected extents for intersection *
- * *
- * Once the extents are projected onto the axis, this function contains *
- * the logic that determines whether the box and triangle intersect. *
- * *
- * INPUT: *
- * context - the BTIntersectStruct containing the data for this intersection test *
- * lp - the leading edge of the polygon projected onto the axis *
- * leb0 - the leading edge of the box projected onto the axis *
- * *
- * OUTPUT: *
- * true = objects are separated *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 2/3/2000 gth : Created. *
- *=============================================================================================*/
- static inline bool obbtri_intersection_separation_test
- (
- BTIntersectStruct & context,
- float lp,
- float leb0
- )
- {
- /*
- ** Only compute the normalized epsilon if we need to.
- ** - compute 'EPSILON' normalized to the length of the axis
- ** - If (I'm no more than 'EPSILON' embedded in the polygon) then the box and tri are separated
- */
- float eps = 0.0f;
- if (lp - leb0 <= 0.0f) {
- eps = COLLISION_EPSILON * context.TestAxis.Length(); // trying to only compute epsilon if I have to
- }
- return (lp - leb0 > -eps);
- }
- /***********************************************************************************************
- * obbtri_check_intersection_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: *
- * true = the objects are SEPARATED *
- * *
- * HISTORY: *
- * 5/4/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_intersection_cross_axis
- (
- BTIntersectStruct & context,
- float dp,
- float leb0
- )
- {
- float p0; // distance from box center to vertex 0
- float lp; // leading edge of the polygon.
- p0 = Vector3::Dot_Product(context.D,context.TestAxis);
- // I want the axis centered at the box, pointing towards the triangle
- if (p0 < 0) {
- context.TestAxis = -context.TestAxis;
- p0 = -p0;
- 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 obbtri_intersection_separation_test(context,lp,leb0);
- }
- /***********************************************************************************************
- * obbtri_check_intersection_basis_axis -- intersection check for a basis axis *
- * *
- * axis being checked is one of the basis vectors for the box *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * true = the objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 5/4/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_intersection_basis_axis
- (
- BTIntersectStruct & context,
- float leb0,
- float dp1,
- float dp2
- )
- {
- float dist; // separation along the axis
- float lp; // leading edge of the polygon.
- dist = Vector3::Dot_Product(context.D,context.TestAxis);
- // we want the axis centered at the box, pointing towards the triangle
- if (dist < 0) {
- context.TestAxis = -context.TestAxis;
- dist = -dist;
- 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 obbtri_intersection_separation_test(context,lp,leb0);
- }
- /***********************************************************************************************
- * obbtri_check_intersection_normal_axis -- intersection check for the triangle normal *
- * *
- * axis being checked is the triangle's normal *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * true = the objects are SEPARATED *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 5/4/99 GTH : Created. *
- *=============================================================================================*/
- static inline bool obbtri_check_intersection_normal_axis
- (
- BTIntersectStruct & context
- )
- {
- 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(context.D,context.TestAxis);
- // we want the axis centered at the box, pointing towards the triangle
- if (dist < 0) {
- context.TestAxis = -context.TestAxis;
- dist = -dist;
- }
- leb0 = context.Box.Extent.X * WWMath::Fabs(context.AN[0]) +
- context.Box.Extent.Y * WWMath::Fabs(context.AN[1]) +
- context.Box.Extent.Z * WWMath::Fabs(context.AN[2]);
- lp = dist; // this is the "optimization", don't have to find lp
- return obbtri_intersection_separation_test(context,lp,leb0);
- }
- /***********************************************************************************************
- * CollisionMath::Intersection_Test -- Intersection check for an OBBox and a triangle *
- * *
- * INPUT: *
- * box - obbox to be tested *
- * tri - triangle to be tested *
- * *
- * OUTPUT: *
- * true = objects INTERSECT *
- * *
- * WARNINGS: *
- * note that the other inline functions are "quick-reject" functions which return true when *
- * the objects are separated. *
- * *
- * HISTORY: *
- * 5/4/99 GTH : Created. *
- *=============================================================================================*/
- bool CollisionMath::Intersection_Test(const OBBoxClass & box,const TriClass & tri)
- {
- float dp,leb0;
- BTIntersectStruct context(box,tri);
- /*
- ** AXIS_N
- */
- context.TestAxis = context.N;
- context.AN[0] = Vector3::Dot_Product(context.A[0],context.N);
- context.AN[1] = Vector3::Dot_Product(context.A[1],context.N);
- context.AN[2] = Vector3::Dot_Product(context.A[2],context.N);
- if (obbtri_check_intersection_normal_axis(context)) return false;
- /*
- ** AXIS_A0
- */
- context.TestAxis = context.A[0];
- context.AE[0][0] = Vector3::Dot_Product(context.A[0],context.E[0]);
- context.AE[0][1] = Vector3::Dot_Product(context.A[0],context.E[1]);
- if (obbtri_check_intersection_basis_axis(context,box.Extent.X,context.AE[0][0],context.AE[0][1])) return false;
- /*
- ** AXIS_A1
- */
- context.TestAxis = context.A[1];
- context.AE[1][0] = Vector3::Dot_Product(context.A[1],context.E[0]);
- context.AE[1][1] = Vector3::Dot_Product(context.A[1],context.E[1]);
- if (obbtri_check_intersection_basis_axis(context,box.Extent.Y,context.AE[1][0],context.AE[1][1])) return false;
- /*
- ** AXIS_A2
- */
- context.TestAxis = context.A[2];
- context.AE[2][0] = Vector3::Dot_Product(context.A[2],context.E[0]);
- context.AE[2][1] = Vector3::Dot_Product(context.A[2],context.E[1]);
- if (obbtri_check_intersection_basis_axis(context,box.Extent.Z,context.AE[2][0],context.AE[2][1])) return false;
- /*
- ** AXIS_A0xE0
- */
- Vector3::Cross_Product(context.A[0],context.E[0],&context.AxE[0][0]);
- context.TestAxis = context.AxE[0][0];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[1][0]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A0xE1
- */
- Vector3::Cross_Product(context.A[0],context.E[1],&context.AxE[0][1]);
- context.TestAxis = context.AxE[0][1];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[1][1]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A0xE2
- */
- Vector3::Cross_Product(context.A[0],context.E[2],&context.AxE[0][2]);
- context.TestAxis = context.AxE[0][2];
- context.AE[1][2] = Vector3::Dot_Product(context.A[1],context.E[2]);
- context.AE[2][2] = Vector3::Dot_Product(context.A[2],context.E[2]);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[0];
- leb0 = box.Extent[1]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[1][2]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A1xE0
- */
- Vector3::Cross_Product(context.A[1],context.E[0],&context.AxE[1][0]);
- context.TestAxis = context.AxE[1][0];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][0]) + box.Extent[2]*WWMath::Fabs(context.AE[0][0]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A1xE1
- */
- Vector3::Cross_Product(context.A[1],context.E[1],&context.AxE[1][1]);
- context.TestAxis = context.AxE[1][1];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][1]) + box.Extent[2]*WWMath::Fabs(context.AE[0][1]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A1xE2
- */
- Vector3::Cross_Product(context.A[1],context.E[2],&context.AxE[1][2]);
- context.TestAxis = context.AxE[1][2];
- context.AE[0][2] = Vector3::Dot_Product(context.A[0],context.E[2]);
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[1];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[2][2]) + box.Extent[2]*WWMath::Fabs(context.AE[0][2]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A2xE0
- */
- Vector3::Cross_Product(context.A[2],context.E[0],&context.AxE[2][0]);
- context.TestAxis = context.AxE[2][0];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][0]) + box.Extent[1]*WWMath::Fabs(context.AE[0][0]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- /*
- ** AXIS_A2xE1
- */
- Vector3::Cross_Product(context.A[2],context.E[1],&context.AxE[2][1]);
- context.TestAxis = context.AxE[2][1];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][1]) + box.Extent[1]*WWMath::Fabs(context.AE[0][1]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
-
- /*
- ** AXIS_A2xE2
- */
- Vector3::Cross_Product(context.A[2],context.E[2],&context.AxE[2][2]);
- context.TestAxis = context.AxE[2][2];
- if (context.TestAxis.Length2() > AXISLEN_EPSILON2) {
- dp = -context.AN[2];
- leb0 = box.Extent[0]*WWMath::Fabs(context.AE[1][2]) + box.Extent[1]*WWMath::Fabs(context.AE[0][2]);
- if (obbtri_check_intersection_cross_axis(context,dp,leb0)) return false;
- }
- return true;
- }
|