2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 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 Source Code. If not, please request a copy in writing from id Software at the address below.
24 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.
26 ===========================================================================
30 ===============================================================================
32 Trace model vs. polygonal model collision detection.
34 ===============================================================================
37 #include "../idlib/precompiled.h"
40 #include "CollisionModel_local.h"
43 ===============================================================================
45 Collision detection for translational motion
47 ===============================================================================
52 idCollisionModelManagerLocal::TranslateEdgeThroughEdge
54 calculates fraction of the translation completed at which the edges collide
57 ID_INLINE int idCollisionModelManagerLocal::TranslateEdgeThroughEdge( idVec3 &cross, idPluecker &l1, idPluecker &l2, float *fraction ) {
65 dir = movement direction
66 l1 = pluecker coordinate for line
67 l2 = pluecker coordinate for edge we might collide with
68 a+dir = start of line after movement
69 b+dir = end of line after movement
71 solve pluecker inner product for t of line (a+t*dir : b+t*dir) and line l2
73 v[0] = (a[0]+t*dir[0]) * (b[1]+t*dir[1]) - (b[0]+t*dir[0]) * (a[1]+t*dir[1]);
74 v[1] = (a[0]+t*dir[0]) * (b[2]+t*dir[2]) - (b[0]+t*dir[0]) * (a[2]+t*dir[2]);
75 v[2] = (a[0]+t*dir[0]) - (b[0]+t*dir[0]);
76 v[3] = (a[1]+t*dir[1]) * (b[2]+t*dir[2]) - (b[1]+t*dir[1]) * (a[2]+t*dir[2]);
77 v[4] = (a[2]+t*dir[2]) - (b[2]+t*dir[2]);
78 v[5] = (b[1]+t*dir[1]) - (a[1]+t*dir[1]);
80 l2[0] * v[4] + l2[1] * v[5] + l2[2] * v[3] + l2[4] * v[0] + l2[5] * v[1] + l2[3] * v[2] = 0;
84 v[0] = (a[0]+t*dir[0]) * (b[1]+t*dir[1]) - (b[0]+t*dir[0]) * (a[1]+t*dir[1]);
85 v[0] = (a[0]*b[1]) + a[0]*t*dir[1] + b[1]*t*dir[0] + (t*t*dir[0]*dir[1]) -
86 ((b[0]*a[1]) + b[0]*t*dir[1] + a[1]*t*dir[0] + (t*t*dir[0]*dir[1]));
87 v[0] = a[0]*b[1] + a[0]*t*dir[1] + b[1]*t*dir[0] - b[0]*a[1] - b[0]*t*dir[1] - a[1]*t*dir[0];
89 v[1] = (a[0]+t*dir[0]) * (b[2]+t*dir[2]) - (b[0]+t*dir[0]) * (a[2]+t*dir[2]);
90 v[1] = (a[0]*b[2]) + a[0]*t*dir[2] + b[2]*t*dir[0] + (t*t*dir[0]*dir[2]) -
91 ((b[0]*a[2]) + b[0]*t*dir[2] + a[2]*t*dir[0] + (t*t*dir[0]*dir[2]));
92 v[1] = a[0]*b[2] + a[0]*t*dir[2] + b[2]*t*dir[0] - b[0]*a[2] - b[0]*t*dir[2] - a[2]*t*dir[0];
94 v[2] = (a[0]+t*dir[0]) - (b[0]+t*dir[0]);
97 v[3] = (a[1]+t*dir[1]) * (b[2]+t*dir[2]) - (b[1]+t*dir[1]) * (a[2]+t*dir[2]);
98 v[3] = (a[1]*b[2]) + a[1]*t*dir[2] + b[2]*t*dir[1] + (t*t*dir[1]*dir[2]) -
99 ((b[1]*a[2]) + b[1]*t*dir[2] + a[2]*t*dir[1] + (t*t*dir[1]*dir[2]));
100 v[3] = a[1]*b[2] + a[1]*t*dir[2] + b[2]*t*dir[1] - b[1]*a[2] - b[1]*t*dir[2] - a[2]*t*dir[1];
102 v[4] = (a[2]+t*dir[2]) - (b[2]+t*dir[2]);
105 v[5] = (b[1]+t*dir[1]) - (a[1]+t*dir[1]);
109 v[0] = a[0]*b[1] + a[0]*t*dir[1] + b[1]*t*dir[0] - b[0]*a[1] - b[0]*t*dir[1] - a[1]*t*dir[0];
110 v[1] = a[0]*b[2] + a[0]*t*dir[2] + b[2]*t*dir[0] - b[0]*a[2] - b[0]*t*dir[2] - a[2]*t*dir[0];
112 v[3] = a[1]*b[2] + a[1]*t*dir[2] + b[2]*t*dir[1] - b[1]*a[2] - b[1]*t*dir[2] - a[2]*t*dir[1];
116 v[0] = (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) * t + a[0]*b[1] - b[0]*a[1];
117 v[1] = (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) * t + a[0]*b[2] - b[0]*a[2];
119 v[3] = (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]) * t + a[1]*b[2] - b[1]*a[2];
123 l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) * t + l2[4] * (a[0]*b[1] - b[0]*a[1])
124 + l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) * t + l2[5] * (a[0]*b[2] - b[0]*a[2])
125 + l2[3] * (a[0] - b[0])
126 + l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]) * t + l2[2] * (a[1]*b[2] - b[1]*a[2])
127 + l2[0] * (a[2] - b[2])
128 + l2[1] * (b[1] - a[1]) = 0
130 t = (- l2[4] * (a[0]*b[1] - b[0]*a[1]) -
131 l2[5] * (a[0]*b[2] - b[0]*a[2]) -
132 l2[3] * (a[0] - b[0]) -
133 l2[2] * (a[1]*b[2] - b[1]*a[2]) -
134 l2[0] * (a[2] - b[2]) -
135 l2[1] * (b[1] - a[1])) /
136 (l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) +
137 l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) +
138 l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]));
140 d = l2[4] * (a[0]*dir[1] + b[1]*dir[0] - b[0]*dir[1] - a[1]*dir[0]) +
141 l2[5] * (a[0]*dir[2] + b[2]*dir[0] - b[0]*dir[2] - a[2]*dir[0]) +
142 l2[2] * (a[1]*dir[2] + b[2]*dir[1] - b[1]*dir[2] - a[2]*dir[1]);
144 t = - ( l2[4] * (a[0]*b[1] - b[0]*a[1]) +
145 l2[5] * (a[0]*b[2] - b[0]*a[2]) +
146 l2[3] * (a[0] - b[0]) +
147 l2[2] * (a[1]*b[2] - b[1]*a[2]) +
148 l2[0] * (a[2] - b[2]) +
149 l2[1] * (b[1] - a[1]));
152 MrE pats Pluecker on the head.. good monkey
155 d = l2[4] * (edgeDir[0]*dir[1] - edgeDir[1]*dir[0]) +
156 l2[5] * (edgeDir[0]*dir[2] - edgeDir[2]*dir[0]) +
157 l2[2] * (edgeDir[1]*dir[2] - edgeDir[2]*dir[1]);
160 d = l2[4] * cross[0] + l2[5] * cross[1] + l2[2] * cross[2];
168 t = -l1.PermutedInnerProduct( l2 );
169 // if the lines cross each other to begin with
174 // fraction of movement at the time the lines cross each other
184 ID_INLINE void CM_AddContact( cm_traceWork_t *tw ) {
186 if ( tw->numContacts >= tw->maxContacts ) {
189 // copy contact information from trace_t
190 tw->contacts[tw->numContacts] = tw->trace.c;
192 // set fraction back to 1 to find all other contacts
193 tw->trace.fraction = 1.0f;
198 CM_SetVertexSidedness
200 stores for the given model vertex at which side of one of the trm edges it passes
203 ID_INLINE void CM_SetVertexSidedness( cm_vertex_t *v, const idPluecker &vpl, const idPluecker &epl, const int bitNum ) {
204 if ( !(v->sideSet & (1<<bitNum)) ) {
206 fl = vpl.PermutedInnerProduct( epl );
207 v->side = (v->side & ~(1<<bitNum)) | (FLOATSIGNBITSET(fl) << bitNum);
208 v->sideSet |= (1 << bitNum);
216 stores for the given model edge at which side one of the trm vertices
219 ID_INLINE void CM_SetEdgeSidedness( cm_edge_t *edge, const idPluecker &vpl, const idPluecker &epl, const int bitNum ) {
220 if ( !(edge->sideSet & (1<<bitNum)) ) {
222 fl = vpl.PermutedInnerProduct( epl );
223 edge->side = (edge->side & ~(1<<bitNum)) | (FLOATSIGNBITSET(fl) << bitNum);
224 edge->sideSet |= (1 << bitNum);
230 idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon
233 void idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmEdge_t *trmEdge ) {
235 float f1, f2, dist, d1, d2;
236 idVec3 start, end, normal;
238 cm_vertex_t *v1, *v2;
239 idPluecker *pl, epsPl;
241 // check edges for a collision
242 for ( i = 0; i < poly->numEdges; i++) {
243 edgeNum = poly->edges[i];
244 edge = tw->model->edges + abs(edgeNum);
245 // if this edge is already checked
246 if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
249 // can never collide with internal edges
250 if ( edge->internal ) {
253 pl = &tw->polygonEdgePlueckerCache[i];
254 // get the sides at which the trm edge vertices pass the polygon edge
255 CM_SetEdgeSidedness( edge, *pl, tw->vertices[trmEdge->vertexNum[0]].pl, trmEdge->vertexNum[0] );
256 CM_SetEdgeSidedness( edge, *pl, tw->vertices[trmEdge->vertexNum[1]].pl, trmEdge->vertexNum[1] );
257 // if the trm edge start and end vertex do not pass the polygon edge at different sides
258 if ( !(((edge->side >> trmEdge->vertexNum[0]) ^ (edge->side >> trmEdge->vertexNum[1])) & 1) ) {
261 // get the sides at which the polygon edge vertices pass the trm edge
262 v1 = tw->model->vertices + edge->vertexNum[INTSIGNBITSET(edgeNum)];
263 CM_SetVertexSidedness( v1, tw->polygonVertexPlueckerCache[i], trmEdge->pl, trmEdge->bitNum );
264 v2 = tw->model->vertices + edge->vertexNum[INTSIGNBITNOTSET(edgeNum)];
265 CM_SetVertexSidedness( v2, tw->polygonVertexPlueckerCache[i+1], trmEdge->pl, trmEdge->bitNum );
266 // if the polygon edge start and end vertex do not pass the trm edge at different sides
267 if ( !((v1->side ^ v2->side) & (1<<trmEdge->bitNum)) ) {
270 // if there is no possible collision between the trm edge and the polygon edge
271 if ( !idCollisionModelManagerLocal::TranslateEdgeThroughEdge( trmEdge->cross, trmEdge->pl, *pl, &f1 ) ) {
274 // if moving away from edge
279 // pluecker coordinate for epsilon expanded edge
280 epsPl.FromLine( tw->model->vertices[edge->vertexNum[0]].p + edge->normal * CM_CLIP_EPSILON,
281 tw->model->vertices[edge->vertexNum[1]].p + edge->normal * CM_CLIP_EPSILON );
282 // calculate collision fraction with epsilon expanded edge
283 if ( !idCollisionModelManagerLocal::TranslateEdgeThroughEdge( trmEdge->cross, trmEdge->pl, epsPl, &f2 ) ) {
286 // if no collision with epsilon edge or moving away from edge
287 if ( f2 > 1.0f || f1 < f2 ) {
295 if ( f2 < tw->trace.fraction ) {
296 tw->trace.fraction = f2;
297 // create plane with normal vector orthogonal to both the polygon edge and the trm edge
298 start = tw->model->vertices[edge->vertexNum[0]].p;
299 end = tw->model->vertices[edge->vertexNum[1]].p;
300 tw->trace.c.normal = ( end - start ).Cross( trmEdge->end - trmEdge->start );
301 // FIXME: do this normalize when we know the first collision
302 tw->trace.c.normal.Normalize();
303 tw->trace.c.dist = tw->trace.c.normal * start;
304 // make sure the collision plane faces the trace model
305 if ( tw->trace.c.normal * trmEdge->start - tw->trace.c.dist < 0.0f ) {
306 tw->trace.c.normal = -tw->trace.c.normal;
307 tw->trace.c.dist = -tw->trace.c.dist;
309 tw->trace.c.contents = poly->contents;
310 tw->trace.c.material = poly->material;
311 tw->trace.c.type = CONTACT_EDGE;
312 tw->trace.c.modelFeature = edgeNum;
313 tw->trace.c.trmFeature = trmEdge - tw->edges;
314 // calculate collision point
315 normal[0] = trmEdge->cross[2];
316 normal[1] = -trmEdge->cross[1];
317 normal[2] = trmEdge->cross[0];
318 dist = normal * trmEdge->start;
319 d1 = normal * start - dist;
320 d2 = normal * end - dist;
321 f1 = d1 / ( d1 - d2 );
322 //assert( f1 >= 0.0f && f1 <= 1.0f );
323 tw->trace.c.point = start + f1 * ( end - start );
324 // if retrieving contacts
325 if ( tw->getContacts ) {
334 CM_TranslationPlaneFraction
340 float CM_TranslationPlaneFraction( idPlane &plane, idVec3 &start, idVec3 &end ) {
343 d2 = plane.Distance( end );
344 // if the end point is closer to the plane than an epsilon we still take it for a collision
345 if ( d2 >= CM_CLIP_EPSILON ) {
348 d1 = plane.Distance( start );
350 // if completely behind the polygon
358 return (d1-CM_CLIP_EPSILON) / (d1-d2);
363 float CM_TranslationPlaneFraction( idPlane &plane, idVec3 &start, idVec3 &end ) {
366 d2 = plane.Distance( end );
367 // if the end point is closer to the plane than an epsilon we still take it for a collision
368 // if ( d2 >= CM_CLIP_EPSILON ) {
369 d2eps = d2 - CM_CLIP_EPSILON;
370 if ( FLOATSIGNBITNOTSET(d2eps) ) {
373 d1 = plane.Distance( start );
375 // if completely behind the polygon
376 if ( FLOATSIGNBITSET(d1) ) {
379 // if going towards the front of the plane and
380 // the start and end point are not at equal distance from the plane
386 return (d1-CM_CLIP_EPSILON) / d2;
393 idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon
396 void idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v, int bitNum ) {
401 f = CM_TranslationPlaneFraction( poly->plane, v->p, v->endp );
402 if ( f < tw->trace.fraction ) {
404 for ( i = 0; i < poly->numEdges; i++ ) {
405 edgeNum = poly->edges[i];
406 edge = tw->model->edges + abs(edgeNum);
407 CM_SetEdgeSidedness( edge, tw->polygonEdgePlueckerCache[i], v->pl, bitNum );
408 if ( INTSIGNBITSET(edgeNum) ^ ((edge->side >> bitNum) & 1) ) {
415 tw->trace.fraction = f;
416 // collision plane is the polygon plane
417 tw->trace.c.normal = poly->plane.Normal();
418 tw->trace.c.dist = poly->plane.Dist();
419 tw->trace.c.contents = poly->contents;
420 tw->trace.c.material = poly->material;
421 tw->trace.c.type = CONTACT_TRMVERTEX;
422 tw->trace.c.modelFeature = *reinterpret_cast<int *>(&poly);
423 tw->trace.c.trmFeature = v - tw->vertices;
424 tw->trace.c.point = v->p + tw->trace.fraction * ( v->endp - v->p );
425 // if retrieving contacts
426 if ( tw->getContacts ) {
428 // no need to store the trm vertex more than once as a contact
436 idCollisionModelManagerLocal::TranslatePointThroughPolygon
439 void idCollisionModelManagerLocal::TranslatePointThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *poly, cm_trmVertex_t *v ) {
445 f = CM_TranslationPlaneFraction( poly->plane, v->p, v->endp );
446 if ( f < tw->trace.fraction ) {
448 for ( i = 0; i < poly->numEdges; i++ ) {
449 edgeNum = poly->edges[i];
450 edge = tw->model->edges + abs(edgeNum);
451 // if we didn't yet calculate the sidedness for this edge
452 if ( edge->checkcount != idCollisionModelManagerLocal::checkCount ) {
454 edge->checkcount = idCollisionModelManagerLocal::checkCount;
455 pl.FromLine(tw->model->vertices[edge->vertexNum[0]].p, tw->model->vertices[edge->vertexNum[1]].p);
456 fl = v->pl.PermutedInnerProduct( pl );
457 edge->side = FLOATSIGNBITSET(fl);
459 // if the point passes the edge at the wrong side
460 //if ( (edgeNum > 0) == edge->side ) {
461 if ( INTSIGNBITSET(edgeNum) ^ edge->side ) {
468 tw->trace.fraction = f;
469 // collision plane is the polygon plane
470 tw->trace.c.normal = poly->plane.Normal();
471 tw->trace.c.dist = poly->plane.Dist();
472 tw->trace.c.contents = poly->contents;
473 tw->trace.c.material = poly->material;
474 tw->trace.c.type = CONTACT_TRMVERTEX;
475 tw->trace.c.modelFeature = *reinterpret_cast<int *>(&poly);
476 tw->trace.c.trmFeature = v - tw->vertices;
477 tw->trace.c.point = v->p + tw->trace.fraction * ( v->endp - v->p );
478 // if retrieving contacts
479 if ( tw->getContacts ) {
481 // no need to store the trm vertex more than once as a contact
489 idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon
492 void idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon( cm_traceWork_t *tw, cm_trmPolygon_t *trmpoly, cm_polygon_t *poly, cm_vertex_t *v, idVec3 &endp, idPluecker &pl ) {
497 f = CM_TranslationPlaneFraction( trmpoly->plane, v->p, endp );
498 if ( f < tw->trace.fraction ) {
500 for ( i = 0; i < trmpoly->numEdges; i++ ) {
501 edgeNum = trmpoly->edges[i];
502 edge = tw->edges + abs(edgeNum);
504 CM_SetVertexSidedness( v, pl, edge->pl, edge->bitNum );
505 if ( INTSIGNBITSET(edgeNum) ^ ((v->side >> edge->bitNum) & 1) ) {
512 tw->trace.fraction = f;
513 // collision plane is the inverse trm polygon plane
514 tw->trace.c.normal = -trmpoly->plane.Normal();
515 tw->trace.c.dist = -trmpoly->plane.Dist();
516 tw->trace.c.contents = poly->contents;
517 tw->trace.c.material = poly->material;
518 tw->trace.c.type = CONTACT_MODELVERTEX;
519 tw->trace.c.modelFeature = v - tw->model->vertices;
520 tw->trace.c.trmFeature = trmpoly - tw->polys;
521 tw->trace.c.point = v->p + tw->trace.fraction * ( endp - v->p );
522 // if retrieving contacts
523 if ( tw->getContacts ) {
531 idCollisionModelManagerLocal::TranslateTrmThroughPolygon
533 returns true if the polygon blocks the complete translation
536 bool idCollisionModelManagerLocal::TranslateTrmThroughPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) {
537 int i, j, k, edgeNum;
547 // if already checked this polygon
548 if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) {
551 p->checkcount = idCollisionModelManagerLocal::checkCount;
553 // if this polygon does not have the right contents behind it
554 if ( !(p->contents & tw->contents) ) {
558 // if the the trace bounds do not intersect the polygon bounds
559 if ( !tw->bounds.IntersectsBounds( p->bounds ) ) {
563 // only collide with the polygon if approaching at the front
564 if ( ( p->plane.Normal() * tw->dir ) > 0.0f ) {
568 // if the polygon is too far from the first heart plane
569 d = p->bounds.PlaneDistance( tw->heartPlane1 );
570 if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane1 ) {
574 // if the polygon is too far from the second heart plane
575 d = p->bounds.PlaneDistance( tw->heartPlane2 );
576 if ( idMath::Fabs(d) > tw->maxDistFromHeartPlane2 ) {
579 fraction = tw->trace.fraction;
582 if ( tw->pointTrace ) {
583 idCollisionModelManagerLocal::TranslatePointThroughPolygon( tw, p, &tw->vertices[0] );
587 // trace bounds should cross polygon plane
588 switch ( tw->bounds.PlaneSide( p->plane ) ) {
589 case PLANESIDE_CROSS:
591 case PLANESIDE_FRONT:
592 if ( tw->model->isConvex ) {
593 tw->quickExit = true;
600 // calculate pluecker coordinates for the polygon edges and polygon vertices
601 for ( i = 0; i < p->numEdges; i++ ) {
602 edgeNum = p->edges[i];
603 e = tw->model->edges + abs(edgeNum);
604 // reset sidedness cache if this is the first time we encounter this edge during this trace
605 if ( e->checkcount != idCollisionModelManagerLocal::checkCount ) {
608 // pluecker coordinate for edge
609 tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[e->vertexNum[0]].p,
610 tw->model->vertices[e->vertexNum[1]].p );
612 v = &tw->model->vertices[e->vertexNum[INTSIGNBITSET(edgeNum)]];
613 // reset sidedness cache if this is the first time we encounter this vertex during this trace
614 if ( v->checkcount != idCollisionModelManagerLocal::checkCount ) {
617 // pluecker coordinate for vertex movement vector
618 tw->polygonVertexPlueckerCache[i].FromRay( v->p, -tw->dir );
620 // copy first to last so we can easily cycle through for the edges
621 tw->polygonVertexPlueckerCache[p->numEdges] = tw->polygonVertexPlueckerCache[0];
623 // trace trm vertices through polygon
624 for ( i = 0; i < tw->numVerts; i++ ) {
625 bv = tw->vertices + i;
627 idCollisionModelManagerLocal::TranslateTrmVertexThroughPolygon( tw, p, bv, i );
631 // trace trm edges through polygon
632 for ( i = 1; i <= tw->numEdges; i++ ) {
635 idCollisionModelManagerLocal::TranslateTrmEdgeThroughPolygon( tw, p, be);
639 // trace all polygon vertices through the trm
640 for ( i = 0; i < p->numEdges; i++ ) {
641 edgeNum = p->edges[i];
642 e = tw->model->edges + abs(edgeNum);
644 if ( e->checkcount == idCollisionModelManagerLocal::checkCount ) {
647 // set edge check count
648 e->checkcount = idCollisionModelManagerLocal::checkCount;
649 // can never collide with internal edges
653 // got to check both vertices because we skip internal edges
654 for ( k = 0; k < 2; k++ ) {
656 v = tw->model->vertices + e->vertexNum[k ^ INTSIGNBITSET(edgeNum)];
657 // if this vertex is already checked
658 if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) {
661 // set vertex check count
662 v->checkcount = idCollisionModelManagerLocal::checkCount;
664 // if the vertex is outside the trace bounds
665 if ( !tw->bounds.ContainsPoint( v->p ) ) {
669 // vertex end point after movement
670 endp = v->p - tw->dir;
671 // pluecker coordinate for vertex movement vector
672 pl = &tw->polygonVertexPlueckerCache[i+k];
674 for ( j = 0; j < tw->numPolys; j++ ) {
677 idCollisionModelManagerLocal::TranslateVertexThroughTrmPolygon( tw, bp, p, v, endp, *pl );
684 // if there was a collision with this polygon and we are not retrieving contacts
685 if ( tw->trace.fraction < fraction && !tw->getContacts ) {
686 fraction = tw->trace.fraction;
687 endp = tw->start + fraction * tw->dir;
689 for ( i = 0; i < 3; i++ ) {
690 if ( tw->start[i] < endp[i] ) {
691 tw->bounds[0][i] = tw->start[i] + tw->size[0][i] - CM_BOX_EPSILON;
692 tw->bounds[1][i] = endp[i] + tw->size[1][i] + CM_BOX_EPSILON;
695 tw->bounds[0][i] = endp[i] + tw->size[0][i] - CM_BOX_EPSILON;
696 tw->bounds[1][i] = tw->start[i] + tw->size[1][i] + CM_BOX_EPSILON;
701 return ( tw->trace.fraction == 0.0f );
706 idCollisionModelManagerLocal::SetupTrm
709 void idCollisionModelManagerLocal::SetupTrm( cm_traceWork_t *tw, const idTraceModel *trm ) {
713 tw->numVerts = trm->numVerts;
714 for ( i = 0; i < trm->numVerts; i++ ) {
715 tw->vertices[i].p = trm->verts[i];
716 tw->vertices[i].used = false;
719 tw->numEdges = trm->numEdges;
720 for ( i = 1; i <= trm->numEdges; i++ ) {
721 tw->edges[i].vertexNum[0] = trm->edges[i].v[0];
722 tw->edges[i].vertexNum[1] = trm->edges[i].v[1];
723 tw->edges[i].used = false;
726 tw->numPolys = trm->numPolys;
727 for ( i = 0; i < trm->numPolys; i++ ) {
728 tw->polys[i].numEdges = trm->polys[i].numEdges;
729 for ( j = 0; j < trm->polys[i].numEdges; j++ ) {
730 tw->polys[i].edges[j] = trm->polys[i].edges[j];
732 tw->polys[i].plane.SetNormal( trm->polys[i].normal );
733 tw->polys[i].used = false;
735 // is the trace model convex or not
736 tw->isConvex = trm->isConvex;
741 idCollisionModelManagerLocal::SetupTranslationHeartPlanes
744 void idCollisionModelManagerLocal::SetupTranslationHeartPlanes( cm_traceWork_t *tw ) {
745 idVec3 dir, normal1, normal2;
747 // calculate trace heart planes
750 dir.NormalVectors( normal1, normal2 );
751 tw->heartPlane1.SetNormal( normal1 );
752 tw->heartPlane1.FitThroughPoint( tw->start );
753 tw->heartPlane2.SetNormal( normal2 );
754 tw->heartPlane2.FitThroughPoint( tw->start );
759 idCollisionModelManagerLocal::Translation
763 static int entered = 0;
766 void idCollisionModelManagerLocal::Translation( trace_t *results, const idVec3 &start, const idVec3 &end,
767 const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
768 cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
772 bool model_rotated, trm_rotated;
773 idVec3 dir1, dir2, dir;
774 idMat3 invModelAxis, tmpAxis;
775 cm_trmPolygon_t *poly;
777 cm_trmVertex_t *vert;
778 ALIGN16( static cm_traceWork_t tw );
780 assert( ((byte *)&start) < ((byte *)results) || ((byte *)&start) >= (((byte *)results) + sizeof( trace_t )) );
781 assert( ((byte *)&end) < ((byte *)results) || ((byte *)&end) >= (((byte *)results) + sizeof( trace_t )) );
782 assert( ((byte *)&trmAxis) < ((byte *)results) || ((byte *)&trmAxis) >= (((byte *)results) + sizeof( trace_t )) );
784 memset( results, 0, sizeof( *results ) );
786 if ( model < 0 || model > MAX_SUBMODELS || model > idCollisionModelManagerLocal::maxModels ) {
787 common->Printf("idCollisionModelManagerLocal::Translation: invalid model handle\n");
790 if ( !idCollisionModelManagerLocal::models[model] ) {
791 common->Printf("idCollisionModelManagerLocal::Translation: invalid model\n");
795 // if case special position test
796 if ( start[0] == end[0] && start[1] == end[1] && start[2] == end[2] ) {
797 idCollisionModelManagerLocal::ContentsTrm( results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
802 bool startsolid = false;
803 // test whether or not stuck to begin with
804 if ( cm_debugCollision.GetBool() ) {
805 if ( !entered && !idCollisionModelManagerLocal::getContacts ) {
807 // if already messed up to begin with
808 if ( idCollisionModelManagerLocal::Contents( start, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
816 idCollisionModelManagerLocal::checkCount++;
818 tw.trace.fraction = 1.0f;
819 tw.trace.c.contents = 0;
820 tw.trace.c.type = CONTACT_NONE;
821 tw.contents = contentMask;
824 tw.positionTest = false;
825 tw.quickExit = false;
826 tw.getContacts = idCollisionModelManagerLocal::getContacts;
827 tw.contacts = idCollisionModelManagerLocal::contacts;
828 tw.maxContacts = idCollisionModelManagerLocal::maxContacts;
830 tw.model = idCollisionModelManagerLocal::models[model];
831 tw.start = start - modelOrigin;
832 tw.end = end - modelOrigin;
833 tw.dir = end - start;
835 model_rotated = modelAxis.IsRotated();
836 if ( model_rotated ) {
837 invModelAxis = modelAxis.Transpose();
840 // if optimized point trace
841 if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f &&
842 trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f &&
843 trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) {
845 if ( model_rotated ) {
846 // rotate trace instead of model
847 tw.start *= invModelAxis;
848 tw.end *= invModelAxis;
849 tw.dir *= invModelAxis;
853 for ( i = 0; i < 3; i++ ) {
854 if ( tw.start[i] < tw.end[i] ) {
855 tw.bounds[0][i] = tw.start[i] - CM_BOX_EPSILON;
856 tw.bounds[1][i] = tw.end[i] + CM_BOX_EPSILON;
859 tw.bounds[0][i] = tw.end[i] - CM_BOX_EPSILON;
860 tw.bounds[1][i] = tw.start[i] + CM_BOX_EPSILON;
863 tw.extents[0] = tw.extents[1] = tw.extents[2] = CM_BOX_EPSILON;
866 // setup trace heart planes
867 idCollisionModelManagerLocal::SetupTranslationHeartPlanes( &tw );
868 tw.maxDistFromHeartPlane1 = CM_BOX_EPSILON;
869 tw.maxDistFromHeartPlane2 = CM_BOX_EPSILON;
870 // collision with single point
872 tw.vertices[0].p = tw.start;
873 tw.vertices[0].endp = tw.vertices[0].p + tw.dir;
874 tw.vertices[0].pl.FromRay( tw.vertices[0].p, tw.dir );
875 tw.numEdges = tw.numPolys = 0;
876 tw.pointTrace = true;
877 // trace through the model
878 idCollisionModelManagerLocal::TraceThroughModel( &tw );
881 results->endpos = start + results->fraction * (end - start);
882 results->endAxis = mat3_identity;
884 if ( results->fraction < 1.0f ) {
885 // rotate trace plane normal if there was a collision with a rotated model
886 if ( model_rotated ) {
887 results->c.normal *= modelAxis;
888 results->c.point *= modelAxis;
890 results->c.point += modelOrigin;
891 results->c.dist += modelOrigin * results->c.normal;
893 idCollisionModelManagerLocal::numContacts = tw.numContacts;
897 // the trace fraction is too inaccurate to describe translations over huge distances
898 if ( tw.dir.LengthSqr() > Square( CM_MAX_TRACE_DIST ) ) {
899 results->fraction = 0.0f;
900 results->endpos = start;
901 results->endAxis = trmAxis;
902 results->c.normal = vec3_origin;
903 results->c.material = NULL;
904 results->c.point = start;
906 session->rw->DebugArrow( colorRed, start, end, 1 );
908 common->Printf( "idCollisionModelManagerLocal::Translation: huge translation\n" );
912 tw.pointTrace = false;
915 // setup trm structure
916 idCollisionModelManagerLocal::SetupTrm( &tw, trm );
918 trm_rotated = trmAxis.IsRotated();
920 // calculate vertex positions
922 for ( i = 0; i < tw.numVerts; i++ ) {
923 // rotate trm around the start position
924 tw.vertices[i].p *= trmAxis;
927 for ( i = 0; i < tw.numVerts; i++ ) {
928 // set trm at start position
929 tw.vertices[i].p += tw.start;
931 if ( model_rotated ) {
932 for ( i = 0; i < tw.numVerts; i++ ) {
933 // rotate trm around model instead of rotating the model
934 tw.vertices[i].p *= invModelAxis;
938 // add offset to start point
940 dir = trm->offset * trmAxis;
944 tw.start += trm->offset;
945 tw.end += trm->offset;
947 if ( model_rotated ) {
948 // rotate trace instead of model
949 tw.start *= invModelAxis;
950 tw.end *= invModelAxis;
951 tw.dir *= invModelAxis;
954 // rotate trm polygon planes
955 if ( trm_rotated & model_rotated ) {
956 tmpAxis = trmAxis * invModelAxis;
957 for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
958 poly->plane *= tmpAxis;
960 } else if ( trm_rotated ) {
961 for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
962 poly->plane *= trmAxis;
964 } else if ( model_rotated ) {
965 for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
966 poly->plane *= invModelAxis;
970 // setup trm polygons
971 for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
972 // if the trm poly plane is facing in the movement direction
973 dist = poly->plane.Normal() * tw.dir;
974 if ( dist > 0.0f || ( !trm->isConvex && dist == 0.0f ) ) {
975 // this trm poly and it's edges and vertices need to be used for collision
977 for ( j = 0; j < poly->numEdges; j++ ) {
978 edge = &tw.edges[abs( poly->edges[j] )];
980 tw.vertices[edge->vertexNum[0]].used = true;
981 tw.vertices[edge->vertexNum[1]].used = true;
986 // setup trm vertices
987 for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) {
991 // get axial trm size after rotations
992 tw.size.AddPoint( vert->p - tw.start );
993 // calculate the end position of each vertex for a full trace
994 vert->endp = vert->p + tw.dir;
995 // pluecker coordinate for vertex movement line
996 vert->pl.FromRay( vert->p, tw.dir );
1000 for ( edge = tw.edges + 1, i = 1; i <= tw.numEdges; i++, edge++ ) {
1001 if ( !edge->used ) {
1004 // edge start, end and pluecker coordinate
1005 edge->start = tw.vertices[edge->vertexNum[0]].p;
1006 edge->end = tw.vertices[edge->vertexNum[1]].p;
1007 edge->pl.FromLine( edge->start, edge->end );
1008 // calculate normal of plane through movement plane created by the edge
1009 dir = edge->start - edge->end;
1010 edge->cross[0] = dir[0] * tw.dir[1] - dir[1] * tw.dir[0];
1011 edge->cross[1] = dir[0] * tw.dir[2] - dir[2] * tw.dir[0];
1012 edge->cross[2] = dir[1] * tw.dir[2] - dir[2] * tw.dir[1];
1013 // bit for vertex sidedness bit cache
1017 // set trm plane distances
1018 for ( poly = tw.polys, i = 0; i < tw.numPolys; i++, poly++ ) {
1020 poly->plane.FitThroughPoint( tw.edges[abs(poly->edges[0])].start );
1024 // bounds for full trace, a little bit larger for epsilons
1025 for ( i = 0; i < 3; i++ ) {
1026 if ( tw.start[i] < tw.end[i] ) {
1027 tw.bounds[0][i] = tw.start[i] + tw.size[0][i] - CM_BOX_EPSILON;
1028 tw.bounds[1][i] = tw.end[i] + tw.size[1][i] + CM_BOX_EPSILON;
1030 tw.bounds[0][i] = tw.end[i] + tw.size[0][i] - CM_BOX_EPSILON;
1031 tw.bounds[1][i] = tw.start[i] + tw.size[1][i] + CM_BOX_EPSILON;
1033 if ( idMath::Fabs( tw.size[0][i] ) > idMath::Fabs( tw.size[1][i] ) ) {
1034 tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + CM_BOX_EPSILON;
1036 tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + CM_BOX_EPSILON;
1040 // setup trace heart planes
1041 idCollisionModelManagerLocal::SetupTranslationHeartPlanes( &tw );
1042 tw.maxDistFromHeartPlane1 = 0;
1043 tw.maxDistFromHeartPlane2 = 0;
1044 // calculate maximum trm vertex distance from both heart planes
1045 for ( vert = tw.vertices, i = 0; i < tw.numVerts; i++, vert++ ) {
1046 if ( !vert->used ) {
1049 dist = idMath::Fabs( tw.heartPlane1.Distance( vert->p ) );
1050 if ( dist > tw.maxDistFromHeartPlane1 ) {
1051 tw.maxDistFromHeartPlane1 = dist;
1053 dist = idMath::Fabs( tw.heartPlane2.Distance( vert->p ) );
1054 if ( dist > tw.maxDistFromHeartPlane2 ) {
1055 tw.maxDistFromHeartPlane2 = dist;
1059 tw.maxDistFromHeartPlane1 += CM_BOX_EPSILON;
1060 tw.maxDistFromHeartPlane2 += CM_BOX_EPSILON;
1062 // trace through the model
1063 idCollisionModelManagerLocal::TraceThroughModel( &tw );
1065 // if we're getting contacts
1066 if ( tw.getContacts ) {
1067 // move all contacts to world space
1068 if ( model_rotated ) {
1069 for ( i = 0; i < tw.numContacts; i++ ) {
1070 tw.contacts[i].normal *= modelAxis;
1071 tw.contacts[i].point *= modelAxis;
1074 if ( modelOrigin != vec3_origin ) {
1075 for ( i = 0; i < tw.numContacts; i++ ) {
1076 tw.contacts[i].point += modelOrigin;
1077 tw.contacts[i].dist += modelOrigin * tw.contacts[i].normal;
1080 idCollisionModelManagerLocal::numContacts = tw.numContacts;
1083 *results = tw.trace;
1084 results->endpos = start + results->fraction * ( end - start );
1085 results->endAxis = trmAxis;
1087 if ( results->fraction < 1.0f ) {
1088 // if the fraction is tiny the actual movement could end up zero
1089 if ( results->fraction > 0.0f && results->endpos.Compare( start ) ) {
1090 results->fraction = 0.0f;
1092 // rotate trace plane normal if there was a collision with a rotated model
1093 if ( model_rotated ) {
1094 results->c.normal *= modelAxis;
1095 results->c.point *= modelAxis;
1097 results->c.point += modelOrigin;
1098 results->c.dist += modelOrigin * results->c.normal;
1103 // test for missed collisions
1104 if ( cm_debugCollision.GetBool() ) {
1105 if ( !entered && !idCollisionModelManagerLocal::getContacts ) {
1107 // if the trm is stuck in the model
1108 if ( idCollisionModelManagerLocal::Contents( results->endpos, trm, trmAxis, -1, model, modelOrigin, modelAxis ) & contentMask ) {
1111 // test where the trm is stuck in the model
1112 idCollisionModelManagerLocal::Contents( results->endpos, trm, trmAxis, -1, model, modelOrigin, modelAxis );
1113 // re-run collision detection to find out where it failed
1114 idCollisionModelManagerLocal::Translation( &tr, start, end, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );