1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition Source Code 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.
- Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
- ===========================================================================
- */
- /*
- ===============================================================================
- Trace model vs. polygonal model collision detection.
- ===============================================================================
- */
- #pragma hdrstop
- #include "../idlib/precompiled.h"
- #include "CollisionModel_local.h"
- /*
- ===============================================================================
- Collision detection for rotational motion
- ===============================================================================
- */
- // epsilon for round-off errors in epsilon calculations
- #define CM_PL_RANGE_EPSILON 1e-4f
- // if the collision point is this close to the rotation axis it is not considered a collision
- #define ROTATION_AXIS_EPSILON (CM_CLIP_EPSILON*0.25f)
- /*
- ================
- CM_RotatePoint
- rotates a point about an arbitrary axis using the tangent of half the rotation angle
- ================
- */
- void CM_RotatePoint( idVec3 &point, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) {
- double d, t, s, c;
- idVec3 proj, v1, v2;
- point -= origin;
- proj = axis * ( point * axis );
- v1 = point - proj;
- v2 = axis.Cross( v1 );
- // r = tan( a / 2 );
- // sin(a) = 2*r/(1+r*r);
- // cos(a) = (1-r*r)/(1+r*r);
- t = tanHalfAngle * tanHalfAngle;
- d = 1.0f / ( 1.0f + t );
- s = 2.0f * tanHalfAngle * d;
- c = ( 1.0f - t ) * d;
- point = v1 * c - v2 * s + proj + origin;
- }
- /*
- ================
- CM_RotateEdge
- rotates an edge about an arbitrary axis using the tangent of half the rotation angle
- ================
- */
- void CM_RotateEdge( idVec3 &start, idVec3 &end, const idVec3 &origin, const idVec3 &axis, const float tanHalfAngle ) {
- double d, t, s, c;
- idVec3 proj, v1, v2;
- // r = tan( a / 2 );
- // sin(a) = 2*r/(1+r*r);
- // cos(a) = (1-r*r)/(1+r*r);
- t = tanHalfAngle * tanHalfAngle;
- d = 1.0f / ( 1.0f + t );
- s = 2.0f * tanHalfAngle * d;
- c = ( 1.0f - t ) * d;
- start -= origin;
- proj = axis * ( start * axis );
- v1 = start - proj;
- v2 = axis.Cross( v1 );
- start = v1 * c - v2 * s + proj + origin;
- end -= origin;
- proj = axis * ( end * axis );
- v1 = end - proj;
- v2 = axis.Cross( v1 );
- end = v1 * c - v2 * s + proj + origin;
- }
- /*
- ================
- idCollisionModelManagerLocal::CollisionBetweenEdgeBounds
- verifies if the collision of two edges occurs between the edge bounds
- also calculates the collision point and collision plane normal if the collision occurs between the bounds
- ================
- */
- int idCollisionModelManagerLocal::CollisionBetweenEdgeBounds( cm_traceWork_t *tw, const idVec3 &va, const idVec3 &vb,
- const idVec3 &vc, const idVec3 &vd, float tanHalfAngle,
- idVec3 &collisionPoint, idVec3 &collisionNormal ) {
- float d1, d2, d;
- idVec3 at, bt, dir, dir1, dir2;
- idPluecker pl1, pl2;
- at = va;
- bt = vb;
- if ( tanHalfAngle != 0.0f ) {
- CM_RotateEdge( at, bt, tw->origin, tw->axis, tanHalfAngle );
- }
- dir1 = (at - tw->origin).Cross( tw->axis );
- dir2 = (bt - tw->origin).Cross( tw->axis );
- if ( dir1 * dir1 > dir2 * dir2 ) {
- dir = dir1;
- }
- else {
- dir = dir2;
- }
- if ( tw->angle < 0.0f ) {
- dir = -dir;
- }
- pl1.FromLine( at, bt );
- pl2.FromRay( vc, dir );
- d1 = pl1.PermutedInnerProduct( pl2 );
- pl2.FromRay( vd, dir );
- d2 = pl1.PermutedInnerProduct( pl2 );
- if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) {
- return false;
- }
- pl1.FromLine( vc, vd );
- pl2.FromRay( at, dir );
- d1 = pl1.PermutedInnerProduct( pl2 );
- pl2.FromRay( bt, dir );
- d2 = pl1.PermutedInnerProduct( pl2 );
- if ( ( d1 > 0.0f && d2 > 0.0f ) || ( d1 < 0.0f && d2 < 0.0f ) ) {
- return false;
- }
- // collision point on the edge at-bt
- dir1 = (vd - vc).Cross( dir );
- d = dir1 * vc;
- d1 = dir1 * at - d;
- d2 = dir1 * bt - d;
- if ( d1 == d2 ) {
- return false;
- }
- collisionPoint = at + ( d1 / (d1 - d2) ) * ( bt - at );
- // normal is cross product of the rotated edge va-vb and the edge vc-vd
- collisionNormal.Cross( bt-at, vd-vc );
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::RotateEdgeThroughEdge
- calculates the tangent of half the rotation angle at which the edges collide
- ================
- */
- int idCollisionModelManagerLocal::RotateEdgeThroughEdge( cm_traceWork_t *tw, const idPluecker &pl1,
- const idVec3 &vc, const idVec3 &vd,
- const float minTan, float &tanHalfAngle ) {
- double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
- idVec3 ct, dt;
- idPluecker pl2;
- /*
- a = start of line being rotated
- b = end of line being rotated
- pl1 = pluecker coordinate for line (a - b)
- pl2 = pluecker coordinate for edge we might collide with (c - d)
- t = rotation angle around the z-axis
- solve pluecker inner product for t of rotating line a-b and line l2
- // start point of rotated line during rotation
- an[0] = a[0] * cos(t) + a[1] * sin(t)
- an[1] = a[0] * -sin(t) + a[1] * cos(t)
- an[2] = a[2];
- // end point of rotated line during rotation
- bn[0] = b[0] * cos(t) + b[1] * sin(t)
- bn[1] = b[0] * -sin(t) + b[1] * cos(t)
- bn[2] = b[2];
- pl1[0] = a[0] * b[1] - b[0] * a[1];
- pl1[1] = a[0] * b[2] - b[0] * a[2];
- pl1[2] = a[0] - b[0];
- pl1[3] = a[1] * b[2] - b[1] * a[2];
- pl1[4] = a[2] - b[2];
- pl1[5] = b[1] - a[1];
- v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t));
- v[1] = (a[0] * cos(t) + a[1] * sin(t)) * b[2] - (b[0] * cos(t) + b[1] * sin(t)) * a[2];
- v[2] = (a[0] * cos(t) + a[1] * sin(t)) - (b[0] * cos(t) + b[1] * sin(t));
- v[3] = (a[0] * -sin(t) + a[1] * cos(t)) * b[2] - (b[0] * -sin(t) + b[1] * cos(t)) * a[2];
- v[4] = a[2] - b[2];
- v[5] = (b[0] * -sin(t) + b[1] * cos(t)) - (a[0] * -sin(t) + a[1] * cos(t));
- pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0;
- v[0] = (a[0] * cos(t) + a[1] * sin(t)) * (b[0] * -sin(t) + b[1] * cos(t)) - (b[0] * cos(t) + b[1] * sin(t)) * (a[0] * -sin(t) + a[1] * cos(t));
- v[0] = (a[1] * b[1] - a[0] * b[0]) * cos(t) * sin(t) + (a[0] * b[1] + a[1] * b[0] * cos(t)^2) - (a[1] * b[0]) - ((b[1] * a[1] - b[0] * a[0]) * cos(t) * sin(t) + (b[0] * a[1] + b[1] * a[0]) * cos(t)^2 - (b[1] * a[0]))
- v[0] = - (a[1] * b[0]) - ( - (b[1] * a[0]))
- v[0] = (b[1] * a[0]) - (a[1] * b[0])
- v[0] = (a[0]*b[1]) - (a[1]*b[0]);
- v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t);
- v[2] = (a[0]-b[0]) * cos(t) + (a[1]-b[1]) * sin(t);
- v[3] = (b[0]*a[2] - a[0]*b[2]) * sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t);
- v[4] = a[2] - b[2];
- v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t);
- v[0] = (a[0]*b[1]) - (a[1]*b[0]);
- v[1] = (a[0]*b[2] - b[0]*a[2]) * cos(t) + (a[1]*b[2] - b[1]*a[2]) * sin(t);
- v[2] = (a[0]-b[0]) * cos(t) - (b[1]-a[1]) * sin(t);
- v[3] = (a[0]*b[2] - b[0]*a[2]) * -sin(t) + (a[1]*b[2] - b[1]*a[2]) * cos(t);
- v[4] = a[2] - b[2];
- v[5] = (a[0]-b[0]) * sin(t) + (b[1]-a[1]) * cos(t);
- v[0] = pl1[0];
- v[1] = pl1[1] * cos(t) + pl1[3] * sin(t);
- v[2] = pl1[2] * cos(t) - pl1[5] * sin(t);
- v[3] = pl1[3] * cos(t) - pl1[1] * sin(t);
- v[4] = pl1[4];
- v[5] = pl1[5] * cos(t) + pl1[2] * sin(t);
- pl2[0] * v[4] + pl2[1] * v[5] + pl2[2] * v[3] + pl2[4] * v[0] + pl2[5] * v[1] + pl2[3] * v[2] = 0;
- 0 = pl2[0] * pl1[4] +
- pl2[1] * (pl1[5] * cos(t) + pl1[2] * sin(t)) +
- pl2[2] * (pl1[3] * cos(t) - pl1[1] * sin(t)) +
- pl2[4] * pl1[0] +
- pl2[5] * (pl1[1] * cos(t) + pl1[3] * sin(t)) +
- pl2[3] * (pl1[2] * cos(t) - pl1[5] * sin(t));
- v2 * cos(t) + v1 * sin(t) + v0 = 0;
- // rotation about the z-axis
- v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
- v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
- v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
- // rotation about the x-axis
- //v0 = pl2[3] * pl1[2] + pl2[2] * pl1[3];
- //v1 = -pl2[5] * pl1[0] + pl2[4] * pl1[1] - pl2[1] * pl1[4] + pl2[0] * pl1[5];
- //v2 = pl2[4] * pl1[0] + pl2[5] * pl1[1] + pl2[0] * pl1[4] + pl2[1] * pl1[5];
- r = tan(t / 2);
- sin(t) = 2*r/(1+r*r);
- cos(t) = (1-r*r)/(1+r*r);
- v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0
- (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0
- (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0
- v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r)
- v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r
- (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0;
- MrE gives Pluecker a banana.. good monkey
- */
- tanHalfAngle = tw->maxTan;
- // transform rotation axis to z-axis
- ct = (vc - tw->origin) * tw->matrix;
- dt = (vd - tw->origin) * tw->matrix;
- pl2.FromLine( ct, dt );
- v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
- v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
- v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
- a = v0 - v2;
- b = v1;
- c = v0 + v2;
- if ( a == 0.0f ) {
- if ( b == 0.0f ) {
- return false;
- }
- frac1 = -c / ( 2.0f * b );
- frac2 = 1e10; // = tan( idMath::HALF_PI )
- }
- else {
- d = b * b - c * a;
- if ( d <= 0.0f ) {
- return false;
- }
- sqrtd = sqrt( d );
- if ( b > 0.0f ) {
- q = - b + sqrtd;
- }
- else {
- q = - b - sqrtd;
- }
- frac1 = q / a;
- frac2 = c / q;
- }
- if ( tw->angle < 0.0f ) {
- frac1 = -frac1;
- frac2 = -frac2;
- }
- // get smallest tangent for which a collision occurs
- if ( frac1 >= minTan && frac1 < tanHalfAngle ) {
- tanHalfAngle = frac1;
- }
- if ( frac2 >= minTan && frac2 < tanHalfAngle ) {
- tanHalfAngle = frac2;
- }
- if ( tw->angle < 0.0f ) {
- tanHalfAngle = -tanHalfAngle;
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::EdgeFurthestFromEdge
- calculates the direction of motion at the initial position, where dir < 0 means the edges move towards each other
- if the edges move away from each other the tangent of half the rotation angle at which
- the edges are furthest apart is also calculated
- ================
- */
- int idCollisionModelManagerLocal::EdgeFurthestFromEdge( cm_traceWork_t *tw, const idPluecker &pl1,
- const idVec3 &vc, const idVec3 &vd,
- float &tanHalfAngle, float &dir ) {
- double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
- idVec3 ct, dt;
- idPluecker pl2;
- /*
- v2 * cos(t) + v1 * sin(t) + v0 = 0;
- // rotation about the z-axis
- v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
- v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
- v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
- derivative:
- v1 * cos(t) - v2 * sin(t) = 0;
- r = tan(t / 2);
- sin(t) = 2*r/(1+r*r);
- cos(t) = (1-r*r)/(1+r*r);
- -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r);
- -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0;
- -v2 * 2 * r + v1 * (1 - r*r) = 0;
- (-v1) * r * r + (-2 * v2) * r + (v1) = 0;
- */
- tanHalfAngle = 0.0f;
- // transform rotation axis to z-axis
- ct = (vc - tw->origin) * tw->matrix;
- dt = (vd - tw->origin) * tw->matrix;
- pl2.FromLine( ct, dt );
- v0 = pl2[0] * pl1[4] + pl2[4] * pl1[0];
- v1 = pl2[1] * pl1[2] - pl2[2] * pl1[1] + pl2[5] * pl1[3] - pl2[3] * pl1[5];
- v2 = pl2[1] * pl1[5] + pl2[2] * pl1[3] + pl2[5] * pl1[1] + pl2[3] * pl1[2];
- // get the direction of motion at the initial position
- c = v0 + v2;
- if ( tw->angle > 0.0f ) {
- if ( c > 0.0f ) {
- dir = v1;
- }
- else {
- dir = -v1;
- }
- }
- else {
- if ( c > 0.0f ) {
- dir = -v1;
- }
- else {
- dir = v1;
- }
- }
- // negative direction means the edges move towards each other at the initial position
- if ( dir <= 0.0f ) {
- return true;
- }
- a = -v1;
- b = -v2;
- c = v1;
- if ( a == 0.0f ) {
- if ( b == 0.0f ) {
- return false;
- }
- frac1 = -c / ( 2.0f * b );
- frac2 = 1e10; // = tan( idMath::HALF_PI )
- }
- else {
- d = b * b - c * a;
- if ( d <= 0.0f ) {
- return false;
- }
- sqrtd = sqrt( d );
- if ( b > 0.0f ) {
- q = - b + sqrtd;
- }
- else {
- q = - b - sqrtd;
- }
- frac1 = q / a;
- frac2 = c / q;
- }
- if ( tw->angle < 0.0f ) {
- frac1 = -frac1;
- frac2 = -frac2;
- }
- if ( frac1 < 0.0f && frac2 < 0.0f ) {
- return false;
- }
- if ( frac1 > frac2 ) {
- tanHalfAngle = frac1;
- }
- else {
- tanHalfAngle = frac2;
- }
- if ( tw->angle < 0.0f ) {
- tanHalfAngle = -tanHalfAngle;
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon
- ================
- */
- void idCollisionModelManagerLocal::RotateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ) {
- int i, j, edgeNum;
- float f1, f2, startTan, dir, tanHalfAngle;
- cm_edge_t *edge;
- cm_vertex_t *v1, *v2;
- idVec3 collisionPoint, collisionNormal, origin, epsDir;
- idPluecker epsPl;
- idBounds bounds;
- // if the trm is convex and the rotation axis intersects the trm
- if ( tw->isConvex && tw->axisIntersectsTrm ) {
- // if both points are behind the polygon the edge cannot collide within a 180 degrees rotation
- if ( tw->vertices[trmEdge->vertexNum[0]].polygonSide & tw->vertices[trmEdge->vertexNum[1]].polygonSide ) {
- return;
- }
- }
- // if the trace model edge rotation bounds do not intersect the polygon bounds
- if ( !trmEdge->rotationBounds.IntersectsBounds( poly->bounds ) ) {
- return;
- }
- // edge rotation bounds should cross polygon plane
- if ( trmEdge->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) {
- return;
- }
- // check edges for a collision
- for ( i = 0; i < poly->numEdges; i++ ) {
- edgeNum = poly->edges[i];
- edge = tw->model->edges + abs(edgeNum);
- // if this edge is already checked
- if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
- continue;
- }
- // can never collide with internal edges
- if ( edge->internal ) {
- continue;
- }
- v1 = tw->model->vertices + edge->vertexNum[INT32_SIGNBITSET( edgeNum )];
- v2 = tw->model->vertices + edge->vertexNum[INT32_SIGNBITNOTSET( edgeNum )];
- // edge bounds
- for ( j = 0; j < 3; j++ ) {
- if ( v1->p[j] > v2->p[j] ) {
- bounds[0][j] = v2->p[j];
- bounds[1][j] = v1->p[j];
- }
- else {
- bounds[0][j] = v1->p[j];
- bounds[1][j] = v2->p[j];
- }
- }
- // if the trace model edge rotation bounds do not intersect the polygon edge bounds
- if ( !trmEdge->rotationBounds.IntersectsBounds( bounds ) ) {
- continue;
- }
- f1 = trmEdge->pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] );
- // pluecker coordinate for epsilon expanded edge
- epsDir = edge->normal * (CM_CLIP_EPSILON+CM_PL_RANGE_EPSILON);
- epsPl.FromLine( tw->model->vertices[edge->vertexNum[0]].p + epsDir,
- tw->model->vertices[edge->vertexNum[1]].p + epsDir );
- f2 = trmEdge->pl.PermutedInnerProduct( epsPl );
- // if the rotating edge is inbetween the polygon edge and the epsilon expanded edge
- if ( ( f1 < 0.0f && f2 > 0.0f ) || ( f1 > 0.0f && f2 < 0.0f ) ) {
- if ( !EdgeFurthestFromEdge( tw, trmEdge->plzaxis, v1->p, v2->p, startTan, dir ) ) {
- continue;
- }
- if ( dir <= 0.0f ) {
- // moving towards the polygon edge so stop immediately
- tanHalfAngle = 0.0f;
- }
- else if ( idMath::Fabs( startTan ) >= tw->maxTan ) {
- // never going to get beyond the start tangent during the current rotation
- continue;
- }
- else {
- // collide with the epsilon expanded edge
- if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, idMath::Fabs( startTan ), tanHalfAngle ) ) {
- tanHalfAngle = startTan;
- }
- }
- }
- else {
- // collide with the epsilon expanded edge
- epsDir = edge->normal * CM_CLIP_EPSILON;
- if ( !RotateEdgeThroughEdge(tw, trmEdge->plzaxis, v1->p + epsDir, v2->p + epsDir, 0.0f, tanHalfAngle ) ) {
- continue;
- }
- }
- if ( idMath::Fabs( tanHalfAngle ) >= tw->maxTan ) {
- continue;
- }
- // check if the collision is between the edge bounds
- if ( !CollisionBetweenEdgeBounds( tw, trmEdge->start, trmEdge->end, v1->p, v2->p,
- tanHalfAngle, collisionPoint, collisionNormal ) ) {
- continue;
- }
- // allow rotation if the rotation axis goes through the collisionPoint
- origin = tw->origin + tw->axis * ( tw->axis * ( collisionPoint - tw->origin ) );
- if ( ( collisionPoint - origin ).LengthSqr() < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
- continue;
- }
- // fill in trace structure
- tw->maxTan = idMath::Fabs( tanHalfAngle );
- tw->trace.c.normal = collisionNormal;
- tw->trace.c.normal.Normalize();
- tw->trace.c.dist = tw->trace.c.normal * v1->p;
- // make sure the collision plane faces the trace model
- if ( (tw->trace.c.normal * trmEdge->start) - tw->trace.c.dist < 0 ) {
- tw->trace.c.normal = -tw->trace.c.normal;
- tw->trace.c.dist = -tw->trace.c.dist;
- }
- tw->trace.c.contents = poly->contents;
- tw->trace.c.material = poly->material;
- tw->trace.c.type = CONTACT_EDGE;
- tw->trace.c.modelFeature = edgeNum;
- tw->trace.c.trmFeature = trmEdge - tw->edges;
- tw->trace.c.point = collisionPoint;
- // if no collision can be closer
- if ( tw->maxTan == 0.0f ) {
- break;
- }
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::RotatePointThroughPlane
- calculates the tangent of half the rotation angle at which the point collides with the plane
- ================
- */
- int idCollisionModelManagerLocal::RotatePointThroughPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane,
- const float angle, const float minTan, float &tanHalfAngle ) {
- double v0, v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
- idVec3 p, normal;
- /*
- p[0] = point[0] * cos(t) + point[1] * sin(t)
- p[1] = point[0] * -sin(t) + point[1] * cos(t)
- p[2] = point[2];
- normal[0] * (p[0] * cos(t) + p[1] * sin(t)) +
- normal[1] * (p[0] * -sin(t) + p[1] * cos(t)) +
- normal[2] * p[2] + dist = 0
- normal[0] * p[0] * cos(t) + normal[0] * p[1] * sin(t) +
- -normal[1] * p[0] * sin(t) + normal[1] * p[1] * cos(t) +
- normal[2] * p[2] + dist = 0
- v2 * cos(t) + v1 * sin(t) + v0
- // rotation about the z-axis
- v0 = normal[2] * p[2] + dist
- v1 = normal[0] * p[1] - normal[1] * p[0]
- v2 = normal[0] * p[0] + normal[1] * p[1]
- r = tan(t / 2);
- sin(t) = 2*r/(1+r*r);
- cos(t) = (1-r*r)/(1+r*r);
- v1 * 2 * r / (1 + r*r) + v2 * (1 - r*r) / (1 + r*r) + v0 = 0
- (v1 * 2 * r + v2 * (1 - r*r)) / (1 + r*r) = -v0
- (v1 * 2 * r + v2 - v2 * r*r) / (1 + r*r) = -v0
- v1 * 2 * r + v2 - v2 * r*r = -v0 * (1 + r*r)
- v1 * 2 * r + v2 - v2 * r*r = -v0 + -v0 * r*r
- (v0 - v2) * r * r + (2 * v1) * r + (v0 + v2) = 0;
- */
- tanHalfAngle = tw->maxTan;
- // transform rotation axis to z-axis
- p = (point - tw->origin) * tw->matrix;
- d = plane[3] + plane.Normal() * tw->origin;
- normal = plane.Normal() * tw->matrix;
- v0 = normal[2] * p[2] + d;
- v1 = normal[0] * p[1] - normal[1] * p[0];
- v2 = normal[0] * p[0] + normal[1] * p[1];
- a = v0 - v2;
- b = v1;
- c = v0 + v2;
- if ( a == 0.0f ) {
- if ( b == 0.0f ) {
- return false;
- }
- frac1 = -c / ( 2.0f * b );
- frac2 = 1e10; // = tan( idMath::HALF_PI )
- }
- else {
- d = b * b - c * a;
- if ( d <= 0.0f ) {
- return false;
- }
- sqrtd = sqrt( d );
- if ( b > 0.0f ) {
- q = - b + sqrtd;
- }
- else {
- q = - b - sqrtd;
- }
- frac1 = q / a;
- frac2 = c / q;
- }
- if ( angle < 0.0f ) {
- frac1 = -frac1;
- frac2 = -frac2;
- }
- // get smallest tangent for which a collision occurs
- if ( frac1 >= minTan && frac1 < tanHalfAngle ) {
- tanHalfAngle = frac1;
- }
- if ( frac2 >= minTan && frac2 < tanHalfAngle ) {
- tanHalfAngle = frac2;
- }
- if ( angle < 0.0f ) {
- tanHalfAngle = -tanHalfAngle;
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::PointFurthestFromPlane
- calculates the direction of motion at the initial position, where dir < 0 means the point moves towards the plane
- if the point moves away from the plane the tangent of half the rotation angle at which
- the point is furthest away from the plane is also calculated
- ================
- */
- int idCollisionModelManagerLocal::PointFurthestFromPlane( const cm_traceWork_t *tw, const idVec3 &point, const idPlane &plane,
- const float angle, float &tanHalfAngle, float &dir ) {
- double v1, v2, a, b, c, d, sqrtd, q, frac1, frac2;
- idVec3 p, normal;
- /*
- v2 * cos(t) + v1 * sin(t) + v0 = 0;
- // rotation about the z-axis
- v0 = normal[2] * p[2] + dist
- v1 = normal[0] * p[1] - normal[1] * p[0]
- v2 = normal[0] * p[0] + normal[1] * p[1]
- derivative:
- v1 * cos(t) - v2 * sin(t) = 0;
- r = tan(t / 2);
- sin(t) = 2*r/(1+r*r);
- cos(t) = (1-r*r)/(1+r*r);
- -v2 * 2 * r / (1 + r*r) + v1 * (1 - r*r)/(1+r*r);
- -v2 * 2 * r + v1 * (1 - r*r) / (1 + r*r) = 0;
- -v2 * 2 * r + v1 * (1 - r*r) = 0;
- (-v1) * r * r + (-2 * v2) * r + (v1) = 0;
- */
- tanHalfAngle = 0.0f;
- // transform rotation axis to z-axis
- p = (point - tw->origin) * tw->matrix;
- normal = plane.Normal() * tw->matrix;
- v1 = normal[0] * p[1] - normal[1] * p[0];
- v2 = normal[0] * p[0] + normal[1] * p[1];
- // the point will always start at the front of the plane, therefore v0 + v2 > 0 is always true
- if ( angle < 0.0f ) {
- dir = -v1;
- }
- else {
- dir = v1;
- }
- // negative direction means the point moves towards the plane at the initial position
- if ( dir <= 0.0f ) {
- return true;
- }
- a = -v1;
- b = -v2;
- c = v1;
- if ( a == 0.0f ) {
- if ( b == 0.0f ) {
- return false;
- }
- frac1 = -c / ( 2.0f * b );
- frac2 = 1e10; // = tan( idMath::HALF_PI )
- }
- else {
- d = b * b - c * a;
- if ( d <= 0.0f ) {
- return false;
- }
- sqrtd = sqrt( d );
- if ( b > 0.0f ) {
- q = - b + sqrtd;
- }
- else {
- q = - b - sqrtd;
- }
- frac1 = q / a;
- frac2 = c / q;
- }
- if ( angle < 0.0f ) {
- frac1 = -frac1;
- frac2 = -frac2;
- }
- if ( frac1 < 0.0f && frac2 < 0.0f ) {
- return false;
- }
- if ( frac1 > frac2 ) {
- tanHalfAngle = frac1;
- }
- else {
- tanHalfAngle = frac2;
- }
- if ( angle < 0.0f ) {
- tanHalfAngle = -tanHalfAngle;
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane
- ================
- */
- int idCollisionModelManagerLocal::RotatePointThroughEpsilonPlane( const cm_traceWork_t *tw, const idVec3 &point, const idVec3 &endPoint,
- const idPlane &plane, const float angle, const idVec3 &origin,
- float &tanHalfAngle, idVec3 &collisionPoint, idVec3 &endDir ) {
- float d, dir, startTan;
- idVec3 vec, startDir;
- idPlane epsPlane;
- // epsilon expanded plane
- epsPlane = plane;
- epsPlane.SetDist( epsPlane.Dist() + CM_CLIP_EPSILON );
- // if the rotation sphere at the rotation origin is too far away from the polygon plane
- d = epsPlane.Distance( origin );
- vec = point - origin;
- if ( d * d > vec * vec ) {
- return false;
- }
- // calculate direction of motion at vertex start position
- startDir = ( point - origin ).Cross( tw->axis );
- if ( angle < 0.0f ) {
- startDir = -startDir;
- }
- // if moving away from plane at start position
- if ( startDir * epsPlane.Normal() >= 0.0f ) {
- // if end position is outside epsilon range
- d = epsPlane.Distance( endPoint );
- if ( d >= 0.0f ) {
- return false; // no collision
- }
- // calculate direction of motion at vertex end position
- endDir = ( endPoint - origin ).Cross( tw->axis );
- if ( angle < 0.0f ) {
- endDir = -endDir;
- }
- // if also moving away from plane at end position
- if ( endDir * epsPlane.Normal() > 0.0f ) {
- return false; // no collision
- }
- }
- // if the start position is in the epsilon range
- d = epsPlane.Distance( point );
- if ( d <= CM_PL_RANGE_EPSILON ) {
- // calculate tangent of half the rotation for which the vertex is furthest away from the plane
- if ( !PointFurthestFromPlane( tw, point, plane, angle, startTan, dir ) ) {
- return false;
- }
- if ( dir <= 0.0f ) {
- // moving towards the polygon plane so stop immediately
- tanHalfAngle = 0.0f;
- }
- else if ( idMath::Fabs( startTan ) >= tw->maxTan ) {
- // never going to get beyond the start tangent during the current rotation
- return false;
- }
- else {
- // calculate collision with epsilon expanded plane
- if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, idMath::Fabs( startTan ), tanHalfAngle ) ) {
- tanHalfAngle = startTan;
- }
- }
- }
- else {
- // calculate collision with epsilon expanded plane
- if ( !RotatePointThroughPlane( tw, point, epsPlane, angle, 0.0f, tanHalfAngle ) ) {
- return false;
- }
- }
- // calculate collision point
- collisionPoint = point;
- if ( tanHalfAngle != 0.0f ) {
- CM_RotatePoint( collisionPoint, tw->origin, tw->axis, tanHalfAngle );
- }
- // calculate direction of motion at collision point
- endDir = ( collisionPoint - origin ).Cross( tw->axis );
- if ( angle < 0.0f ) {
- endDir = -endDir;
- }
- return true;
- }
- /*
- ================
- idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon
- ================
- */
- void idCollisionModelManagerLocal::RotateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int vertexNum ) {
- int i;
- float tanHalfAngle;
- idVec3 endDir, collisionPoint;
- idPluecker pl;
- // if the trm vertex is behind the polygon plane it cannot collide with the polygon within a 180 degrees rotation
- if ( tw->isConvex && tw->axisIntersectsTrm && v->polygonSide ) {
- return;
- }
- // if the trace model vertex rotation bounds do not intersect the polygon bounds
- if ( !v->rotationBounds.IntersectsBounds( poly->bounds ) ) {
- return;
- }
- // vertex rotation bounds should cross polygon plane
- if ( v->rotationBounds.PlaneSide( poly->plane ) != SIDE_CROSS ) {
- return;
- }
- // rotate the vertex through the epsilon plane
- if ( !RotatePointThroughEpsilonPlane( tw, v->p, v->endp, poly->plane, tw->angle, v->rotationOrigin,
- tanHalfAngle, collisionPoint, endDir ) ) {
- return;
- }
- if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) {
- // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges
- pl.FromRay( collisionPoint, endDir );
- for ( i = 0; i < poly->numEdges; i++ ) {
- if ( poly->edges[i] < 0 ) {
- if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) > 0.0f ) {
- return;
- }
- }
- else {
- if ( pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] ) < 0.0f ) {
- return;
- }
- }
- }
- tw->maxTan = idMath::Fabs( tanHalfAngle );
- // collision plane is the polygon plane
- tw->trace.c.normal = poly->plane.Normal();
- tw->trace.c.dist = poly->plane.Dist();
- tw->trace.c.contents = poly->contents;
- tw->trace.c.material = poly->material;
- tw->trace.c.type = CONTACT_TRMVERTEX;
- tw->trace.c.modelFeature = *reinterpret_cast<int *>(&poly);
- tw->trace.c.trmFeature = v - tw->vertices;
- tw->trace.c.point = collisionPoint;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon
- ================
- */
- void idCollisionModelManagerLocal::RotateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &rotationOrigin ) {
- int i, edgeNum;
- float tanHalfAngle;
- idVec3 dir, endp, endDir, collisionPoint;
- idPluecker pl;
- cm_trmEdge_t *edge;
- // if the polygon vertex is behind the trm plane it cannot collide with the trm polygon within a 180 degrees rotation
- if ( tw->isConvex && tw->axisIntersectsTrm && trmpoly->plane.Distance( v->p ) < 0.0f ) {
- return;
- }
- // if the model vertex is outside the trm polygon rotation bounds
- if ( !trmpoly->rotationBounds.ContainsPoint( v->p ) ) {
- return;
- }
- // if the rotation axis goes through the polygon vertex
- dir = v->p - rotationOrigin;
- if ( dir * dir < ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
- return;
- }
- // calculate vertex end position
- endp = v->p;
- tw->modelVertexRotation.RotatePoint( endp );
- // rotate the vertex through the epsilon plane
- if ( !RotatePointThroughEpsilonPlane( tw, v->p, endp, trmpoly->plane, -tw->angle, rotationOrigin,
- tanHalfAngle, collisionPoint, endDir ) ) {
- return;
- }
- if ( idMath::Fabs( tanHalfAngle ) < tw->maxTan ) {
- // verify if 'collisionPoint' moving along 'endDir' moves between polygon edges
- pl.FromRay( collisionPoint, endDir );
- for ( i = 0; i < trmpoly->numEdges; i++ ) {
- edgeNum = trmpoly->edges[i];
- edge = tw->edges + abs(edgeNum);
- if ( edgeNum < 0 ) {
- if ( pl.PermutedInnerProduct( edge->pl ) > 0.0f ) {
- return;
- }
- }
- else {
- if ( pl.PermutedInnerProduct( edge->pl ) < 0.0f ) {
- return;
- }
- }
- }
- tw->maxTan = idMath::Fabs( tanHalfAngle );
- // collision plane is the flipped trm polygon plane
- tw->trace.c.normal = -trmpoly->plane.Normal();
- tw->trace.c.dist = tw->trace.c.normal * v->p;
- tw->trace.c.contents = poly->contents;
- tw->trace.c.material = poly->material;
- tw->trace.c.type = CONTACT_MODELVERTEX;
- tw->trace.c.modelFeature = v - tw->model->vertices;
- tw->trace.c.trmFeature = trmpoly - tw->polys;
- tw->trace.c.point = v->p;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::RotateTrmThroughPolygon
- returns true if the polygon blocks the complete rotation
- ================
- */
- bool idCollisionModelManagerLocal::RotateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) {
- int i, j, k, edgeNum;
- float d;
- cm_trmVertex_t *bv;
- cm_trmEdge_t *be;
- cm_trmPolygon_t *bp;
- cm_vertex_t *v;
- cm_edge_t *e;
- idVec3 *rotationOrigin;
- // if already checked this polygon
- if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) {
- return false;
- }
- p->checkcount = idCollisionModelManagerLocal::checkCount;
- // if this polygon does not have the right contents behind it
- if ( !(p->contents & tw->contents) ) {
- return false;
- }
- // if the the trace bounds do not intersect the polygon bounds
- if ( !tw->bounds.IntersectsBounds( p->bounds ) ) {
- return false;
- }
- // back face culling
- if ( tw->isConvex ) {
- // if the center of the convex trm is behind the polygon plane
- if ( p->plane.Distance( tw->start ) < 0.0f ) {
- // if the rotation axis intersects the trace model
- if ( tw->axisIntersectsTrm ) {
- return false;
- }
- else {
- // if the direction of motion at the start and end position of the
- // center of the trm both go towards or away from the polygon plane
- // or if the intersections of the rotation axis with the expanded heart planes
- // are both in front of the polygon plane
- }
- }
- }
- // if the polygon is too far from the first heart plane
- d = p->bounds.PlaneDistance( tw->heartPlane1 );
- if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane1 ) {
- return false;
- }
- // rotation bounds should cross polygon plane
- switch( tw->bounds.PlaneSide( p->plane ) ) {
- case PLANESIDE_CROSS:
- break;
- case PLANESIDE_FRONT:
- if ( tw->model->isConvex ) {
- tw->quickExit = true;
- return true;
- }
- default:
- return false;
- }
- for ( i = 0; i < tw->numVerts; i++ ) {
- bv = tw->vertices + i;
- // calculate polygon side this vertex is on
- d = p->plane.Distance( bv->p );
- bv->polygonSide = ( d < 0.0f );
- }
- for ( i = 0; i < p->numEdges; i++ ) {
- edgeNum = p->edges[i];
- e = tw->model->edges + abs(edgeNum);
- v = tw->model->vertices + e->vertexNum[INT32_SIGNBITSET( edgeNum )];
- // pluecker coordinate for edge
- tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[e->vertexNum[0]].p,
- tw->model->vertices[e->vertexNum[1]].p );
- // calculate rotation origin projected into rotation plane through the vertex
- tw->polygonRotationOriginCache[i] = tw->origin + tw->axis * ( tw->axis * ( v->p - tw->origin ) );
- }
- // copy first to last so we can easily cycle through
- tw->polygonRotationOriginCache[p->numEdges] = tw->polygonRotationOriginCache[0];
- // fast point rotation
- if ( tw->pointTrace ) {
- RotateTrmVertexThroughPolygon( tw, p, &tw->vertices[0], 0 );
- }
- else {
- // rotate trm vertices through polygon
- for ( i = 0; i < tw->numVerts; i++ ) {
- bv = tw->vertices + i;
- if ( bv->used ) {
- RotateTrmVertexThroughPolygon( tw, p, bv, i );
- }
- }
- // rotate trm edges through polygon
- for ( i = 1; i <= tw->numEdges; i++ ) {
- be = tw->edges + i;
- if ( be->used ) {
- RotateTrmEdgeThroughPolygon( tw, p, be );
- }
- }
- // rotate all polygon vertices through the trm
- for ( i = 0; i < p->numEdges; i++ ) {
- edgeNum = p->edges[i];
- e = tw->model->edges + abs(edgeNum);
- if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) {
- continue;
- }
- // set edge check count
- e->checkcount = idCollisionModelManagerLocal::checkCount;
- // can never collide with internal edges
- if ( e->internal ) {
- continue;
- }
- // got to check both vertices because we skip internal edges
- for ( k = 0; k < 2; k++ ) {
- v = tw->model->vertices + e->vertexNum[k ^ INT32_SIGNBITSET( edgeNum )];
- // if this vertex is already checked
- if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) {
- continue;
- }
- // set vertex check count
- v->checkcount = idCollisionModelManagerLocal::checkCount;
- // if the vertex is outside the trm rotation bounds
- if ( !tw->bounds.ContainsPoint( v->p ) ) {
- continue;
- }
- rotationOrigin = &tw->polygonRotationOriginCache[i+k];
- for ( j = 0; j < tw->numPolys; j++ ) {
- bp = tw->polys + j;
- if ( bp->used ) {
- RotateVertexThroughTrmPolygon( tw, bp, p, v, *rotationOrigin );
- }
- }
- }
- }
- }
- return ( tw->maxTan == 0.0f );
- }
- /*
- ================
- idCollisionModelManagerLocal::BoundsForRotation
- only for rotations < 180 degrees
- ================
- */
- void idCollisionModelManagerLocal::BoundsForRotation( const idVec3 &origin, const idVec3 &axis, const idVec3 &start, const idVec3 &end, idBounds &bounds ) {
- int i;
- float radiusSqr;
- idVec3 v1, v2;
- radiusSqr = ( start - origin ).LengthSqr();
- v1 = ( start - origin ).Cross( axis );
- v2 = ( end - origin ).Cross( axis );
- for ( i = 0; i < 3; i++ ) {
- // if the derivative changes sign along this axis during the rotation from start to end
- if ( ( v1[i] > 0.0f && v2[i] < 0.0f ) || ( v1[i] < 0.0f && v2[i] > 0.0f ) ) {
- if ( ( 0.5f * (start[i] + end[i]) - origin[i] ) > 0.0f ) {
- bounds[0][i] = Min( start[i], end[i] );
- bounds[1][i] = origin[i] + idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) );
- }
- else {
- bounds[0][i] = origin[i] - idMath::Sqrt( radiusSqr * ( 1.0f - axis[i] * axis[i] ) );
- bounds[1][i] = Max( start[i], end[i] );
- }
- }
- else if ( start[i] > end[i] ) {
- bounds[0][i] = end[i];
- bounds[1][i] = start[i];
- }
- else {
- bounds[0][i] = start[i];
- bounds[1][i] = end[i];
- }
- // expand for epsilons
- bounds[0][i] -= CM_BOX_EPSILON;
- bounds[1][i] += CM_BOX_EPSILON;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::Rotation180
- ================
- */
- void idCollisionModelManagerLocal::Rotation180( trace_t *results, const idVec3 &rorg, const idVec3 &axis,
- const float startAngle, const float endAngle, const idVec3 &start,
- const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
- cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
- int i, j, edgeNum;
- float d, maxErr, initialTan;
- bool model_rotated, trm_rotated;
- idVec3 dir, dir1, dir2, tmp, vr, vup, org, at, bt;
- idMat3 invModelAxis, endAxis, tmpAxis;
- idRotation startRotation, endRotation;
- idPluecker plaxis;
- cm_trmPolygon_t *poly;
- cm_trmEdge_t *edge;
- cm_trmVertex_t *vert;
- ALIGN16( static cm_traceWork_t tw );
- if ( model < 0 || model > MAX_SUBMODELS || model > idCollisionModelManagerLocal::maxModels ) {
- common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model handle\n");
- return;
- }
- if ( !idCollisionModelManagerLocal::models[model] ) {
- common->Printf("idCollisionModelManagerLocal::Rotation180: invalid model\n");
- return;
- }
- idCollisionModelManagerLocal::checkCount++;
- tw.trace.fraction = 1.0f;
- tw.trace.c.contents = 0;
- tw.trace.c.type = CONTACT_NONE;
- tw.contents = contentMask;
- tw.isConvex = true;
- tw.rotation = true;
- tw.positionTest = false;
- tw.axisIntersectsTrm = false;
- tw.quickExit = false;
- tw.angle = endAngle - startAngle;
- assert( tw.angle > -180.0f && tw.angle < 180.0f );
- tw.maxTan = initialTan = idMath::Fabs( tan( ( idMath::PI / 360.0f ) * tw.angle ) );
- tw.model = idCollisionModelManagerLocal::models[model];
- tw.start = start - modelOrigin;
- // rotation axis, axis is assumed to be normalized
- tw.axis = axis;
- // assert( tw.axis[0] * tw.axis[0] + tw.axis[1] * tw.axis[1] + tw.axis[2] * tw.axis[2] > 0.99f );
- // rotation origin projected into rotation plane through tw.start
- tw.origin = rorg - modelOrigin;
- d = (tw.axis * tw.origin) - ( tw.axis * tw.start );
- tw.origin = tw.origin - d * tw.axis;
- // radius of rotation
- tw.radius = ( tw.start - tw.origin ).Length();
- // maximum error of the circle approximation traced through the axial BSP tree
- d = tw.radius * tw.radius - (CIRCLE_APPROXIMATION_LENGTH*CIRCLE_APPROXIMATION_LENGTH*0.25f);
- if ( d > 0.0f ) {
- maxErr = tw.radius - idMath::Sqrt( d );
- } else {
- maxErr = tw.radius;
- }
- model_rotated = modelAxis.IsRotated();
- if ( model_rotated ) {
- invModelAxis = modelAxis.Transpose();
- tw.axis *= invModelAxis;
- tw.origin *= invModelAxis;
- }
- startRotation.Set( tw.origin, tw.axis, startAngle );
- endRotation.Set( tw.origin, tw.axis, endAngle );
- // create matrix which rotates the rotation axis to the z-axis
- tw.axis.NormalVectors( vr, vup );
- tw.matrix[0][0] = vr[0];
- tw.matrix[1][0] = vr[1];
- tw.matrix[2][0] = vr[2];
- tw.matrix[0][1] = -vup[0];
- tw.matrix[1][1] = -vup[1];
- tw.matrix[2][1] = -vup[2];
- tw.matrix[0][2] = tw.axis[0];
- tw.matrix[1][2] = tw.axis[1];
- tw.matrix[2][2] = tw.axis[2];
- // if optimized point trace
- if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f &&
- trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f &&
- trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) {
- if ( model_rotated ) {
- // rotate trace instead of model
- tw.start *= invModelAxis;
- }
- tw.end = tw.start;
- // if we start at a specific angle
- if ( startAngle != 0.0f ) {
- startRotation.RotatePoint( tw.start );
- }
- // calculate end position of rotation
- endRotation.RotatePoint( tw.end );
- // calculate rotation origin projected into rotation plane through the vertex
- tw.numVerts = 1;
- tw.vertices[0].p = tw.start;
- tw.vertices[0].endp = tw.end;
- tw.vertices[0].used = true;
- tw.vertices[0].rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( tw.vertices[0].p - tw.origin ) );
- BoundsForRotation( tw.vertices[0].rotationOrigin, tw.axis, tw.start, tw.end, tw.vertices[0].rotationBounds );
- // rotation bounds
- tw.bounds = tw.vertices[0].rotationBounds;
- tw.numEdges = tw.numPolys = 0;
- // collision with single point
- tw.pointTrace = true;
- // extents is set to maximum error of the circle approximation traced through the axial BSP tree
- tw.extents[0] = tw.extents[1] = tw.extents[2] = maxErr + CM_BOX_EPSILON;
- // setup rotation heart plane
- tw.heartPlane1.SetNormal( tw.axis );
- tw.heartPlane1.FitThroughPoint( tw.start );
- tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON;
- // trace through the model
- idCollisionModelManagerLocal::TraceThroughModel( &tw );
- // store results
- *results = tw.trace;
- results->endpos = start;
- if ( tw.maxTan == initialTan ) {
- results->fraction = 1.0f;
- } else {
- results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle );
- }
- assert( results->fraction <= 1.0f );
- endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction );
- endRotation.RotatePoint( results->endpos );
- results->endAxis.Identity();
- if ( results->fraction < 1.0f ) {
- // rotate trace plane normal if there was a collision with a rotated model
- if ( model_rotated ) {
- results->c.normal *= modelAxis;
- results->c.point *= modelAxis;
- }
- results->c.point += modelOrigin;
- results->c.dist += modelOrigin * results->c.normal;
- }
- return;
- }
- tw.pointTrace = false;
- // setup trm structure
- idCollisionModelManagerLocal::SetupTrm( &tw, trm );
- trm_rotated = trmAxis.IsRotated();
- // calculate vertex positions
- if ( trm_rotated ) {
- for ( i = 0; i < tw.numVerts; i++ ) {
- // rotate trm around the start position
- tw.vertices[i].p *= trmAxis;
- }
- }
- for ( i = 0; i < tw.numVerts; i++ ) {
- // set trm at start position
- tw.vertices[i].p += tw.start;
- }
- if ( model_rotated ) {
- for ( i = 0; i < tw.numVerts; i++ ) {
- tw.vertices[i].p *= invModelAxis;
- }
- }
- for ( i = 0; i < tw.numVerts; i++ ) {
- tw.vertices[i].endp = tw.vertices[i].p;
- }
- // if we start at a specific angle
- if ( startAngle != 0.0f ) {
- for ( i = 0; i < tw.numVerts; i++ ) {
- startRotation.RotatePoint( tw.vertices[i].p );
- }
- }
- for ( i = 0; i < tw.numVerts; i++ ) {
- // end position of vertex
- endRotation.RotatePoint( tw.vertices[i].endp );
- }
- // add offset to start point
- if ( trm_rotated ) {
- tw.start += trm->offset * trmAxis;
- } else {
- tw.start += trm->offset;
- }
- // if the model is rotated
- if ( model_rotated ) {
- // rotate trace instead of model
- tw.start *= invModelAxis;
- }
- tw.end = tw.start;
- // if we start at a specific angle
- if ( startAngle != 0.0f ) {
- startRotation.RotatePoint( tw.start );
- }
- // calculate end position of rotation
- endRotation.RotatePoint( tw.end );
- // setup trm vertices
- for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) {
- // calculate rotation origin projected into rotation plane through the vertex
- vert->rotationOrigin = tw.origin + tw.axis * ( tw.axis * ( vert->p - tw.origin ) );
- // calculate rotation bounds for this vertex
- BoundsForRotation( vert->rotationOrigin, tw.axis, vert->p, vert->endp, vert->rotationBounds );
- // if the rotation axis goes through the vertex then the vertex is not used
- d = ( vert->p - vert->rotationOrigin ).LengthSqr();
- if ( d > ROTATION_AXIS_EPSILON * ROTATION_AXIS_EPSILON ) {
- vert->used = true;
- }
- }
- // setup trm edges
- for ( edge = tw.edges + 1, i = 1; i <= tw.numEdges; i++, edge++ ) {
- // if the rotation axis goes through both the edge vertices then the edge is not used
- if ( tw.vertices[edge->vertexNum[0]].used | tw.vertices[edge->vertexNum[1]].used ) {
- edge->used = true;
- }
- // edge start, end and pluecker coordinate
- edge->start = tw.vertices[edge->vertexNum[0]].p;
- edge->end = tw.vertices[edge->vertexNum[1]].p;
- edge->pl.FromLine( edge->start, edge->end );
- // pluecker coordinate for edge being rotated about the z-axis
- at = ( edge->start - tw.origin ) * tw.matrix;
- bt = ( edge->end - tw.origin ) * tw.matrix;
- edge->plzaxis.FromLine( at, bt );
- // get edge rotation bounds from the rotation bounds of both vertices
- edge->rotationBounds = tw.vertices[edge->vertexNum[0]].rotationBounds;
- edge->rotationBounds.AddBounds( tw.vertices[edge->vertexNum[1]].rotationBounds );
- // used to calculate if the rotation axis intersects the trm
- edge->bitNum = 0;
- }
- tw.bounds.Clear();
- // rotate trm polygon planes
- if ( trm_rotated & model_rotated ) {
- tmpAxis = trmAxis * invModelAxis;
- for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
- poly->plane *= tmpAxis;
- }
- } else if ( trm_rotated ) {
- for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
- poly->plane *= trmAxis;
- }
- } else if ( model_rotated ) {
- for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
- poly->plane *= invModelAxis;
- }
- }
- // setup trm polygons
- for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
- poly->used = true;
- // set trm polygon plane distance
- poly->plane.FitThroughPoint( tw.edges[abs(poly->edges[0])].start );
- // get polygon bounds from edge bounds
- poly->rotationBounds.Clear();
- for ( j = 0; j < poly->numEdges; j++ ) {
- // add edge rotation bounds to polygon rotation bounds
- edge = &tw.edges[abs( poly->edges[j] )];
- poly->rotationBounds.AddBounds( edge->rotationBounds );
- }
- // get trace bounds from polygon bounds
- tw.bounds.AddBounds( poly->rotationBounds );
- }
- // extents including the maximum error of the circle approximation traced through the axial BSP tree
- for ( i = 0; i < 3; i++ ) {
- tw.size[0][i] = tw.bounds[0][i] - tw.start[i];
- tw.size[1][i] = tw.bounds[1][i] - tw.start[i];
- if ( idMath::Fabs( tw.size[0][i] ) > idMath::Fabs( tw.size[1][i] ) ) {
- tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + maxErr + CM_BOX_EPSILON;
- } else {
- tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + maxErr + CM_BOX_EPSILON;
- }
- }
- // for back-face culling
- if ( tw.isConvex ) {
- if ( tw.start == tw.origin ) {
- tw.axisIntersectsTrm = true;
- } else {
- // determine if the rotation axis intersects the trm
- plaxis.FromRay( tw.origin, tw.axis );
- for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
- // back face cull polygons
- if ( poly->plane.Normal() * tw.axis > 0.0f ) {
- continue;
- }
- // test if the axis goes between the polygon edges
- for ( j = 0; j < poly->numEdges; j++ ) {
- edgeNum = poly->edges[j];
- edge = tw.edges + abs( edgeNum );
- if ( ( edge->bitNum & 2 ) == 0 ) {
- d = plaxis.PermutedInnerProduct( edge->pl );
- edge->bitNum = ( ( d < 0.0f ) ? 1 : 0 ) | 2;
- }
- if ( ( edge->bitNum ^ INT32_SIGNBITSET( edgeNum ) ) & 1 ) {
- break;
- }
- }
- if ( j >= poly->numEdges ) {
- tw.axisIntersectsTrm = true;
- break;
- }
- }
- }
- }
- // setup rotation heart plane
- tw.heartPlane1.SetNormal( tw.axis );
- tw.heartPlane1.FitThroughPoint( tw.start );
- tw.maxDistFromHeartPlane1 = 0.0f;
- for ( i = 0; i < tw.numVerts; i++ ) {
- d = idMath::Fabs( tw.heartPlane1.Distance( tw.vertices[i].p ) );
- if ( d > tw.maxDistFromHeartPlane1 ) {
- tw.maxDistFromHeartPlane1 = d;
- }
- }
- tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON;
- // inverse rotation to rotate model vertices towards trace model
- tw.modelVertexRotation.Set( tw.origin, tw.axis, -tw.angle );
- // trace through the model
- idCollisionModelManagerLocal::TraceThroughModel( &tw );
- // store results
- *results = tw.trace;
- results->endpos = start;
- if ( tw.maxTan == initialTan ) {
- results->fraction = 1.0f;
- } else {
- results->fraction = idMath::Fabs( atan( tw.maxTan ) * ( 2.0f * 180.0f / idMath::PI ) / tw.angle );
- }
- assert( results->fraction <= 1.0f );
- endRotation.Set( rorg, axis, startAngle + (endAngle-startAngle) * results->fraction );
- endRotation.RotatePoint( results->endpos );
- results->endAxis = trmAxis * endRotation.ToMat3();
- if ( results->fraction < 1.0f ) {
- // rotate trace plane normal if there was a collision with a rotated model
- if ( model_rotated ) {
- results->c.normal *= modelAxis;
- results->c.point *= modelAxis;
- }
- results->c.point += modelOrigin;
- results->c.dist += modelOrigin * results->c.normal;
- }
- }
- /*
- ================
- idCollisionModelManagerLocal::Rotation
- ================
- */
- #ifdef _DEBUG
- static int entered = 0;
- #endif
- void idCollisionModelManagerLocal::Rotation( trace_t *results, const idVec3 &start, const idRotation &rotation,
- const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
- cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
- idVec3 tmp;
- float maxa, stepa, a, lasta;
- assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) > (((byte *)results) + sizeof( trace_t )) );
- assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) > (((byte *)results) + sizeof( trace_t )) );
- memset( results, 0, sizeof( *results ) );
- // if special position test
- if ( rotation.GetAngle() == 0.0f ) {
- idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
- return;
- }
- #ifdef _DEBUG
- bool startsolid = false;
- // test whether or not stuck to begin with
- if ( cm_debugCollision.GetBool() ) {
- if ( !entered ) {
- entered = 1;
- // if already messed up to begin with
- if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
- startsolid = true;
- }
- entered = 0;
- }
- }
- #endif
- if ( rotation.GetAngle() >= 180.0f || rotation.GetAngle() <= -180.0f) {
- if ( rotation.GetAngle() >= 360.0f ) {
- maxa = 360.0f;
- stepa = 120.0f; // three steps strictly < 180 degrees
- } else if ( rotation.GetAngle() <= -360.0f ) {
- maxa = -360.0f;
- stepa = -120.0f; // three steps strictly < 180 degrees
- } else {
- maxa = rotation.GetAngle();
- stepa = rotation.GetAngle() * 0.5f; // two steps strictly < 180 degrees
- }
- for ( lasta = 0.0f, a = stepa; fabs( a ) < fabs( maxa ) + 1.0f; lasta = a, a += stepa ) {
- // partial rotation
- idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), lasta, a, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
- // if there is a collision
- if ( results->fraction < 1.0f ) {
- // fraction of total rotation
- results->fraction = (lasta + stepa * results->fraction) / rotation.GetAngle();
- return;
- }
- }
- results->fraction = 1.0f;
- return;
- }
- idCollisionModelManagerLocal::Rotation180( results, rotation.GetOrigin(), rotation.GetVec(), 0.0f, rotation.GetAngle(), start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
- #ifdef _DEBUG
- // test for missed collisions
- if ( cm_debugCollision.GetBool() ) {
- if ( !entered ) {
- entered = 1;
- // if the trm is stuck in the model
- if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
- trace_t tr;
- // test where the trm is stuck in the model
- idCollisionModelManagerLocal::Contents( results->endpos, trm, results->endAxis, -1, model, modelOrigin, modelAxis );
- // re-run collision detection to find out where it failed
- idCollisionModelManagerLocal::Rotation( &tr, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
- }
- entered = 0;
- }
- }
- #endif
- }
|