]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/cm/CollisionModel_contents.cpp
hello world
[icculus/iodoom3.git] / neo / cm / CollisionModel_contents.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
23
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.
25
26 ===========================================================================
27 */
28
29 /*
30 ===============================================================================
31
32         Trace model vs. polygonal model collision detection.
33
34 ===============================================================================
35 */
36
37 #include "../idlib/precompiled.h"
38 #pragma hdrstop
39
40 #include "CollisionModel_local.h"
41
42 /*
43 ===============================================================================
44
45 Contents test
46
47 ===============================================================================
48 */
49
50 /*
51 ================
52 idCollisionModelManagerLocal::TestTrmVertsInBrush
53
54   returns true if any of the trm vertices is inside the brush
55 ================
56 */
57 bool idCollisionModelManagerLocal::TestTrmVertsInBrush( cm_traceWork_t *tw, cm_brush_t *b ) {
58         int i, j, numVerts, bestPlane;
59         float d, bestd;
60         idVec3 *p;
61
62         if ( b->checkcount == idCollisionModelManagerLocal::checkCount ) {
63                 return false;
64         }
65         b->checkcount = idCollisionModelManagerLocal::checkCount;
66
67         if ( !(b->contents & tw->contents) ) {
68                 return false;
69         }
70
71         // if the brush bounds don't intersect the trace bounds
72         if ( !b->bounds.IntersectsBounds( tw->bounds ) ) {
73                 return false;
74         }
75
76         if ( tw->pointTrace ) {
77                 numVerts = 1;
78         }
79         else {
80                 numVerts = tw->numVerts;
81         }
82
83         for ( j = 0; j < numVerts; j++ ) {
84                 p = &tw->vertices[j].p;
85
86                 // see if the point is inside the brush
87                 bestPlane = 0;
88                 bestd = -idMath::INFINITY;
89                 for ( i = 0; i < b->numPlanes; i++ ) {
90                         d = b->planes[i].Distance( *p );
91                         if ( d >= 0.0f ) {
92                                 break;
93                         }
94                         if ( d > bestd ) {
95                                 bestd = d;
96                                 bestPlane = i;
97                         }
98                 }
99                 if ( i >= b->numPlanes ) {
100                         tw->trace.fraction = 0.0f;
101                         tw->trace.c.type = CONTACT_TRMVERTEX;
102                         tw->trace.c.normal = b->planes[bestPlane].Normal();
103                         tw->trace.c.dist = b->planes[bestPlane].Dist();
104                         tw->trace.c.contents = b->contents;
105                         tw->trace.c.material = b->material;
106                         tw->trace.c.point = *p;
107                         tw->trace.c.modelFeature = 0;
108                         tw->trace.c.trmFeature = j;
109                         return true;
110                 }
111         }
112         return false;
113 }
114
115 /*
116 ================
117 CM_SetTrmEdgeSidedness
118 ================
119 */
120 #define CM_SetTrmEdgeSidedness( edge, bpl, epl, bitNum ) {                                                      \
121         if ( !(edge->sideSet & (1<<bitNum)) ) {                                                                                 \
122                 float fl;                                                                                                                                       \
123                 fl = (bpl).PermutedInnerProduct( epl );                                                                         \
124                 edge->side = (edge->side & ~(1<<bitNum)) | (FLOATSIGNBITSET(fl) << bitNum);     \
125                 edge->sideSet |= (1 << bitNum);                                                                                         \
126         }                                                                                                                                                               \
127 }
128
129 /*
130 ================
131 CM_SetTrmPolygonSidedness
132 ================
133 */
134 #define CM_SetTrmPolygonSidedness( v, plane, bitNum ) {                                                         \
135         if ( !((v)->sideSet & (1<<bitNum)) ) {                                                                                  \
136                 float fl;                                                                                                                                       \
137                 fl = plane.Distance( (v)->p );                                                                                          \
138                 /* cannot use float sign bit because it is undetermined when fl == 0.0f */      \
139                 if ( fl < 0.0f ) {                                                                                                                      \
140                         (v)->side |= (1 << bitNum);                                                                                             \
141                 }                                                                                                                                                       \
142                 else {                                                                                                                                          \
143                         (v)->side &= ~(1 << bitNum);                                                                                    \
144                 }                                                                                                                                                       \
145                 (v)->sideSet |= (1 << bitNum);                                                                                          \
146         }                                                                                                                                                               \
147 }
148
149 /*
150 ================
151 idCollisionModelManagerLocal::TestTrmInPolygon
152
153   returns true if the trm intersects the polygon
154 ================
155 */
156 bool idCollisionModelManagerLocal::TestTrmInPolygon( cm_traceWork_t *tw, cm_polygon_t *p ) {
157         int i, j, k, edgeNum, flip, trmEdgeNum, bitNum, bestPlane;
158         int sides[MAX_TRACEMODEL_VERTS];
159         float d, bestd;
160         cm_trmEdge_t *trmEdge;
161         cm_edge_t *edge;
162         cm_vertex_t *v, *v1, *v2;
163
164         // if already checked this polygon
165         if ( p->checkcount == idCollisionModelManagerLocal::checkCount ) {
166                 return false;
167         }
168         p->checkcount = idCollisionModelManagerLocal::checkCount;
169
170         // if this polygon does not have the right contents behind it
171         if ( !(p->contents & tw->contents) ) {
172                 return false;
173         }
174
175         // if the polygon bounds don't intersect the trace bounds
176         if ( !p->bounds.IntersectsBounds( tw->bounds ) ) {
177                 return false;
178         }
179
180         // bounds should cross polygon plane
181         switch( tw->bounds.PlaneSide( p->plane ) ) {
182                 case PLANESIDE_CROSS:
183                         break;
184                 case PLANESIDE_FRONT:
185                         if ( tw->model->isConvex ) {
186                                 tw->quickExit = true;
187                                 return true;
188                         }
189                 default:
190                         return false;
191         }
192
193         // if the trace model is convex
194         if ( tw->isConvex ) {
195                 // test if any polygon vertices are inside the trm
196                 for ( i = 0; i < p->numEdges; i++ ) {
197                         edgeNum = p->edges[i];
198                         edge = tw->model->edges + abs(edgeNum);
199                         // if this edge is already tested
200                         if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
201                                 continue;
202                         }
203
204                         for ( j = 0; j < 2; j++ ) {
205                                 v = &tw->model->vertices[edge->vertexNum[j]];
206                                 // if this vertex is already tested
207                                 if ( v->checkcount == idCollisionModelManagerLocal::checkCount ) {
208                                         continue;
209                                 }
210
211                                 bestPlane = 0;
212                                 bestd = -idMath::INFINITY;
213                                 for ( k = 0; k < tw->numPolys; k++ ) {
214                                         d = tw->polys[k].plane.Distance( v->p );
215                                         if ( d >= 0.0f ) {
216                                                 break;
217                                         }
218                                         if ( d > bestd ) {
219                                                 bestd = d;
220                                                 bestPlane = k;
221                                         }
222                                 }
223                                 if ( k >= tw->numPolys ) {
224                                         tw->trace.fraction = 0.0f;
225                                         tw->trace.c.type = CONTACT_MODELVERTEX;
226                                         tw->trace.c.normal = -tw->polys[bestPlane].plane.Normal();
227                                         tw->trace.c.dist = -tw->polys[bestPlane].plane.Dist();
228                                         tw->trace.c.contents = p->contents;
229                                         tw->trace.c.material = p->material;
230                                         tw->trace.c.point = v->p;
231                                         tw->trace.c.modelFeature = edge->vertexNum[j];
232                                         tw->trace.c.trmFeature = 0;
233                                         return true;
234                                 }
235                         }
236                 }
237         }
238
239         for ( i = 0; i < p->numEdges; i++ ) {
240                 edgeNum = p->edges[i];
241                 edge = tw->model->edges + abs(edgeNum);
242                 // reset sidedness cache if this is the first time we encounter this edge
243                 if ( edge->checkcount != idCollisionModelManagerLocal::checkCount ) {
244                         edge->sideSet = 0;
245                 }
246                 // pluecker coordinate for edge
247                 tw->polygonEdgePlueckerCache[i].FromLine( tw->model->vertices[edge->vertexNum[0]].p,
248                                                                                                         tw->model->vertices[edge->vertexNum[1]].p );
249                 v = &tw->model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]];
250                 // reset sidedness cache if this is the first time we encounter this vertex
251                 if ( v->checkcount != idCollisionModelManagerLocal::checkCount ) {
252                         v->sideSet = 0;
253                 }
254                 v->checkcount = idCollisionModelManagerLocal::checkCount;
255         }
256
257         // get side of polygon for each trm vertex
258         for ( i = 0; i < tw->numVerts; i++ ) {
259                 d = p->plane.Distance( tw->vertices[i].p );
260                 sides[i] = d < 0.0f ? -1 : 1;
261         }
262
263         // test if any trm edges go through the polygon
264         for ( i = 1; i <= tw->numEdges; i++ ) {
265                 // if the trm edge does not cross the polygon plane
266                 if ( sides[tw->edges[i].vertexNum[0]] == sides[tw->edges[i].vertexNum[1]] ) {
267                         continue;
268                 }
269                 // check from which side to which side the trm edge goes
270                 flip = INTSIGNBITSET( sides[tw->edges[i].vertexNum[0]] );
271                 // test if trm edge goes through the polygon between the polygon edges
272                 for ( j = 0; j < p->numEdges; j++ ) {
273                         edgeNum = p->edges[j];
274                         edge = tw->model->edges + abs(edgeNum);
275 #if 1
276                         CM_SetTrmEdgeSidedness( edge, tw->edges[i].pl, tw->polygonEdgePlueckerCache[j], i );
277                         if ( INTSIGNBITSET(edgeNum) ^ ((edge->side >> i) & 1) ^ flip ) {
278                                 break;
279                         }
280 #else
281                         d = tw->edges[i].pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[j] );
282                         if ( flip ) {
283                                 d = -d;
284                         }
285                         if ( edgeNum > 0 ) {
286                                 if ( d <= 0.0f ) {
287                                         break;
288                                 }
289                         }
290                         else {
291                                 if ( d >= 0.0f ) {
292                                         break;
293                                 }
294                         }
295 #endif
296                 }
297                 if ( j >= p->numEdges ) {
298                         tw->trace.fraction = 0.0f;
299                         tw->trace.c.type = CONTACT_EDGE;
300                         tw->trace.c.normal = p->plane.Normal();
301                         tw->trace.c.dist = p->plane.Dist();
302                         tw->trace.c.contents = p->contents;
303                         tw->trace.c.material = p->material;
304                         tw->trace.c.point = tw->vertices[tw->edges[i].vertexNum[ !flip ]].p;
305                         tw->trace.c.modelFeature = *reinterpret_cast<int *>(&p);
306                         tw->trace.c.trmFeature = i;
307                         return true;
308                 }
309         }
310
311         // test if any polygon edges go through the trm polygons
312         for ( i = 0; i < p->numEdges; i++ ) {
313                 edgeNum = p->edges[i];
314                 edge = tw->model->edges + abs(edgeNum);
315                 if ( edge->checkcount == idCollisionModelManagerLocal::checkCount ) {
316                         continue;
317                 }
318                 edge->checkcount = idCollisionModelManagerLocal::checkCount;
319
320                 for ( j = 0; j < tw->numPolys; j++ ) {
321 #if 1
322                         v1 = tw->model->vertices + edge->vertexNum[0];
323                         CM_SetTrmPolygonSidedness( v1, tw->polys[j].plane, j );
324                         v2 = tw->model->vertices + edge->vertexNum[1];
325                         CM_SetTrmPolygonSidedness( v2, tw->polys[j].plane, j );
326                         // if the polygon edge does not cross the trm polygon plane
327                         if ( !(((v1->side ^ v2->side) >> j) & 1) ) {
328                                 continue;
329                         }
330                         flip = (v1->side >> j) & 1;
331 #else
332                         float d1, d2;
333
334                         v1 = tw->model->vertices + edge->vertexNum[0];
335                         d1 = tw->polys[j].plane.Distance( v1->p );
336                         v2 = tw->model->vertices + edge->vertexNum[1];
337                         d2 = tw->polys[j].plane.Distance( v2->p );
338                         // if the polygon edge does not cross the trm polygon plane
339                         if ( (d1 >= 0.0f && d2 >= 0.0f) || (d1 <= 0.0f && d2 <= 0.0f) ) {
340                                 continue;
341                         }
342                         flip = false;
343                         if ( d1 < 0.0f ) {
344                                 flip = true;
345                         }
346 #endif
347                         // test if polygon edge goes through the trm polygon between the trm polygon edges
348                         for ( k = 0; k < tw->polys[j].numEdges; k++ ) {
349                                 trmEdgeNum = tw->polys[j].edges[k];
350                                 trmEdge = tw->edges + abs(trmEdgeNum);
351 #if 1
352                                 bitNum = abs(trmEdgeNum);
353                                 CM_SetTrmEdgeSidedness( edge, trmEdge->pl, tw->polygonEdgePlueckerCache[i], bitNum );
354                                 if ( INTSIGNBITSET(trmEdgeNum) ^ ((edge->side >> bitNum) & 1) ^ flip ) {
355                                         break;
356                                 }
357 #else
358                                 d = trmEdge->pl.PermutedInnerProduct( tw->polygonEdgePlueckerCache[i] );
359                                 if ( flip ) {
360                                         d = -d;
361                                 }
362                                 if ( trmEdgeNum > 0 ) {
363                                         if ( d <= 0.0f ) {
364                                                 break;
365                                         }
366                                 }
367                                 else {
368                                         if ( d >= 0.0f ) {
369                                                 break;
370                                         }
371                                 }
372 #endif
373                         }
374                         if ( k >= tw->polys[j].numEdges ) {
375                                 tw->trace.fraction = 0.0f;
376                                 tw->trace.c.type = CONTACT_EDGE;
377                                 tw->trace.c.normal = -tw->polys[j].plane.Normal();
378                                 tw->trace.c.dist = -tw->polys[j].plane.Dist();
379                                 tw->trace.c.contents = p->contents;
380                                 tw->trace.c.material = p->material;
381                                 tw->trace.c.point = tw->model->vertices[edge->vertexNum[ !flip ]].p;
382                                 tw->trace.c.modelFeature = edgeNum;
383                                 tw->trace.c.trmFeature = j;
384                                 return true;
385                         }
386                 }
387         }
388         return false;
389 }
390
391 /*
392 ================
393 idCollisionModelManagerLocal::PointNode
394 ================
395 */
396 cm_node_t *idCollisionModelManagerLocal::PointNode( const idVec3 &p, cm_model_t *model ) {
397         cm_node_t *node;
398
399         node = model->node;
400         while ( node->planeType != -1 ) {
401                 if (p[node->planeType] > node->planeDist) {
402                         node = node->children[0];
403                 }
404                 else {
405                         node = node->children[1];
406                 }
407
408                 assert( node != NULL );
409         }
410         return node;
411 }
412
413 /*
414 ================
415 idCollisionModelManagerLocal::PointContents
416 ================
417 */
418 int idCollisionModelManagerLocal::PointContents( const idVec3 p, cmHandle_t model ) {
419         int i;
420         float d;
421         cm_node_t *node;
422         cm_brushRef_t *bref;
423         cm_brush_t *b;
424         idPlane *plane;
425
426         node = idCollisionModelManagerLocal::PointNode( p, idCollisionModelManagerLocal::models[model] );
427         for ( bref = node->brushes; bref; bref = bref->next ) {
428                 b = bref->b;
429                 // test if the point is within the brush bounds
430                 for ( i = 0; i < 3; i++ ) {
431                         if ( p[i] < b->bounds[0][i] ) {
432                                 break;
433                         }
434                         if ( p[i] > b->bounds[1][i] ) {
435                                 break;
436                         }
437                 }
438                 if ( i < 3 ) {
439                         continue;
440                 }
441                 // test if the point is inside the brush
442                 plane = b->planes;
443                 for ( i = 0; i < b->numPlanes; i++, plane++ ) {
444                         d = plane->Distance( p );
445                         if ( d >= 0 ) {
446                                 break;
447                         }
448                 }
449                 if ( i >= b->numPlanes ) {
450                         return b->contents;
451                 }
452         }
453         return 0;
454 }
455
456 /*
457 ==================
458 idCollisionModelManagerLocal::TransformedPointContents
459 ==================
460 */
461 int     idCollisionModelManagerLocal::TransformedPointContents( const idVec3 &p, cmHandle_t model, const idVec3 &origin, const idMat3 &modelAxis ) {
462         idVec3 p_l;
463
464         // subtract origin offset
465         p_l = p - origin;
466         if ( modelAxis.IsRotated() ) {
467                 p_l *= modelAxis;
468         }
469         return idCollisionModelManagerLocal::PointContents( p_l, model );
470 }
471
472
473 /*
474 ==================
475 idCollisionModelManagerLocal::ContentsTrm
476 ==================
477 */
478 int idCollisionModelManagerLocal::ContentsTrm( trace_t *results, const idVec3 &start,
479                                                                         const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
480                                                                         cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
481         int i;
482         bool model_rotated, trm_rotated;
483         idMat3 invModelAxis, tmpAxis;
484         idVec3 dir;
485         ALIGN16( cm_traceWork_t tw );
486
487         // fast point case
488         if ( !trm || ( trm->bounds[1][0] - trm->bounds[0][0] <= 0.0f &&
489                                         trm->bounds[1][1] - trm->bounds[0][1] <= 0.0f &&
490                                         trm->bounds[1][2] - trm->bounds[0][2] <= 0.0f ) ) {
491
492                 results->c.contents = idCollisionModelManagerLocal::TransformedPointContents( start, model, modelOrigin, modelAxis );
493                 results->fraction = ( results->c.contents == 0 );
494                 results->endpos = start;
495                 results->endAxis = trmAxis;
496
497                 return results->c.contents;
498         }
499
500         idCollisionModelManagerLocal::checkCount++;
501
502         tw.trace.fraction = 1.0f;
503         tw.trace.c.contents = 0;
504         tw.trace.c.type = CONTACT_NONE;
505         tw.contents = contentMask;
506         tw.isConvex = true;
507         tw.rotation = false;
508         tw.positionTest = true;
509         tw.pointTrace = false;
510         tw.quickExit = false;
511         tw.numContacts = 0;
512         tw.model = idCollisionModelManagerLocal::models[model];
513         tw.start = start - modelOrigin;
514         tw.end = tw.start;
515
516         model_rotated = modelAxis.IsRotated();
517         if ( model_rotated ) {
518                 invModelAxis = modelAxis.Transpose();
519         }
520
521         // setup trm structure
522         idCollisionModelManagerLocal::SetupTrm( &tw, trm );
523
524         trm_rotated = trmAxis.IsRotated();
525
526         // calculate vertex positions
527         if ( trm_rotated ) {
528                 for ( i = 0; i < tw.numVerts; i++ ) {
529                         // rotate trm around the start position
530                         tw.vertices[i].p *= trmAxis;
531                 }
532         }
533         for ( i = 0; i < tw.numVerts; i++ ) {
534                 // set trm at start position
535                 tw.vertices[i].p += tw.start;
536         }
537         if ( model_rotated ) {
538                 for ( i = 0; i < tw.numVerts; i++ ) {
539                         // rotate trm around model instead of rotating the model
540                         tw.vertices[i].p *= invModelAxis;
541                 }
542         }
543
544         // add offset to start point
545         if ( trm_rotated ) {
546                 dir = trm->offset * trmAxis;
547                 tw.start += dir;
548                 tw.end += dir;
549         } else {
550                 tw.start += trm->offset;
551                 tw.end += trm->offset;
552         }
553         if ( model_rotated ) {
554                 // rotate trace instead of model
555                 tw.start *= invModelAxis;
556                 tw.end *= invModelAxis;
557         }
558
559
560         // setup trm vertices
561         tw.size.Clear();
562         for ( i = 0; i < tw.numVerts; i++ ) {
563                 // get axial trm size after rotations
564                 tw.size.AddPoint( tw.vertices[i].p - tw.start );
565         }
566
567         // setup trm edges
568         for ( i = 1; i <= tw.numEdges; i++ ) {
569                 // edge start, end and pluecker coordinate
570                 tw.edges[i].start = tw.vertices[tw.edges[i].vertexNum[0]].p;
571                 tw.edges[i].end = tw.vertices[tw.edges[i].vertexNum[1]].p;
572                 tw.edges[i].pl.FromLine( tw.edges[i].start, tw.edges[i].end );
573         }
574
575         // setup trm polygons
576         if ( trm_rotated & model_rotated ) {
577                 tmpAxis = trmAxis * invModelAxis;
578                 for ( i = 0; i < tw.numPolys; i++ ) {
579                         tw.polys[i].plane *= tmpAxis;
580                 }
581         } else if ( trm_rotated ) {
582                 for ( i = 0; i < tw.numPolys; i++ ) {
583                         tw.polys[i].plane *= trmAxis;
584                 }
585         } else if ( model_rotated ) {
586                 for ( i = 0; i < tw.numPolys; i++ ) {
587                         tw.polys[i].plane *= invModelAxis;
588                 }
589         }
590         for ( i = 0; i < tw.numPolys; i++ ) {
591                 tw.polys[i].plane.FitThroughPoint( tw.edges[abs(tw.polys[i].edges[0])].start );
592         }
593
594         // bounds for full trace, a little bit larger for epsilons
595         for ( i = 0; i < 3; i++ ) {
596                 if ( tw.start[i] < tw.end[i] ) {
597                         tw.bounds[0][i] = tw.start[i] + tw.size[0][i] - CM_BOX_EPSILON;
598                         tw.bounds[1][i] = tw.end[i] + tw.size[1][i] + CM_BOX_EPSILON;
599                 } else {
600                         tw.bounds[0][i] = tw.end[i] + tw.size[0][i] - CM_BOX_EPSILON;
601                         tw.bounds[1][i] = tw.start[i] + tw.size[1][i] + CM_BOX_EPSILON;
602                 }
603                 if ( idMath::Fabs(tw.size[0][i]) > idMath::Fabs(tw.size[1][i]) ) {
604                         tw.extents[i] = idMath::Fabs( tw.size[0][i] ) + CM_BOX_EPSILON;
605                 } else {
606                         tw.extents[i] = idMath::Fabs( tw.size[1][i] ) + CM_BOX_EPSILON;
607                 }
608         }
609
610         // trace through the model
611         idCollisionModelManagerLocal::TraceThroughModel( &tw );
612
613         *results = tw.trace;
614         results->fraction = ( results->c.contents == 0 );
615         results->endpos = start;
616         results->endAxis = trmAxis;
617
618         return results->c.contents;
619 }
620
621 /*
622 ==================
623 idCollisionModelManagerLocal::Contents
624 ==================
625 */
626 int idCollisionModelManagerLocal::Contents( const idVec3 &start,
627                                                                         const idTraceModel *trm, const idMat3 &trmAxis, int contentMask,
628                                                                         cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) {
629         trace_t results;
630
631         if ( model < 0 || model > idCollisionModelManagerLocal::maxModels || model > MAX_SUBMODELS ) {
632                 common->Printf("idCollisionModelManagerLocal::Contents: invalid model handle\n");
633                 return 0;
634         }
635         if ( !idCollisionModelManagerLocal::models || !idCollisionModelManagerLocal::models[model] ) {
636                 common->Printf("idCollisionModelManagerLocal::Contents: invalid model\n");
637                 return 0;
638         }
639
640         return ContentsTrm( &results, start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis );
641 }