doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CollisionModel_load.cpp
Go to the documentation of this file.
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  It is more important to minimize the number of collision polygons
35  than it is to minimize the number of edges used for collision
36  detection (total edges - internal edges).
37 
38  Stitching the world tends to minimize the number of edges used
39  for collision detection (more internal edges). However stitching
40  also results in more collision polygons which usually makes a
41  stitched world slower.
42 
43  In an average map over 30% of all edges is internal.
44 
45 ===============================================================================
46 */
47 
48 #include "../idlib/precompiled.h"
49 #pragma hdrstop
50 
51 #include "CollisionModel_local.h"
52 
53 
56 
60 
63 
66 
67 
68 /*
69 ===============================================================================
70 
71 Proc BSP tree for data pruning
72 
73 ===============================================================================
74 */
75 
76 /*
77 ================
78 idCollisionModelManagerLocal::ParseProcNodes
79 ================
80 */
82  int i;
83 
84  src->ExpectTokenString( "{" );
85 
86  numProcNodes = src->ParseInt();
87  if ( numProcNodes < 0 ) {
88  src->Error( "ParseProcNodes: bad numProcNodes" );
89  }
91 
92  for ( i = 0; i < numProcNodes; i++ ) {
93  cm_procNode_t *node;
94 
95  node = &procNodes[i];
96 
97  src->Parse1DMatrix( 4, node->plane.ToFloatPtr() );
98  node->children[0] = src->ParseInt();
99  node->children[1] = src->ParseInt();
100  }
101 
102  src->ExpectTokenString( "}" );
103 }
104 
105 /*
106 ================
107 idCollisionModelManagerLocal::LoadProcBSP
108 
109  FIXME: if the nodes would be at the start of the .proc file it would speed things up considerably
110 ================
111 */
113  idStr filename;
114  idToken token;
115  idLexer *src;
116 
117  // load it
118  filename = name;
119  filename.SetFileExtension( PROC_FILE_EXT );
120  src = new idLexer( filename, LEXFL_NOSTRINGCONCAT | LEXFL_NODOLLARPRECOMPILE );
121  if ( !src->IsLoaded() ) {
122  common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: couldn't load %s", filename.c_str() );
123  delete src;
124  return;
125  }
126 
127  if ( !src->ReadToken( &token ) || token.Icmp( PROC_FILE_ID ) ) {
128  common->Warning( "idCollisionModelManagerLocal::LoadProcBSP: bad id '%s' instead of '%s'", token.c_str(), PROC_FILE_ID );
129  delete src;
130  return;
131  }
132 
133  // parse the file
134  while ( 1 ) {
135  if ( !src->ReadToken( &token ) ) {
136  break;
137  }
138 
139  if ( token == "model" ) {
140  src->SkipBracedSection();
141  continue;
142  }
143 
144  if ( token == "shadowModel" ) {
145  src->SkipBracedSection();
146  continue;
147  }
148 
149  if ( token == "interAreaPortals" ) {
150  src->SkipBracedSection();
151  continue;
152  }
153 
154  if ( token == "nodes" ) {
155  ParseProcNodes( src );
156  break;
157  }
158 
159  src->Error( "idCollisionModelManagerLocal::LoadProcBSP: bad token \"%s\"", token.c_str() );
160  }
161 
162  delete src;
163 }
164 
165 /*
166 ===============================================================================
167 
168 Free map
169 
170 ===============================================================================
171 */
172 
173 /*
174 ================
175 idCollisionModelManagerLocal::Clear
176 ================
177 */
179  mapName.Clear();
180  mapFileTime = 0;
181  loaded = 0;
182  checkCount = 0;
183  maxModels = 0;
184  numModels = 0;
185  models = NULL;
186  memset( trmPolygons, 0, sizeof( trmPolygons ) );
187  trmBrushes[0] = NULL;
188  trmMaterial = NULL;
189  numProcNodes = 0;
190  procNodes = NULL;
191  getContacts = false;
192  contacts = NULL;
193  maxContacts = 0;
194  numContacts = 0;
195 }
196 
197 /*
198 ================
199 idCollisionModelManagerLocal::RemovePolygonReferences_r
200 ================
201 */
203  cm_polygonRef_t *pref;
204 
205  while( node ) {
206  for ( pref = node->polygons; pref; pref = pref->next ) {
207  if ( pref->p == p ) {
208  pref->p = NULL;
209  // cannot return here because we can have links down the tree due to polygon merging
210  //return;
211  }
212  }
213  // if leaf node
214  if ( node->planeType == -1 ) {
215  break;
216  }
217  if ( p->bounds[0][node->planeType] > node->planeDist ) {
218  node = node->children[0];
219  }
220  else if ( p->bounds[1][node->planeType] < node->planeDist ) {
221  node = node->children[1];
222  }
223  else {
224  RemovePolygonReferences_r( node->children[1], p );
225  node = node->children[0];
226  }
227  }
228 }
229 
230 /*
231 ================
232 idCollisionModelManagerLocal::RemoveBrushReferences_r
233 ================
234 */
236  cm_brushRef_t *bref;
237 
238  while( node ) {
239  for ( bref = node->brushes; bref; bref = bref->next ) {
240  if ( bref->b == b ) {
241  bref->b = NULL;
242  return;
243  }
244  }
245  // if leaf node
246  if ( node->planeType == -1 ) {
247  break;
248  }
249  if ( b->bounds[0][node->planeType] > node->planeDist ) {
250  node = node->children[0];
251  }
252  else if ( b->bounds[1][node->planeType] < node->planeDist ) {
253  node = node->children[1];
254  }
255  else {
256  RemoveBrushReferences_r( node->children[1], b );
257  node = node->children[0];
258  }
259  }
260 }
261 
262 /*
263 ================
264 idCollisionModelManagerLocal::FreeNode
265 ================
266 */
268  // don't free the node here
269  // the nodes are allocated in blocks which are freed when the model is freed
270 }
271 
272 /*
273 ================
274 idCollisionModelManagerLocal::FreePolygonReference
275 ================
276 */
278  // don't free the polygon reference here
279  // the polygon references are allocated in blocks which are freed when the model is freed
280 }
281 
282 /*
283 ================
284 idCollisionModelManagerLocal::FreeBrushReference
285 ================
286 */
288  // don't free the brush reference here
289  // the brush references are allocated in blocks which are freed when the model is freed
290 }
291 
292 /*
293 ================
294 idCollisionModelManagerLocal::FreePolygon
295 ================
296 */
298  model->numPolygons--;
299  model->polygonMemory -= sizeof( cm_polygon_t ) + ( poly->numEdges - 1 ) * sizeof( poly->edges[0] );
300  if ( model->polygonBlock == NULL ) {
301  Mem_Free( poly );
302  }
303 }
304 
305 /*
306 ================
307 idCollisionModelManagerLocal::FreeBrush
308 ================
309 */
311  model->numBrushes--;
312  model->brushMemory -= sizeof( cm_brush_t ) + ( brush->numPlanes - 1 ) * sizeof( brush->planes[0] );
313  if ( model->brushBlock == NULL ) {
314  Mem_Free( brush );
315  }
316 }
317 
318 /*
319 ================
320 idCollisionModelManagerLocal::FreeTree_r
321 ================
322 */
324  cm_polygonRef_t *pref;
325  cm_polygon_t *p;
326  cm_brushRef_t *bref;
327  cm_brush_t *b;
328 
329  // free all polygons at this node
330  for ( pref = node->polygons; pref; pref = node->polygons ) {
331  p = pref->p;
332  if ( p ) {
333  // remove all other references to this polygon
334  RemovePolygonReferences_r( headNode, p );
335  FreePolygon( model, p );
336  }
337  node->polygons = pref->next;
338  FreePolygonReference( pref );
339  }
340  // free all brushes at this node
341  for ( bref = node->brushes; bref; bref = node->brushes ) {
342  b = bref->b;
343  if ( b ) {
344  // remove all other references to this brush
345  RemoveBrushReferences_r( headNode, b );
346  FreeBrush( model, b );
347  }
348  node->brushes = bref->next;
349  FreeBrushReference( bref );
350  }
351  // recurse down the tree
352  if ( node->planeType != -1 ) {
353  FreeTree_r( model, headNode, node->children[0] );
354  node->children[0] = NULL;
355  FreeTree_r( model, headNode, node->children[1] );
356  node->children[1] = NULL;
357  }
358  FreeNode( node );
359 }
360 
361 /*
362 ================
363 idCollisionModelManagerLocal::FreeModel
364 ================
365 */
367  cm_polygonRefBlock_t *polygonRefBlock, *nextPolygonRefBlock;
368  cm_brushRefBlock_t *brushRefBlock, *nextBrushRefBlock;
369  cm_nodeBlock_t *nodeBlock, *nextNodeBlock;
370 
371  // free the tree structure
372  if ( model->node ) {
373  FreeTree_r( model, model->node, model->node );
374  }
375  // free blocks with polygon references
376  for ( polygonRefBlock = model->polygonRefBlocks; polygonRefBlock; polygonRefBlock = nextPolygonRefBlock ) {
377  nextPolygonRefBlock = polygonRefBlock->next;
378  Mem_Free( polygonRefBlock );
379  }
380  // free blocks with brush references
381  for ( brushRefBlock = model->brushRefBlocks; brushRefBlock; brushRefBlock = nextBrushRefBlock ) {
382  nextBrushRefBlock = brushRefBlock->next;
383  Mem_Free( brushRefBlock );
384  }
385  // free blocks with nodes
386  for ( nodeBlock = model->nodeBlocks; nodeBlock; nodeBlock = nextNodeBlock ) {
387  nextNodeBlock = nodeBlock->next;
388  Mem_Free( nodeBlock );
389  }
390  // free block allocated polygons
391  Mem_Free( model->polygonBlock );
392  // free block allocated brushes
393  Mem_Free( model->brushBlock );
394  // free edges
395  Mem_Free( model->edges );
396  // free vertices
397  Mem_Free( model->vertices );
398  // free the model
399  delete model;
400 }
401 
402 /*
403 ================
404 idCollisionModelManagerLocal::FreeMap
405 ================
406 */
408  int i;
409 
410  if ( !loaded ) {
411  Clear();
412  return;
413  }
414 
415  for ( i = 0; i < maxModels; i++ ) {
416  if ( !models[i] ) {
417  continue;
418  }
419  FreeModel( models[i] );
420  }
421 
423 
424  Mem_Free( models );
425 
426  Clear();
427 
428  ShutdownHash();
429 }
430 
431 /*
432 ================
433 idCollisionModelManagerLocal::FreeTrmModelStructure
434 ================
435 */
437  int i;
438 
439  assert( models );
440  if ( !models[MAX_SUBMODELS] ) {
441  return;
442  }
443 
444  for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) {
445  FreePolygon( models[MAX_SUBMODELS], trmPolygons[i]->p );
446  }
447  FreeBrush( models[MAX_SUBMODELS], trmBrushes[0]->b );
448 
451  FreeModel( models[MAX_SUBMODELS] );
452 }
453 
454 
455 /*
456 ===============================================================================
457 
458 Edge normals
459 
460 ===============================================================================
461 */
462 
463 /*
464 ================
465 idCollisionModelManagerLocal::CalculateEdgeNormals
466 ================
467 */
468 #define SHARP_EDGE_DOT -0.7f
469 
471  cm_polygonRef_t *pref;
472  cm_polygon_t *p;
473  cm_edge_t *edge;
474  float dot, s;
475  int i, edgeNum;
476  idVec3 dir;
477 
478  while( 1 ) {
479  for ( pref = node->polygons; pref; pref = pref->next ) {
480  p = pref->p;
481  // if we checked this polygon already
482  if ( p->checkcount == checkCount ) {
483  continue;
484  }
485  p->checkcount = checkCount;
486 
487  for ( i = 0; i < p->numEdges; i++ ) {
488  edgeNum = p->edges[i];
489  edge = model->edges + abs( edgeNum );
490  if ( edge->normal[0] == 0.0f && edge->normal[1] == 0.0f && edge->normal[2] == 0.0f ) {
491  // if the edge is only used by this polygon
492  if ( edge->numUsers == 1 ) {
493  dir = model->vertices[ edge->vertexNum[edgeNum < 0]].p - model->vertices[ edge->vertexNum[edgeNum > 0]].p;
494  edge->normal = p->plane.Normal().Cross( dir );
495  edge->normal.Normalize();
496  } else {
497  // the edge is used by more than one polygon
498  edge->normal = p->plane.Normal();
499  }
500  } else {
501  dot = edge->normal * p->plane.Normal();
502  // if the two planes make a very sharp edge
503  if ( dot < SHARP_EDGE_DOT ) {
504  // max length normal pointing outside both polygons
505  dir = model->vertices[ edge->vertexNum[edgeNum > 0]].p - model->vertices[ edge->vertexNum[edgeNum < 0]].p;
506  edge->normal = edge->normal.Cross( dir ) + p->plane.Normal().Cross( -dir );
507  edge->normal *= ( 0.5f / ( 0.5f + 0.5f * SHARP_EDGE_DOT ) ) / edge->normal.Length();
508  model->numSharpEdges++;
509  } else {
510  s = 0.5f / ( 0.5f + 0.5f * dot );
511  edge->normal = s * ( edge->normal + p->plane.Normal() );
512  }
513  }
514  }
515  }
516  // if leaf node
517  if ( node->planeType == -1 ) {
518  break;
519  }
520  CalculateEdgeNormals( model, node->children[1] );
521  node = node->children[0];
522  }
523 }
524 
525 /*
526 ===============================================================================
527 
528 Trace model to general collision model
529 
530 ===============================================================================
531 */
532 
533 /*
534 ================
535 idCollisionModelManagerLocal::AllocModel
536 ================
537 */
539  cm_model_t *model;
540 
541  model = new cm_model_t;
542  model->contents = 0;
543  model->isConvex = false;
544  model->maxVertices = 0;
545  model->numVertices = 0;
546  model->vertices = NULL;
547  model->maxEdges = 0;
548  model->numEdges = 0;
549  model->edges= NULL;
550  model->node = NULL;
551  model->nodeBlocks = NULL;
552  model->polygonRefBlocks = NULL;
553  model->brushRefBlocks = NULL;
554  model->polygonBlock = NULL;
555  model->brushBlock = NULL;
556  model->numPolygons = model->polygonMemory =
557  model->numBrushes = model->brushMemory =
558  model->numNodes = model->numBrushRefs =
559  model->numPolygonRefs = model->numInternalEdges =
560  model->numSharpEdges = model->numRemovedPolys =
561  model->numMergedPolys = model->usedMemory = 0;
562 
563  return model;
564 }
565 
566 /*
567 ================
568 idCollisionModelManagerLocal::AllocNode
569 ================
570 */
572  int i;
573  cm_node_t *node;
574  cm_nodeBlock_t *nodeBlock;
575 
576  if ( !model->nodeBlocks || !model->nodeBlocks->nextNode ) {
577  nodeBlock = (cm_nodeBlock_t *) Mem_ClearedAlloc( sizeof( cm_nodeBlock_t ) + blockSize * sizeof(cm_node_t) );
578  nodeBlock->nextNode = (cm_node_t *) ( ( (byte *) nodeBlock ) + sizeof( cm_nodeBlock_t ) );
579  nodeBlock->next = model->nodeBlocks;
580  model->nodeBlocks = nodeBlock;
581  node = nodeBlock->nextNode;
582  for ( i = 0; i < blockSize - 1; i++ ) {
583  node->parent = node + 1;
584  node = node->parent;
585  }
586  node->parent = NULL;
587  }
588 
589  node = model->nodeBlocks->nextNode;
590  model->nodeBlocks->nextNode = node->parent;
591  node->parent = NULL;
592 
593  return node;
594 }
595 
596 /*
597 ================
598 idCollisionModelManagerLocal::AllocPolygonReference
599 ================
600 */
602  int i;
603  cm_polygonRef_t *pref;
604  cm_polygonRefBlock_t *prefBlock;
605 
606  if ( !model->polygonRefBlocks || !model->polygonRefBlocks->nextRef ) {
607  prefBlock = (cm_polygonRefBlock_t *) Mem_Alloc( sizeof( cm_polygonRefBlock_t ) + blockSize * sizeof(cm_polygonRef_t) );
608  prefBlock->nextRef = (cm_polygonRef_t *) ( ( (byte *) prefBlock ) + sizeof( cm_polygonRefBlock_t ) );
609  prefBlock->next = model->polygonRefBlocks;
610  model->polygonRefBlocks = prefBlock;
611  pref = prefBlock->nextRef;
612  for ( i = 0; i < blockSize - 1; i++ ) {
613  pref->next = pref + 1;
614  pref = pref->next;
615  }
616  pref->next = NULL;
617  }
618 
619  pref = model->polygonRefBlocks->nextRef;
620  model->polygonRefBlocks->nextRef = pref->next;
621 
622  return pref;
623 }
624 
625 /*
626 ================
627 idCollisionModelManagerLocal::AllocBrushReference
628 ================
629 */
631  int i;
632  cm_brushRef_t *bref;
633  cm_brushRefBlock_t *brefBlock;
634 
635  if ( !model->brushRefBlocks || !model->brushRefBlocks->nextRef ) {
636  brefBlock = (cm_brushRefBlock_t *) Mem_Alloc( sizeof(cm_brushRefBlock_t) + blockSize * sizeof(cm_brushRef_t) );
637  brefBlock->nextRef = (cm_brushRef_t *) ( ( (byte *) brefBlock ) + sizeof(cm_brushRefBlock_t) );
638  brefBlock->next = model->brushRefBlocks;
639  model->brushRefBlocks = brefBlock;
640  bref = brefBlock->nextRef;
641  for ( i = 0; i < blockSize - 1; i++ ) {
642  bref->next = bref + 1;
643  bref = bref->next;
644  }
645  bref->next = NULL;
646  }
647 
648  bref = model->brushRefBlocks->nextRef;
649  model->brushRefBlocks->nextRef = bref->next;
650 
651  return bref;
652 }
653 
654 /*
655 ================
656 idCollisionModelManagerLocal::AllocPolygon
657 ================
658 */
661  int size;
662 
663  size = sizeof( cm_polygon_t ) + ( numEdges - 1 ) * sizeof( poly->edges[0] );
664  model->numPolygons++;
665  model->polygonMemory += size;
666  if ( model->polygonBlock && model->polygonBlock->bytesRemaining >= size ) {
667  poly = (cm_polygon_t *) model->polygonBlock->next;
668  model->polygonBlock->next += size;
669  model->polygonBlock->bytesRemaining -= size;
670  } else {
671  poly = (cm_polygon_t *) Mem_Alloc( size );
672  }
673  return poly;
674 }
675 
676 /*
677 ================
678 idCollisionModelManagerLocal::AllocBrush
679 ================
680 */
682  cm_brush_t *brush;
683  int size;
684 
685  size = sizeof( cm_brush_t ) + ( numPlanes - 1 ) * sizeof( brush->planes[0] );
686  model->numBrushes++;
687  model->brushMemory += size;
688  if ( model->brushBlock && model->brushBlock->bytesRemaining >= size ) {
689  brush = (cm_brush_t *) model->brushBlock->next;
690  model->brushBlock->next += size;
691  model->brushBlock->bytesRemaining -= size;
692  } else {
693  brush = (cm_brush_t *) Mem_Alloc( size );
694  }
695  return brush;
696 }
697 
698 /*
699 ================
700 idCollisionModelManagerLocal::AddPolygonToNode
701 ================
702 */
704  cm_polygonRef_t *pref;
705 
707  pref->p = p;
708  pref->next = node->polygons;
709  node->polygons = pref;
710  model->numPolygonRefs++;
711 }
712 
713 /*
714 ================
715 idCollisionModelManagerLocal::AddBrushToNode
716 ================
717 */
719  cm_brushRef_t *bref;
720 
722  bref->b = b;
723  bref->next = node->brushes;
724  node->brushes = bref;
725  model->numBrushRefs++;
726 }
727 
728 /*
729 ================
730 idCollisionModelManagerLocal::SetupTrmModelStructure
731 ================
732 */
734  int i;
735  cm_node_t *node;
736  cm_model_t *model;
737 
738  // setup model
739  model = AllocModel();
740 
741  assert( models );
742  models[MAX_SUBMODELS] = model;
743  // create node to hold the collision data
744  node = (cm_node_t *) AllocNode( model, 1 );
745  node->planeType = -1;
746  model->node = node;
747  // allocate vertex and edge arrays
748  model->numVertices = 0;
750  model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) );
751  model->numEdges = 0;
752  model->maxEdges = MAX_TRACEMODEL_EDGES+1;
753  model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) );
754  // create a material for the trace model polygons
755  trmMaterial = declManager->FindMaterial( "_tracemodel", false );
756  if ( !trmMaterial ) {
757  common->FatalError( "_tracemodel material not found" );
758  }
759 
760  // allocate polygons
761  for ( i = 0; i < MAX_TRACEMODEL_POLYS; i++ ) {
762  trmPolygons[i] = AllocPolygonReference( model, MAX_TRACEMODEL_POLYS );
764  trmPolygons[i]->p->bounds.Clear();
765  trmPolygons[i]->p->plane.Zero();
766  trmPolygons[i]->p->checkcount = 0;
767  trmPolygons[i]->p->contents = -1; // all contents
769  trmPolygons[i]->p->numEdges = 0;
770  }
771  // allocate brush for position test
772  trmBrushes[0] = AllocBrushReference( model, 1 );
773  trmBrushes[0]->b = AllocBrush( model, MAX_TRACEMODEL_POLYS );
774  trmBrushes[0]->b->primitiveNum = 0;
775  trmBrushes[0]->b->bounds.Clear();
776  trmBrushes[0]->b->checkcount = 0;
777  trmBrushes[0]->b->contents = -1; // all contents
778  trmBrushes[0]->b->numPlanes = 0;
779 }
780 
781 /*
782 ================
783 idCollisionModelManagerLocal::SetupTrmModel
784 
785 Trace models (item boxes, etc) are converted to collision models on the fly, using the last model slot
786 as a reusable temporary buffer
787 ================
788 */
790  int i, j;
791  cm_vertex_t *vertex;
792  cm_edge_t *edge;
794  cm_model_t *model;
795  const traceModelVert_t *trmVert;
796  const traceModelEdge_t *trmEdge;
797  const traceModelPoly_t *trmPoly;
798 
799  assert( models );
800 
801  if ( material == NULL ) {
802  material = trmMaterial;
803  }
804 
805  model = models[MAX_SUBMODELS];
806  model->node->brushes = NULL;
807  model->node->polygons = NULL;
808  // if not a valid trace model
809  if ( trm.type == TRM_INVALID || !trm.numPolys ) {
810  return TRACE_MODEL_HANDLE;
811  }
812  // vertices
813  model->numVertices = trm.numVerts;
814  vertex = model->vertices;
815  trmVert = trm.verts;
816  for ( i = 0; i < trm.numVerts; i++, vertex++, trmVert++ ) {
817  vertex->p = *trmVert;
818  vertex->sideSet = 0;
819  }
820  // edges
821  model->numEdges = trm.numEdges;
822  edge = model->edges + 1;
823  trmEdge = trm.edges + 1;
824  for ( i = 0; i < trm.numEdges; i++, edge++, trmEdge++ ) {
825  edge->vertexNum[0] = trmEdge->v[0];
826  edge->vertexNum[1] = trmEdge->v[1];
827  edge->normal = trmEdge->normal;
828  edge->internal = false;
829  edge->sideSet = 0;
830  }
831  // polygons
832  model->numPolygons = trm.numPolys;
833  trmPoly = trm.polys;
834  for ( i = 0; i < trm.numPolys; i++, trmPoly++ ) {
835  poly = trmPolygons[i]->p;
836  poly->numEdges = trmPoly->numEdges;
837  for ( j = 0; j < trmPoly->numEdges; j++ ) {
838  poly->edges[j] = trmPoly->edges[j];
839  }
840  poly->plane.SetNormal( trmPoly->normal );
841  poly->plane.SetDist( trmPoly->dist );
842  poly->bounds = trmPoly->bounds;
843  poly->material = material;
844  // link polygon at node
845  trmPolygons[i]->next = model->node->polygons;
846  model->node->polygons = trmPolygons[i];
847  }
848  // if the trace model is convex
849  if ( trm.isConvex ) {
850  // setup brush for position test
851  trmBrushes[0]->b->numPlanes = trm.numPolys;
852  for ( i = 0; i < trm.numPolys; i++ ) {
853  trmBrushes[0]->b->planes[i] = trmPolygons[i]->p->plane;
854  }
855  trmBrushes[0]->b->bounds = trm.bounds;
856  // link brush at node
857  trmBrushes[0]->next = model->node->brushes;
858  model->node->brushes = trmBrushes[0];
859  }
860  // model bounds
861  model->bounds = trm.bounds;
862  // convex
863  model->isConvex = trm.isConvex;
864 
865  return TRACE_MODEL_HANDLE;
866 }
867 
868 /*
869 ===============================================================================
870 
871 Optimisation, removal of polygons contained within brushes or solid
872 
873 ===============================================================================
874 */
875 
876 /*
877 ============
878 idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP
879 ============
880 */
881 int idCollisionModelManagerLocal::R_ChoppedAwayByProcBSP( int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius ) {
882  int res;
883  idFixedWinding back;
884  cm_procNode_t *node;
885  float dist;
886 
887  do {
888  node = procNodes + nodeNum;
889  dist = node->plane.Normal() * origin + node->plane[3];
890  if ( dist > radius ) {
891  res = SIDE_FRONT;
892  }
893  else if ( dist < -radius ) {
894  res = SIDE_BACK;
895  }
896  else {
897  res = w->Split( &back, node->plane, CHOP_EPSILON );
898  }
899  if ( res == SIDE_FRONT ) {
900  nodeNum = node->children[0];
901  }
902  else if ( res == SIDE_BACK ) {
903  nodeNum = node->children[1];
904  }
905  else if ( res == SIDE_ON ) {
906  // continue with the side the winding faces
907  if ( node->plane.Normal() * normal > 0.0f ) {
908  nodeNum = node->children[0];
909  }
910  else {
911  nodeNum = node->children[1];
912  }
913  }
914  else {
915  // if either node is not solid
916  if ( node->children[0] < 0 || node->children[1] < 0 ) {
917  return false;
918  }
919  // only recurse if the node is not solid
920  if ( node->children[1] > 0 ) {
921  if ( !R_ChoppedAwayByProcBSP( node->children[1], &back, normal, origin, radius ) ) {
922  return false;
923  }
924  }
925  nodeNum = node->children[0];
926  }
927  } while ( nodeNum > 0 );
928  if ( nodeNum < 0 ) {
929  return false;
930  }
931  return true;
932 }
933 
934 /*
935 ============
936 idCollisionModelManagerLocal::ChoppedAwayByProcBSP
937 ============
938 */
940  idFixedWinding neww;
941  idBounds bounds;
942  float radius;
943  idVec3 origin;
944 
945  // if the .proc file has no BSP tree
946  if ( procNodes == NULL ) {
947  return false;
948  }
949  // don't chop if the polygon is not solid
950  if ( !(contents & CONTENTS_SOLID) ) {
951  return false;
952  }
953  // make a local copy of the winding
954  neww = w;
955  neww.GetBounds( bounds );
956  origin = (bounds[1] - bounds[0]) * 0.5f;
957  radius = origin.Length() + CHOP_EPSILON;
958  origin = bounds[0] + origin;
959  //
960  return R_ChoppedAwayByProcBSP( 0, &neww, plane.Normal(), origin, radius );
961 }
962 
963 /*
964 =============
965 idCollisionModelManagerLocal::ChopWindingWithBrush
966 
967  returns the least number of winding fragments outside the brush
968 =============
969 */
971  int i, k, res, startPlane, planeNum, bestNumWindings;
972  idFixedWinding back, front;
973  idPlane plane;
974  bool chopped;
975  int sidedness[MAX_POINTS_ON_WINDING];
976  float dist;
977 
978  if ( b->numPlanes > MAX_POINTS_ON_WINDING ) {
979  return;
980  }
981 
982  // get sidedness for the list of windings
983  for ( i = 0; i < b->numPlanes; i++ ) {
984  plane = -b->planes[i];
985 
986  dist = plane.Distance( list->origin );
987  if ( dist > list->radius ) {
988  sidedness[i] = SIDE_FRONT;
989  }
990  else if ( dist < -list->radius ) {
991  sidedness[i] = SIDE_BACK;
992  }
993  else {
994  sidedness[i] = list->bounds.PlaneSide( plane );
995  if ( sidedness[i] == PLANESIDE_FRONT ) {
996  sidedness[i] = SIDE_FRONT;
997  }
998  else if ( sidedness[i] == PLANESIDE_BACK ) {
999  sidedness[i] = SIDE_BACK;
1000  }
1001  else {
1002  sidedness[i] = SIDE_CROSS;
1003  }
1004  }
1005  }
1006 
1007  cm_outList->numWindings = 0;
1008  for ( k = 0; k < list->numWindings; k++ ) {
1009  //
1010  startPlane = 0;
1011  bestNumWindings = 1 + b->numPlanes;
1012  chopped = false;
1013  do {
1014  front = list->w[k];
1015  cm_tmpList->numWindings = 0;
1016  for ( planeNum = startPlane, i = 0; i < b->numPlanes; i++, planeNum++ ) {
1017 
1018  if ( planeNum >= b->numPlanes ) {
1019  planeNum = 0;
1020  }
1021 
1022  res = sidedness[planeNum];
1023 
1024  if ( res == SIDE_CROSS ) {
1025  plane = -b->planes[planeNum];
1026  res = front.Split( &back, plane, CHOP_EPSILON );
1027  }
1028 
1029  // NOTE: disabling this can create gaps at places where Z-fighting occurs
1030  // Z-fighting should not occur but what if there is a decal brush side
1031  // with exactly the same size as another brush side ?
1032  // only leave windings on a brush if the winding plane and brush side plane face the same direction
1033  if ( res == SIDE_ON && list->primitiveNum >= 0 && (list->normal * b->planes[planeNum].Normal()) > 0 ) {
1034  // return because all windings in the list will be on this brush side plane
1035  return;
1036  }
1037 
1038  if ( res == SIDE_BACK ) {
1039  if ( cm_outList->numWindings >= MAX_WINDING_LIST ) {
1040  common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
1041  return;
1042  }
1043  // winding and brush didn't intersect, store the original winding
1044  cm_outList->w[cm_outList->numWindings] = list->w[k];
1045  cm_outList->numWindings++;
1046  chopped = false;
1047  break;
1048  }
1049 
1050  if ( res == SIDE_CROSS ) {
1051  if ( cm_tmpList->numWindings >= MAX_WINDING_LIST ) {
1052  common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
1053  return;
1054  }
1055  // store the front winding in the temporary list
1056  cm_tmpList->w[cm_tmpList->numWindings] = back;
1057  cm_tmpList->numWindings++;
1058  chopped = true;
1059  }
1060 
1061  // if already found a start plane which generates less fragments
1062  if ( cm_tmpList->numWindings >= bestNumWindings ) {
1063  break;
1064  }
1065  }
1066 
1067  // find the best start plane to get the least number of fragments outside the brush
1068  if ( cm_tmpList->numWindings < bestNumWindings ) {
1069  bestNumWindings = cm_tmpList->numWindings;
1070  // store windings from temporary list in the out list
1071  for ( i = 0; i < cm_tmpList->numWindings; i++ ) {
1072  if ( cm_outList->numWindings + i >= MAX_WINDING_LIST ) {
1073  common->Warning( "idCollisionModelManagerLocal::ChopWindingWithBrush: primitive %d more than %d windings", list->primitiveNum, MAX_WINDING_LIST );
1074  return;
1075  }
1076  cm_outList->w[cm_outList->numWindings+i] = cm_tmpList->w[i];
1077  }
1078  // if only one winding left then we can't do any better
1079  if ( bestNumWindings == 1 ) {
1080  break;
1081  }
1082  }
1083 
1084  // try the next start plane
1085  startPlane++;
1086 
1087  } while ( chopped && startPlane < b->numPlanes );
1088  //
1089  if ( chopped ) {
1090  cm_outList->numWindings += bestNumWindings;
1091  }
1092  }
1093  for ( k = 0; k < cm_outList->numWindings; k++ ) {
1094  list->w[k] = cm_outList->w[k];
1095  }
1096  list->numWindings = cm_outList->numWindings;
1097 }
1098 
1099 /*
1100 ============
1101 idCollisionModelManagerLocal::R_ChopWindingListWithTreeBrushes
1102 ============
1103 */
1105  int i;
1106  cm_brushRef_t *bref;
1107  cm_brush_t *b;
1108 
1109  while( 1 ) {
1110  for ( bref = node->brushes; bref; bref = bref->next ) {
1111  b = bref->b;
1112  // if we checked this brush already
1113  if ( b->checkcount == checkCount ) {
1114  continue;
1115  }
1116  b->checkcount = checkCount;
1117  // if the windings in the list originate from this brush
1118  if ( b->primitiveNum == list->primitiveNum ) {
1119  continue;
1120  }
1121  // if brush has a different contents
1122  if ( b->contents != list->contents ) {
1123  continue;
1124  }
1125  // brush bounds and winding list bounds should overlap
1126  for ( i = 0; i < 3; i++ ) {
1127  if ( list->bounds[0][i] > b->bounds[1][i] ) {
1128  break;
1129  }
1130  if ( list->bounds[1][i] < b->bounds[0][i] ) {
1131  break;
1132  }
1133  }
1134  if ( i < 3 ) {
1135  continue;
1136  }
1137  // chop windings in the list with brush
1138  ChopWindingListWithBrush( list, b );
1139  // if all windings are chopped away we're done
1140  if ( !list->numWindings ) {
1141  return;
1142  }
1143  }
1144  // if leaf node
1145  if ( node->planeType == -1 ) {
1146  break;
1147  }
1148  if ( list->bounds[0][node->planeType] > node->planeDist ) {
1149  node = node->children[0];
1150  }
1151  else if ( list->bounds[1][node->planeType] < node->planeDist ) {
1152  node = node->children[1];
1153  }
1154  else {
1155  R_ChopWindingListWithTreeBrushes( list, node->children[1] );
1156  if ( !list->numWindings ) {
1157  return;
1158  }
1159  node = node->children[0];
1160  }
1161  }
1162 }
1163 
1164 /*
1165 ============
1166 idCollisionModelManagerLocal::WindingOutsideBrushes
1167 
1168  Returns one winding which is not fully contained in brushes.
1169  We always favor less polygons over a stitched world.
1170  If the winding is partly contained and the contained pieces can be chopped off
1171  without creating multiple winding fragments then the chopped winding is returned.
1172 ============
1173 */
1174 idFixedWinding *idCollisionModelManagerLocal::WindingOutsideBrushes( idFixedWinding *w, const idPlane &plane, int contents, int primitiveNum, cm_node_t *headNode ) {
1175  int i, windingLeft;
1176 
1177  cm_windingList->bounds.Clear();
1178  for ( i = 0; i < w->GetNumPoints(); i++ ) {
1179  cm_windingList->bounds.AddPoint( (*w)[i].ToVec3() );
1180  }
1181 
1182  cm_windingList->origin = (cm_windingList->bounds[1] - cm_windingList->bounds[0]) * 0.5;
1183  cm_windingList->radius = cm_windingList->origin.Length() + CHOP_EPSILON;
1184  cm_windingList->origin = cm_windingList->bounds[0] + cm_windingList->origin;
1185  cm_windingList->bounds[0] -= idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON );
1186  cm_windingList->bounds[1] += idVec3( CHOP_EPSILON, CHOP_EPSILON, CHOP_EPSILON );
1187 
1188  cm_windingList->w[0] = *w;
1189  cm_windingList->numWindings = 1;
1190  cm_windingList->normal = plane.Normal();
1191  cm_windingList->contents = contents;
1192  cm_windingList->primitiveNum = primitiveNum;
1193  //
1194  checkCount++;
1195  R_ChopWindingListWithTreeBrushes( cm_windingList, headNode );
1196  //
1197  if ( !cm_windingList->numWindings ) {
1198  return NULL;
1199  }
1200  if ( cm_windingList->numWindings == 1 ) {
1201  return &cm_windingList->w[0];
1202  }
1203  // if not the world model
1204  if ( numModels != 0 ) {
1205  return w;
1206  }
1207  // check if winding fragments would be chopped away by the proc BSP tree
1208  windingLeft = -1;
1209  for ( i = 0; i < cm_windingList->numWindings; i++ ) {
1210  if ( !ChoppedAwayByProcBSP( cm_windingList->w[i], plane, contents ) ) {
1211  if ( windingLeft >= 0 ) {
1212  return w;
1213  }
1214  windingLeft = i;
1215  }
1216  }
1217  if ( windingLeft >= 0 ) {
1218  return &cm_windingList->w[windingLeft];
1219  }
1220  return NULL;
1221 }
1222 
1223 /*
1224 ===============================================================================
1225 
1226 Merging polygons
1227 
1228 ===============================================================================
1229 */
1230 
1231 /*
1232 =============
1233 idCollisionModelManagerLocal::ReplacePolygons
1234 
1235  does not allow for a node to have multiple references to the same polygon
1236 =============
1237 */
1239  cm_polygonRef_t *pref, *lastpref, *nextpref;
1240  cm_polygon_t *p;
1241  bool linked;
1242 
1243  while( 1 ) {
1244  linked = false;
1245  lastpref = NULL;
1246  for ( pref = node->polygons; pref; pref = nextpref ) {
1247  nextpref = pref->next;
1248  //
1249  p = pref->p;
1250  // if this polygon reference should change
1251  if ( p == p1 || p == p2 ) {
1252  // if the new polygon is already linked at this node
1253  if ( linked ) {
1254  if ( lastpref ) {
1255  lastpref->next = nextpref;
1256  }
1257  else {
1258  node->polygons = nextpref;
1259  }
1260  FreePolygonReference( pref );
1261  model->numPolygonRefs--;
1262  }
1263  else {
1264  pref->p = newp;
1265  linked = true;
1266  lastpref = pref;
1267  }
1268  }
1269  else {
1270  lastpref = pref;
1271  }
1272  }
1273  // if leaf node
1274  if ( node->planeType == -1 ) {
1275  break;
1276  }
1277  if ( p1->bounds[0][node->planeType] > node->planeDist && p2->bounds[0][node->planeType] > node->planeDist ) {
1278  node = node->children[0];
1279  }
1280  else if ( p1->bounds[1][node->planeType] < node->planeDist && p2->bounds[1][node->planeType] < node->planeDist ) {
1281  node = node->children[1];
1282  }
1283  else {
1284  ReplacePolygons( model, node->children[1], p1, p2, newp );
1285  node = node->children[0];
1286  }
1287  }
1288 }
1289 
1290 /*
1291 =============
1292 idCollisionModelManagerLocal::TryMergePolygons
1293 =============
1294 */
1295 #define CONTINUOUS_EPSILON 0.005f
1296 #define NORMAL_EPSILON 0.01f
1297 
1299  int i, j, nexti, prevj;
1300  int p1BeforeShare, p1AfterShare, p2BeforeShare, p2AfterShare;
1301  int newEdges[CM_MAX_POLYGON_EDGES], newNumEdges;
1302  int edgeNum, edgeNum1, edgeNum2, newEdgeNum1, newEdgeNum2;
1303  cm_edge_t *edge;
1304  cm_polygon_t *newp;
1305  idVec3 delta, normal;
1306  float dot;
1307  bool keep1, keep2;
1308 
1309  if ( p1->material != p2->material ) {
1310  return NULL;
1311  }
1312  if ( idMath::Fabs( p1->plane.Dist() - p2->plane.Dist() ) > NORMAL_EPSILON ) {
1313  return NULL;
1314  }
1315  for ( i = 0; i < 3; i++ ) {
1316  if ( idMath::Fabs( p1->plane.Normal()[i] - p2->plane.Normal()[i] ) > NORMAL_EPSILON ) {
1317  return NULL;
1318  }
1319  if ( p1->bounds[0][i] > p2->bounds[1][i] ) {
1320  return NULL;
1321  }
1322  if ( p1->bounds[1][i] < p2->bounds[0][i] ) {
1323  return NULL;
1324  }
1325  }
1326  // this allows for merging polygons with multiple shared edges
1327  // polygons with multiple shared edges probably never occur tho ;)
1328  p1BeforeShare = p1AfterShare = p2BeforeShare = p2AfterShare = -1;
1329  for ( i = 0; i < p1->numEdges; i++ ) {
1330  nexti = (i+1)%p1->numEdges;
1331  for ( j = 0; j < p2->numEdges; j++ ) {
1332  prevj = (j+p2->numEdges-1)%p2->numEdges;
1333  //
1334  if ( abs(p1->edges[i]) != abs(p2->edges[j]) ) {
1335  // if the next edge of p1 and the previous edge of p2 are the same
1336  if ( abs(p1->edges[nexti]) == abs(p2->edges[prevj]) ) {
1337  // if both polygons don't use the edge in the same direction
1338  if ( p1->edges[nexti] != p2->edges[prevj] ) {
1339  p1BeforeShare = i;
1340  p2AfterShare = j;
1341  }
1342  break;
1343  }
1344  }
1345  // if both polygons don't use the edge in the same direction
1346  else if ( p1->edges[i] != p2->edges[j] ) {
1347  // if the next edge of p1 and the previous edge of p2 are not the same
1348  if ( abs(p1->edges[nexti]) != abs(p2->edges[prevj]) ) {
1349  p1AfterShare = nexti;
1350  p2BeforeShare = prevj;
1351  break;
1352  }
1353  }
1354  }
1355  }
1356  if ( p1BeforeShare < 0 || p1AfterShare < 0 || p2BeforeShare < 0 || p2AfterShare < 0 ) {
1357  return NULL;
1358  }
1359 
1360  // check if the new polygon would still be convex
1361  edgeNum = p1->edges[p1BeforeShare];
1362  edge = model->edges + abs(edgeNum);
1363  delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p -
1364  model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1365  normal = p1->plane.Normal().Cross( delta );
1366  normal.Normalize();
1367 
1368  edgeNum = p2->edges[p2AfterShare];
1369  edge = model->edges + abs(edgeNum);
1370  delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p -
1371  model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1372 
1373  dot = delta * normal;
1374  if (dot < -CONTINUOUS_EPSILON)
1375  return NULL; // not a convex polygon
1376  keep1 = (bool)(dot > CONTINUOUS_EPSILON);
1377 
1378  edgeNum = p2->edges[p2BeforeShare];
1379  edge = model->edges + abs(edgeNum);
1380  delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p -
1381  model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1382  normal = p1->plane.Normal().Cross( delta );
1383  normal.Normalize();
1384 
1385  edgeNum = p1->edges[p1AfterShare];
1386  edge = model->edges + abs(edgeNum);
1387  delta = model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p -
1388  model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1389 
1390  dot = delta * normal;
1391  if (dot < -CONTINUOUS_EPSILON)
1392  return NULL; // not a convex polygon
1393  keep2 = (bool)(dot > CONTINUOUS_EPSILON);
1394 
1395  newEdgeNum1 = newEdgeNum2 = 0;
1396  // get new edges if we need to replace colinear ones
1397  if ( !keep1 ) {
1398  edgeNum1 = p1->edges[p1BeforeShare];
1399  edgeNum2 = p2->edges[p2AfterShare];
1400  GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INTSIGNBITSET(edgeNum1)]].p,
1401  model->vertices[model->edges[abs(edgeNum2)].vertexNum[INTSIGNBITNOTSET(edgeNum2)]].p,
1402  &newEdgeNum1, -1 );
1403  if ( newEdgeNum1 == 0 ) {
1404  keep1 = true;
1405  }
1406  }
1407  if ( !keep2 ) {
1408  edgeNum1 = p2->edges[p2BeforeShare];
1409  edgeNum2 = p1->edges[p1AfterShare];
1410  GetEdge( model, model->vertices[model->edges[abs(edgeNum1)].vertexNum[INTSIGNBITSET(edgeNum1)]].p,
1411  model->vertices[model->edges[abs(edgeNum2)].vertexNum[INTSIGNBITNOTSET(edgeNum2)]].p,
1412  &newEdgeNum2, -1 );
1413  if ( newEdgeNum2 == 0 ) {
1414  keep2 = true;
1415  }
1416  }
1417  // set edges for new polygon
1418  newNumEdges = 0;
1419  if ( !keep2 ) {
1420  newEdges[newNumEdges++] = newEdgeNum2;
1421  }
1422  if ( p1AfterShare < p1BeforeShare ) {
1423  for ( i = p1AfterShare + (!keep2); i <= p1BeforeShare - (!keep1); i++ ) {
1424  newEdges[newNumEdges++] = p1->edges[i];
1425  }
1426  }
1427  else {
1428  for ( i = p1AfterShare + (!keep2); i < p1->numEdges; i++ ) {
1429  newEdges[newNumEdges++] = p1->edges[i];
1430  }
1431  for ( i = 0; i <= p1BeforeShare - (!keep1); i++ ) {
1432  newEdges[newNumEdges++] = p1->edges[i];
1433  }
1434  }
1435  if ( !keep1 ) {
1436  newEdges[newNumEdges++] = newEdgeNum1;
1437  }
1438  if ( p2AfterShare < p2BeforeShare ) {
1439  for ( i = p2AfterShare + (!keep1); i <= p2BeforeShare - (!keep2); i++ ) {
1440  newEdges[newNumEdges++] = p2->edges[i];
1441  }
1442  }
1443  else {
1444  for ( i = p2AfterShare + (!keep1); i < p2->numEdges; i++ ) {
1445  newEdges[newNumEdges++] = p2->edges[i];
1446  }
1447  for ( i = 0; i <= p2BeforeShare - (!keep2); i++ ) {
1448  newEdges[newNumEdges++] = p2->edges[i];
1449  }
1450  }
1451 
1452  newp = AllocPolygon( model, newNumEdges );
1453  memcpy( newp, p1, sizeof(cm_polygon_t) );
1454  memcpy( newp->edges, newEdges, newNumEdges * sizeof(int) );
1455  newp->numEdges = newNumEdges;
1456  newp->checkcount = 0;
1457  // increase usage count for the edges of this polygon
1458  for ( i = 0; i < newp->numEdges; i++ ) {
1459  if ( !keep1 && newp->edges[i] == newEdgeNum1 ) {
1460  continue;
1461  }
1462  if ( !keep2 && newp->edges[i] == newEdgeNum2 ) {
1463  continue;
1464  }
1465  model->edges[abs(newp->edges[i])].numUsers++;
1466  }
1467  // create new bounds from the merged polygons
1468  newp->bounds = p1->bounds + p2->bounds;
1469 
1470  return newp;
1471 }
1472 
1473 /*
1474 =============
1475 idCollisionModelManagerLocal::MergePolygonWithTreePolygons
1476 =============
1477 */
1479  int i;
1480  cm_polygonRef_t *pref;
1481  cm_polygon_t *p, *newp;
1482 
1483  while( 1 ) {
1484  for ( pref = node->polygons; pref; pref = pref->next ) {
1485  p = pref->p;
1486  //
1487  if ( p == polygon ) {
1488  continue;
1489  }
1490  //
1491  newp = TryMergePolygons( model, polygon, p );
1492  // if polygons were merged
1493  if ( newp ) {
1494  model->numMergedPolys++;
1495  // replace links to the merged polygons with links to the new polygon
1496  ReplacePolygons( model, model->node, polygon, p, newp );
1497  // decrease usage count for edges of both merged polygons
1498  for ( i = 0; i < polygon->numEdges; i++ ) {
1499  model->edges[abs(polygon->edges[i])].numUsers--;
1500  }
1501  for ( i = 0; i < p->numEdges; i++ ) {
1502  model->edges[abs(p->edges[i])].numUsers--;
1503  }
1504  // free merged polygons
1505  FreePolygon( model, polygon );
1506  FreePolygon( model, p );
1507 
1508  return true;
1509  }
1510  }
1511  // if leaf node
1512  if ( node->planeType == -1 ) {
1513  break;
1514  }
1515  if ( polygon->bounds[0][node->planeType] > node->planeDist ) {
1516  node = node->children[0];
1517  }
1518  else if ( polygon->bounds[1][node->planeType] < node->planeDist ) {
1519  node = node->children[1];
1520  }
1521  else {
1522  if ( MergePolygonWithTreePolygons( model, node->children[1], polygon ) ) {
1523  return true;
1524  }
1525  node = node->children[0];
1526  }
1527  }
1528  return false;
1529 }
1530 
1531 /*
1532 =============
1533 idCollisionModelManagerLocal::MergeTreePolygons
1534 
1535  try to merge any two polygons with the same surface flags and the same contents
1536 =============
1537 */
1539  cm_polygonRef_t *pref;
1540  cm_polygon_t *p;
1541  bool merge;
1542 
1543  while( 1 ) {
1544  do {
1545  merge = false;
1546  for ( pref = node->polygons; pref; pref = pref->next ) {
1547  p = pref->p;
1548  // if we checked this polygon already
1549  if ( p->checkcount == checkCount ) {
1550  continue;
1551  }
1552  p->checkcount = checkCount;
1553  // try to merge this polygon with other polygons in the tree
1554  if ( MergePolygonWithTreePolygons( model, model->node, p ) ) {
1555  merge = true;
1556  break;
1557  }
1558  }
1559  } while (merge);
1560  // if leaf node
1561  if ( node->planeType == -1 ) {
1562  break;
1563  }
1564  MergeTreePolygons( model, node->children[1] );
1565  node = node->children[0];
1566  }
1567 }
1568 
1569 /*
1570 ===============================================================================
1571 
1572 Find internal edges
1573 
1574 ===============================================================================
1575 */
1576 
1577 /*
1578 
1579  if (two polygons have the same contents)
1580  if (the normals of the two polygon planes face towards each other)
1581  if (an edge is shared between the polygons)
1582  if (the edge is not shared in the same direction)
1583  then this is an internal edge
1584  else
1585  if (this edge is on the plane of the other polygon)
1586  if (this edge if fully inside the winding of the other polygon)
1587  then this edge is an internal edge
1588 
1589 */
1590 
1591 /*
1592 =============
1593 idCollisionModelManagerLocal::PointInsidePolygon
1594 =============
1595 */
1597  int i, edgeNum;
1598  idVec3 *v1, *v2, dir1, dir2, vec;
1599  cm_edge_t *edge;
1600 
1601  for ( i = 0; i < p->numEdges; i++ ) {
1602  edgeNum = p->edges[i];
1603  edge = model->edges + abs(edgeNum);
1604  //
1605  v1 = &model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1606  v2 = &model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p;
1607  dir1 = (*v2) - (*v1);
1608  vec = v - (*v1);
1609  dir2 = dir1.Cross( p->plane.Normal() );
1610  if ( vec * dir2 > VERTEX_EPSILON ) {
1611  return false;
1612  }
1613  }
1614  return true;
1615 }
1616 
1617 /*
1618 =============
1619 idCollisionModelManagerLocal::FindInternalEdgesOnPolygon
1620 =============
1621 */
1623  int i, j, k, edgeNum;
1624  cm_edge_t *edge;
1625  idVec3 *v1, *v2, dir1, dir2;
1626  float d;
1627 
1628  // bounds of polygons should overlap or touch
1629  for ( i = 0; i < 3; i++ ) {
1630  if ( p1->bounds[0][i] > p2->bounds[1][i] ) {
1631  return;
1632  }
1633  if ( p1->bounds[1][i] < p2->bounds[0][i] ) {
1634  return;
1635  }
1636  }
1637  //
1638  // FIXME: doubled geometry causes problems
1639  //
1640  for ( i = 0; i < p1->numEdges; i++ ) {
1641  edgeNum = p1->edges[i];
1642  edge = model->edges + abs(edgeNum);
1643  // if already an internal edge
1644  if ( edge->internal ) {
1645  continue;
1646  }
1647  //
1648  v1 = &model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1649  v2 = &model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p;
1650  // if either of the two vertices is outside the bounds of the other polygon
1651  for ( k = 0; k < 3; k++ ) {
1652  d = p2->bounds[1][k] + VERTEX_EPSILON;
1653  if ( (*v1)[k] > d || (*v2)[k] > d ) {
1654  break;
1655  }
1656  d = p2->bounds[0][k] - VERTEX_EPSILON;
1657  if ( (*v1)[k] < d || (*v2)[k] < d ) {
1658  break;
1659  }
1660  }
1661  if ( k < 3 ) {
1662  continue;
1663  }
1664  //
1665  k = abs(edgeNum);
1666  for ( j = 0; j < p2->numEdges; j++ ) {
1667  if ( k == abs(p2->edges[j]) ) {
1668  break;
1669  }
1670  }
1671  // if the edge is shared between the two polygons
1672  if ( j < p2->numEdges ) {
1673  // if the edge is used by more than 2 polygons
1674  if ( edge->numUsers > 2 ) {
1675  // could still be internal but we'd have to test all polygons using the edge
1676  continue;
1677  }
1678  // if the edge goes in the same direction for both polygons
1679  if ( edgeNum == p2->edges[j] ) {
1680  // the polygons can lay ontop of each other or one can obscure the other
1681  continue;
1682  }
1683  }
1684  // the edge was not shared
1685  else {
1686  // both vertices should be on the plane of the other polygon
1687  d = p2->plane.Distance( *v1 );
1688  if ( idMath::Fabs(d) > VERTEX_EPSILON ) {
1689  continue;
1690  }
1691  d = p2->plane.Distance( *v2 );
1692  if ( idMath::Fabs(d) > VERTEX_EPSILON ) {
1693  continue;
1694  }
1695  }
1696  // the two polygon plane normals should face towards each other
1697  dir1 = (*v2) - (*v1);
1698  dir2 = p1->plane.Normal().Cross( dir1 );
1699  if ( p2->plane.Normal() * dir2 < 0 ) {
1700  //continue;
1701  break;
1702  }
1703  // if the edge was not shared
1704  if ( j >= p2->numEdges ) {
1705  // both vertices of the edge should be inside the winding of the other polygon
1706  if ( !PointInsidePolygon( model, p2, *v1 ) ) {
1707  continue;
1708  }
1709  if ( !PointInsidePolygon( model, p2, *v2 ) ) {
1710  continue;
1711  }
1712  }
1713  // we got another internal edge
1714  edge->internal = true;
1715  model->numInternalEdges++;
1716  }
1717 }
1718 
1719 /*
1720 =============
1721 idCollisionModelManagerLocal::FindInternalPolygonEdges
1722 =============
1723 */
1725  cm_polygonRef_t *pref;
1726  cm_polygon_t *p;
1727 
1728  if ( polygon->material->GetCullType() == CT_TWO_SIDED || polygon->material->ShouldCreateBackSides() ) {
1729  return;
1730  }
1731 
1732  while( 1 ) {
1733  for ( pref = node->polygons; pref; pref = pref->next ) {
1734  p = pref->p;
1735  //
1736  // FIXME: use some sort of additional checkcount because currently
1737  // polygons can be checked multiple times
1738  //
1739  // if the polygons don't have the same contents
1740  if ( p->contents != polygon->contents ) {
1741  continue;
1742  }
1743  if ( p == polygon ) {
1744  continue;
1745  }
1746  FindInternalEdgesOnPolygon( model, polygon, p );
1747  }
1748  // if leaf node
1749  if ( node->planeType == -1 ) {
1750  break;
1751  }
1752  if ( polygon->bounds[0][node->planeType] > node->planeDist ) {
1753  node = node->children[0];
1754  }
1755  else if ( polygon->bounds[1][node->planeType] < node->planeDist ) {
1756  node = node->children[1];
1757  }
1758  else {
1759  FindInternalPolygonEdges( model, node->children[1], polygon );
1760  node = node->children[0];
1761  }
1762  }
1763 }
1764 
1765 /*
1766 =============
1767 idCollisionModelManagerLocal::FindContainedEdges
1768 =============
1769 */
1771  int i, edgeNum;
1772  cm_edge_t *edge;
1773  idFixedWinding w;
1774 
1775  for ( i = 0; i < p->numEdges; i++ ) {
1776  edgeNum = p->edges[i];
1777  edge = model->edges + abs(edgeNum);
1778  if ( edge->internal ) {
1779  continue;
1780  }
1781  w.Clear();
1782  w += model->vertices[edge->vertexNum[INTSIGNBITSET(edgeNum)]].p;
1783  w += model->vertices[edge->vertexNum[INTSIGNBITNOTSET(edgeNum)]].p;
1784  if ( ChoppedAwayByProcBSP( w, p->plane, p->contents ) ) {
1785  edge->internal = true;
1786  }
1787  }
1788 }
1789 
1790 /*
1791 =============
1792 idCollisionModelManagerLocal::FindInternalEdges
1793 =============
1794 */
1796  cm_polygonRef_t *pref;
1797  cm_polygon_t *p;
1798 
1799  while( 1 ) {
1800  for ( pref = node->polygons; pref; pref = pref->next ) {
1801  p = pref->p;
1802  // if we checked this polygon already
1803  if ( p->checkcount == checkCount ) {
1804  continue;
1805  }
1806  p->checkcount = checkCount;
1807 
1808  FindInternalPolygonEdges( model, model->node, p );
1809 
1810  //FindContainedEdges( model, p );
1811  }
1812  // if leaf node
1813  if ( node->planeType == -1 ) {
1814  break;
1815  }
1816  FindInternalEdges( model, node->children[1] );
1817  node = node->children[0];
1818  }
1819 }
1820 
1821 /*
1822 ===============================================================================
1823 
1824 Spatial subdivision
1825 
1826 ===============================================================================
1827 */
1828 
1829 /*
1830 ================
1831 CM_FindSplitter
1832 ================
1833 */
1834 static int CM_FindSplitter( const cm_node_t *node, const idBounds &bounds, int *planeType, float *planeDist ) {
1835  int i, j, type, axis[3], polyCount;
1836  float dist, t, bestt, size[3];
1837  cm_brushRef_t *bref;
1838  cm_polygonRef_t *pref;
1839  const cm_node_t *n;
1840  bool forceSplit = false;
1841 
1842  for ( i = 0; i < 3; i++ ) {
1843  size[i] = bounds[1][i] - bounds[0][i];
1844  axis[i] = i;
1845  }
1846  // sort on largest axis
1847  for ( i = 0; i < 2; i++ ) {
1848  if ( size[i] < size[i+1] ) {
1849  t = size[i];
1850  size[i] = size[i+1];
1851  size[i+1] = t;
1852  j = axis[i];
1853  axis[i] = axis[i+1];
1854  axis[i+1] = j;
1855  i = -1;
1856  }
1857  }
1858  // if the node is too small for further splits
1859  if ( size[0] < MIN_NODE_SIZE ) {
1860  polyCount = 0;
1861  for ( pref = node->polygons; pref; pref = pref->next) {
1862  polyCount++;
1863  }
1864  if ( polyCount > MAX_NODE_POLYGONS ) {
1865  forceSplit = true;
1866  }
1867  }
1868  // find an axial aligned splitter
1869  for ( i = 0; i < 3; i++ ) {
1870  // start with the largest axis first
1871  type = axis[i];
1872  bestt = size[i];
1873  // if the node is small anough in this axis direction
1874  if ( !forceSplit && bestt < MIN_NODE_SIZE ) {
1875  break;
1876  }
1877  // find an axial splitter from the brush bounding boxes
1878  // also try brushes from parent nodes
1879  for ( n = node; n; n = n->parent ) {
1880  for ( bref = n->brushes; bref; bref = bref->next) {
1881  for ( j = 0; j < 2; j++ ) {
1882  dist = bref->b->bounds[j][type];
1883  // if the splitter is already used or outside node bounds
1884  if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) {
1885  continue;
1886  }
1887  // find the most centered splitter
1888  t = abs((bounds[1][type] - dist) - (dist - bounds[0][type]));
1889  if ( t < bestt ) {
1890  bestt = t;
1891  *planeType = type;
1892  *planeDist = dist;
1893  }
1894  }
1895  }
1896  }
1897  // find an axial splitter from the polygon bounding boxes
1898  // also try brushes from parent nodes
1899  for ( n = node; n; n = n->parent ) {
1900  for ( pref = n->polygons; pref; pref = pref->next) {
1901  for ( j = 0; j < 2; j++ ) {
1902  dist = pref->p->bounds[j][type];
1903  // if the splitter is already used or outside node bounds
1904  if ( dist >= bounds[1][type] || dist <= bounds[0][type] ) {
1905  continue;
1906  }
1907  // find the most centered splitter
1908  t = abs((bounds[1][type] - dist) - (dist - bounds[0][type]));
1909  if ( t < bestt ) {
1910  bestt = t;
1911  *planeType = type;
1912  *planeDist = dist;
1913  }
1914  }
1915  }
1916  }
1917  // if we found a splitter on the largest axis
1918  if ( bestt < size[i] ) {
1919  // if forced split due to lots of polygons
1920  if ( forceSplit ) {
1921  return true;
1922  }
1923  // don't create splitters real close to the bounds
1924  if ( bounds[1][type] - *planeDist > (MIN_NODE_SIZE*0.5f) &&
1925  *planeDist - bounds[0][type] > (MIN_NODE_SIZE*0.5f) ) {
1926  return true;
1927  }
1928  }
1929  }
1930  return false;
1931 }
1932 
1933 /*
1934 ================
1935 CM_R_InsideAllChildren
1936 ================
1937 */
1938 static int CM_R_InsideAllChildren( cm_node_t *node, const idBounds &bounds ) {
1939  assert(node != NULL);
1940  if ( node->planeType != -1 ) {
1941  if ( bounds[0][node->planeType] >= node->planeDist ) {
1942  return false;
1943  }
1944  if ( bounds[1][node->planeType] <= node->planeDist ) {
1945  return false;
1946  }
1947  if ( !CM_R_InsideAllChildren( node->children[0], bounds ) ) {
1948  return false;
1949  }
1950  if ( !CM_R_InsideAllChildren( node->children[1], bounds ) ) {
1951  return false;
1952  }
1953  }
1954  return true;
1955 }
1956 
1957 /*
1958 ================
1959 idCollisionModelManagerLocal::R_FilterPolygonIntoTree
1960 ================
1961 */
1963  assert(node != NULL);
1964  while ( node->planeType != -1 ) {
1965  if ( CM_R_InsideAllChildren( node, p->bounds ) ) {
1966  break;
1967  }
1968  if ( p->bounds[0][node->planeType] >= node->planeDist ) {
1969  node = node->children[0];
1970  }
1971  else if ( p->bounds[1][node->planeType] <= node->planeDist ) {
1972  node = node->children[1];
1973  }
1974  else {
1975  R_FilterPolygonIntoTree( model, node->children[1], NULL, p );
1976  node = node->children[0];
1977  }
1978  }
1979  if ( pref ) {
1980  pref->next = node->polygons;
1981  node->polygons = pref;
1982  }
1983  else {
1984  AddPolygonToNode( model, node, p );
1985  }
1986 }
1987 
1988 /*
1989 ================
1990 idCollisionModelManagerLocal::R_FilterBrushIntoTree
1991 ================
1992 */
1994  assert(node != NULL);
1995  while ( node->planeType != -1 ) {
1996  if ( CM_R_InsideAllChildren( node, b->bounds ) ) {
1997  break;
1998  }
1999  if ( b->bounds[0][node->planeType] >= node->planeDist ) {
2000  node = node->children[0];
2001  }
2002  else if ( b->bounds[1][node->planeType] <= node->planeDist ) {
2003  node = node->children[1];
2004  }
2005  else {
2006  R_FilterBrushIntoTree( model, node->children[1], NULL, b );
2007  node = node->children[0];
2008  }
2009  }
2010  if ( pref ) {
2011  pref->next = node->brushes;
2012  node->brushes = pref;
2013  }
2014  else {
2015  AddBrushToNode( model, node, b );
2016  }
2017 }
2018 
2019 /*
2020 ================
2021 idCollisionModelManagerLocal::R_CreateAxialBSPTree
2022 
2023  a brush or polygon is linked in the node closest to the root where
2024  the brush or polygon is inside all children
2025 ================
2026 */
2028  int planeType;
2029  float planeDist;
2030  cm_polygonRef_t *pref, *nextpref, *prevpref;
2031  cm_brushRef_t *bref, *nextbref, *prevbref;
2032  cm_node_t *frontNode, *backNode, *n;
2033  idBounds frontBounds, backBounds;
2034 
2035  if ( !CM_FindSplitter( node, bounds, &planeType, &planeDist ) ) {
2036  node->planeType = -1;
2037  return node;
2038  }
2039  // create two child nodes
2040  frontNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE );
2041  memset( frontNode, 0, sizeof(cm_node_t) );
2042  frontNode->parent = node;
2043  frontNode->planeType = -1;
2044  //
2045  backNode = AllocNode( model, NODE_BLOCK_SIZE_LARGE );
2046  memset( backNode, 0, sizeof(cm_node_t) );
2047  backNode->parent = node;
2048  backNode->planeType = -1;
2049  //
2050  model->numNodes += 2;
2051  // set front node bounds
2052  frontBounds = bounds;
2053  frontBounds[0][planeType] = planeDist;
2054  // set back node bounds
2055  backBounds = bounds;
2056  backBounds[1][planeType] = planeDist;
2057  //
2058  node->planeType = planeType;
2059  node->planeDist = planeDist;
2060  node->children[0] = frontNode;
2061  node->children[1] = backNode;
2062  // filter polygons and brushes down the tree if necesary
2063  for ( n = node; n; n = n->parent ) {
2064  prevpref = NULL;
2065  for ( pref = n->polygons; pref; pref = nextpref) {
2066  nextpref = pref->next;
2067  // if polygon is not inside all children
2068  if ( !CM_R_InsideAllChildren( n, pref->p->bounds ) ) {
2069  // filter polygon down the tree
2070  R_FilterPolygonIntoTree( model, n, pref, pref->p );
2071  if ( prevpref ) {
2072  prevpref->next = nextpref;
2073  }
2074  else {
2075  n->polygons = nextpref;
2076  }
2077  }
2078  else {
2079  prevpref = pref;
2080  }
2081  }
2082  prevbref = NULL;
2083  for ( bref = n->brushes; bref; bref = nextbref) {
2084  nextbref = bref->next;
2085  // if brush is not inside all children
2086  if ( !CM_R_InsideAllChildren( n, bref->b->bounds ) ) {
2087  // filter brush down the tree
2088  R_FilterBrushIntoTree( model, n, bref, bref->b );
2089  if ( prevbref ) {
2090  prevbref->next = nextbref;
2091  }
2092  else {
2093  n->brushes = nextbref;
2094  }
2095  }
2096  else {
2097  prevbref = bref;
2098  }
2099  }
2100  }
2101  R_CreateAxialBSPTree( model, frontNode, frontBounds );
2102  R_CreateAxialBSPTree( model, backNode, backBounds );
2103  return node;
2104 }
2105 
2106 /*
2107 int cm_numSavedPolygonLinks;
2108 int cm_numSavedBrushLinks;
2109 
2110 int CM_R_CountChildren( cm_node_t *node ) {
2111  if ( node->planeType == -1 ) {
2112  return 0;
2113  }
2114  return 2 + CM_R_CountChildren(node->children[0]) + CM_R_CountChildren(node->children[1]);
2115 }
2116 
2117 void CM_R_TestOptimisation( cm_node_t *node ) {
2118  int polyCount, brushCount, numChildren;
2119  cm_polygonRef_t *pref;
2120  cm_brushRef_t *bref;
2121 
2122  if ( node->planeType == -1 ) {
2123  return;
2124  }
2125  polyCount = 0;
2126  for ( pref = node->polygons; pref; pref = pref->next) {
2127  polyCount++;
2128  }
2129  brushCount = 0;
2130  for ( bref = node->brushes; bref; bref = bref->next) {
2131  brushCount++;
2132  }
2133  if ( polyCount || brushCount ) {
2134  numChildren = CM_R_CountChildren( node );
2135  cm_numSavedPolygonLinks += (numChildren - 1) * polyCount;
2136  cm_numSavedBrushLinks += (numChildren - 1) * brushCount;
2137  }
2138  CM_R_TestOptimisation( node->children[0] );
2139  CM_R_TestOptimisation( node->children[1] );
2140 }
2141 */
2142 
2143 /*
2144 ================
2145 idCollisionModelManagerLocal::CreateAxialBSPTree
2146 ================
2147 */
2149  cm_polygonRef_t *pref;
2150  cm_brushRef_t *bref;
2151  idBounds bounds;
2152 
2153  // get head node bounds
2154  bounds.Clear();
2155  for ( pref = node->polygons; pref; pref = pref->next) {
2156  bounds += pref->p->bounds;
2157  }
2158  for ( bref = node->brushes; bref; bref = bref->next) {
2159  bounds += bref->b->bounds;
2160  }
2161 
2162  // create axial BSP tree from head node
2163  node = R_CreateAxialBSPTree( model, node, bounds );
2164 
2165  return node;
2166 }
2167 
2168 /*
2169 ===============================================================================
2170 
2171 Raw polygon and brush data
2172 
2173 ===============================================================================
2174 */
2175 
2176 /*
2177 ================
2178 idCollisionModelManagerLocal::SetupHash
2179 ================
2180 */
2182  if ( !cm_vertexHash ) {
2183  cm_vertexHash = new idHashIndex( VERTEX_HASH_SIZE, 1024 );
2184  }
2185  if ( !cm_edgeHash ) {
2186  cm_edgeHash = new idHashIndex( EDGE_HASH_SIZE, 1024 );
2187  }
2188  // init variables used during loading and optimization
2189  if ( !cm_windingList ) {
2190  cm_windingList = new cm_windingList_t;
2191  }
2192  if ( !cm_outList ) {
2193  cm_outList = new cm_windingList_t;
2194  }
2195  if ( !cm_tmpList ) {
2196  cm_tmpList = new cm_windingList_t;
2197  }
2198 }
2199 
2200 /*
2201 ================
2202 idCollisionModelManagerLocal::ShutdownHash
2203 ================
2204 */
2206  delete cm_vertexHash;
2207  cm_vertexHash = NULL;
2208  delete cm_edgeHash;
2209  cm_edgeHash = NULL;
2210  delete cm_tmpList;
2211  cm_tmpList = NULL;
2212  delete cm_outList;
2213  cm_outList = NULL;
2214  delete cm_windingList;
2215  cm_windingList = NULL;
2216 }
2217 
2218 /*
2219 ================
2220 idCollisionModelManagerLocal::ClearHash
2221 ================
2222 */
2224  int i;
2225  float f, max;
2226 
2227  cm_vertexHash->Clear();
2228  cm_edgeHash->Clear();
2229 
2230  cm_modelBounds = bounds;
2231  max = bounds[1].x - bounds[0].x;
2232  f = bounds[1].y - bounds[0].y;
2233  if ( f > max ) {
2234  max = f;
2235  }
2237  for ( i = 0; (1<<i) < cm_vertexShift; i++ ) {
2238  }
2239  if ( i == 0 ) {
2240  cm_vertexShift = 1;
2241  }
2242  else {
2243  cm_vertexShift = i;
2244  }
2245 }
2246 
2247 /*
2248 ================
2249 idCollisionModelManagerLocal::HashVec
2250 ================
2251 */
2253  /*
2254  int x, y;
2255 
2256  x = (((int)(vec[0] - cm_modelBounds[0].x + 0.5 )) >> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1);
2257  y = (((int)(vec[1] - cm_modelBounds[0].y + 0.5 )) >> cm_vertexShift) & (VERTEX_HASH_BOXSIZE-1);
2258 
2259  assert (x >= 0 && x < VERTEX_HASH_BOXSIZE && y >= 0 && y < VERTEX_HASH_BOXSIZE);
2260 
2261  return y * VERTEX_HASH_BOXSIZE + x;
2262  */
2263  int x, y, z;
2264 
2265  x = (((int) (vec[0] - cm_modelBounds[0].x + 0.5)) + 2) >> 2;
2266  y = (((int) (vec[1] - cm_modelBounds[0].y + 0.5)) + 2) >> 2;
2267  z = (((int) (vec[2] - cm_modelBounds[0].z + 0.5)) + 2) >> 2;
2268  return (x + y * VERTEX_HASH_BOXSIZE + z) & (VERTEX_HASH_SIZE-1);
2269 }
2270 
2271 /*
2272 ================
2273 idCollisionModelManagerLocal::GetVertex
2274 ================
2275 */
2276 int idCollisionModelManagerLocal::GetVertex( cm_model_t *model, const idVec3 &v, int *vertexNum ) {
2277  int i, hashKey, vn;
2278  idVec3 vert, *p;
2279 
2280  for (i = 0; i < 3; i++) {
2281  if ( idMath::Fabs(v[i] - idMath::Rint(v[i])) < INTEGRAL_EPSILON )
2282  vert[i] = idMath::Rint(v[i]);
2283  else
2284  vert[i] = v[i];
2285  }
2286 
2287  hashKey = HashVec( vert );
2288 
2289  for (vn = cm_vertexHash->First( hashKey ); vn >= 0; vn = cm_vertexHash->Next( vn ) ) {
2290  p = &model->vertices[vn].p;
2291  // first compare z-axis because hash is based on x-y plane
2292  if (idMath::Fabs(vert[2] - (*p)[2]) < VERTEX_EPSILON &&
2293  idMath::Fabs(vert[0] - (*p)[0]) < VERTEX_EPSILON &&
2294  idMath::Fabs(vert[1] - (*p)[1]) < VERTEX_EPSILON )
2295  {
2296  *vertexNum = vn;
2297  return true;
2298  }
2299  }
2300 
2301  if ( model->numVertices >= model->maxVertices ) {
2302  cm_vertex_t *oldVertices;
2303 
2304  // resize vertex array
2305  model->maxVertices = (float) model->maxVertices * 1.5f + 1;
2306  oldVertices = model->vertices;
2307  model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) );
2308  memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) );
2309  Mem_Free( oldVertices );
2310 
2311  cm_vertexHash->ResizeIndex( model->maxVertices );
2312  }
2313  model->vertices[model->numVertices].p = vert;
2314  model->vertices[model->numVertices].checkcount = 0;
2315  *vertexNum = model->numVertices;
2316  // add vertice to hash
2317  cm_vertexHash->Add( hashKey, model->numVertices );
2318  //
2319  model->numVertices++;
2320  return false;
2321 }
2322 
2323 /*
2324 ================
2325 idCollisionModelManagerLocal::GetEdge
2326 ================
2327 */
2328 int idCollisionModelManagerLocal::GetEdge( cm_model_t *model, const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num ) {
2329  int v2num, hashKey, e;
2330  int found, *vertexNum;
2331 
2332  // the first edge is a dummy
2333  if ( model->numEdges == 0 ) {
2334  model->numEdges = 1;
2335  }
2336 
2337  if ( v1num != -1 ) {
2338  found = 1;
2339  }
2340  else {
2341  found = GetVertex( model, v1, &v1num );
2342  }
2343  found &= GetVertex( model, v2, &v2num );
2344  // if both vertices are the same or snapped onto each other
2345  if ( v1num == v2num ) {
2346  *edgeNum = 0;
2347  return true;
2348  }
2349  hashKey = cm_edgeHash->GenerateKey( v1num, v2num );
2350  // if both vertices where already stored
2351  if (found) {
2352  for (e = cm_edgeHash->First( hashKey ); e >= 0; e = cm_edgeHash->Next( e ) )
2353  {
2354  // NOTE: only allow at most two users that use the edge in opposite direction
2355  if ( model->edges[e].numUsers != 1 ) {
2356  continue;
2357  }
2358 
2359  vertexNum = model->edges[e].vertexNum;
2360  if ( vertexNum[0] == v2num ) {
2361  if ( vertexNum[1] == v1num ) {
2362  // negative for a reversed edge
2363  *edgeNum = -e;
2364  break;
2365  }
2366  }
2367  /*
2368  else if ( vertexNum[0] == v1num ) {
2369  if ( vertexNum[1] == v2num ) {
2370  *edgeNum = e;
2371  break;
2372  }
2373  }
2374  */
2375  }
2376  // if edge found in hash
2377  if ( e >= 0 ) {
2378  model->edges[e].numUsers++;
2379  return true;
2380  }
2381  }
2382  if ( model->numEdges >= model->maxEdges ) {
2383  cm_edge_t *oldEdges;
2384 
2385  // resize edge array
2386  model->maxEdges = (float) model->maxEdges * 1.5f + 1;
2387  oldEdges = model->edges;
2388  model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) );
2389  memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) );
2390  Mem_Free( oldEdges );
2391 
2392  cm_edgeHash->ResizeIndex( model->maxEdges );
2393  }
2394  // setup edge
2395  model->edges[model->numEdges].vertexNum[0] = v1num;
2396  model->edges[model->numEdges].vertexNum[1] = v2num;
2397  model->edges[model->numEdges].internal = false;
2398  model->edges[model->numEdges].checkcount = 0;
2399  model->edges[model->numEdges].numUsers = 1; // used by one polygon atm
2400  model->edges[model->numEdges].normal.Zero();
2401  //
2402  *edgeNum = model->numEdges;
2403  // add edge to hash
2404  cm_edgeHash->Add( hashKey, model->numEdges );
2405 
2406  model->numEdges++;
2407 
2408  return false;
2409 }
2410 
2411 /*
2412 ================
2413 idCollisionModelManagerLocal::CreatePolygon
2414 ================
2415 */
2416 void idCollisionModelManagerLocal::CreatePolygon( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) {
2417  int i, j, edgeNum, v1num;
2418  int numPolyEdges, polyEdges[MAX_POINTS_ON_WINDING];
2419  idBounds bounds;
2420  cm_polygon_t *p;
2421 
2422  // turn the winding into a sequence of edges
2423  numPolyEdges = 0;
2424  v1num = -1; // first vertex unknown
2425  for ( i = 0, j = 1; i < w->GetNumPoints(); i++, j++ ) {
2426  if ( j >= w->GetNumPoints() ) {
2427  j = 0;
2428  }
2429  GetEdge( model, (*w)[i].ToVec3(), (*w)[j].ToVec3(), &polyEdges[numPolyEdges], v1num );
2430  if ( polyEdges[numPolyEdges] ) {
2431  // last vertex of this edge is the first vertex of the next edge
2432  v1num = model->edges[ abs(polyEdges[numPolyEdges]) ].vertexNum[ INTSIGNBITNOTSET(polyEdges[numPolyEdges]) ];
2433  // this edge is valid so keep it
2434  numPolyEdges++;
2435  }
2436  }
2437  // should have at least 3 edges
2438  if ( numPolyEdges < 3 ) {
2439  return;
2440  }
2441  // the polygon is invalid if some edge is found twice
2442  for ( i = 0; i < numPolyEdges; i++ ) {
2443  for ( j = i+1; j < numPolyEdges; j++ ) {
2444  if ( abs(polyEdges[i]) == abs(polyEdges[j]) ) {
2445  return;
2446  }
2447  }
2448  }
2449  // don't overflow max edges
2450  if ( numPolyEdges > CM_MAX_POLYGON_EDGES ) {
2451  common->Warning( "idCollisionModelManagerLocal::CreatePolygon: polygon has more than %d edges", numPolyEdges );
2452  numPolyEdges = CM_MAX_POLYGON_EDGES;
2453  }
2454 
2455  w->GetBounds( bounds );
2456 
2457  p = AllocPolygon( model, numPolyEdges );
2458  p->numEdges = numPolyEdges;
2459  p->contents = material->GetContentFlags();
2460  p->material = material;
2461  p->checkcount = 0;
2462  p->plane = plane;
2463  p->bounds = bounds;
2464  for ( i = 0; i < numPolyEdges; i++ ) {
2465  edgeNum = polyEdges[i];
2466  p->edges[i] = edgeNum;
2467  }
2468  R_FilterPolygonIntoTree( model, model->node, NULL, p );
2469 }
2470 
2471 /*
2472 ================
2473 idCollisionModelManagerLocal::PolygonFromWinding
2474 
2475  NOTE: for patches primitiveNum < 0 and abs(primitiveNum) is the real number
2476 ================
2477 */
2478 void idCollisionModelManagerLocal::PolygonFromWinding( cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum ) {
2479  int contents;
2480 
2481  contents = material->GetContentFlags();
2482 
2483  // if this polygon is part of the world model
2484  if ( numModels == 0 ) {
2485  // if the polygon is fully chopped away by the proc bsp tree
2486  if ( ChoppedAwayByProcBSP( *w, plane, contents ) ) {
2487  model->numRemovedPolys++;
2488  return;
2489  }
2490  }
2491 
2492  // get one winding that is not or only partly contained in brushes
2493  w = WindingOutsideBrushes( w, plane, contents, primitiveNum, model->node );
2494 
2495  // if the polygon is fully contained within a brush
2496  if ( !w ) {
2497  model->numRemovedPolys++;
2498  return;
2499  }
2500 
2501  if ( w->IsHuge() ) {
2502  common->Warning( "idCollisionModelManagerLocal::PolygonFromWinding: model %s primitive %d is degenerate", model->name.c_str(), abs(primitiveNum) );
2503  return;
2504  }
2505 
2506  CreatePolygon( model, w, plane, material, primitiveNum );
2507 
2508  if ( material->GetCullType() == CT_TWO_SIDED || material->ShouldCreateBackSides() ) {
2509  w->ReverseSelf();
2510  CreatePolygon( model, w, -plane, material, primitiveNum );
2511  }
2512 }
2513 
2514 /*
2515 =================
2516 idCollisionModelManagerLocal::CreatePatchPolygons
2517 =================
2518 */
2519 void idCollisionModelManagerLocal::CreatePatchPolygons( cm_model_t *model, idSurface_Patch &mesh, const idMaterial *material, int primitiveNum ) {
2520  int i, j;
2521  float dot;
2522  int v1, v2, v3, v4;
2523  idFixedWinding w;
2524  idPlane plane;
2525  idVec3 d1, d2;
2526 
2527  for ( i = 0; i < mesh.GetWidth() - 1; i++ ) {
2528  for ( j = 0; j < mesh.GetHeight() - 1; j++ ) {
2529 
2530  v1 = j * mesh.GetWidth() + i;
2531  v2 = v1 + 1;
2532  v3 = v1 + mesh.GetWidth() + 1;
2533  v4 = v1 + mesh.GetWidth();
2534 
2535  d1 = mesh[v2].xyz - mesh[v1].xyz;
2536  d2 = mesh[v3].xyz - mesh[v1].xyz;
2537  plane.SetNormal( d1.Cross(d2) );
2538  if ( plane.Normalize() != 0.0f ) {
2539  plane.FitThroughPoint( mesh[v1].xyz );
2540  dot = plane.Distance( mesh[v4].xyz );
2541  // if we can turn it into a quad
2542  if ( idMath::Fabs(dot) < 0.1f ) {
2543  w.Clear();
2544  w += mesh[v1].xyz;
2545  w += mesh[v2].xyz;
2546  w += mesh[v3].xyz;
2547  w += mesh[v4].xyz;
2548 
2549  PolygonFromWinding( model, &w, plane, material, -primitiveNum );
2550  continue;
2551  }
2552  else {
2553  // create one of the triangles
2554  w.Clear();
2555  w += mesh[v1].xyz;
2556  w += mesh[v2].xyz;
2557  w += mesh[v3].xyz;
2558 
2559  PolygonFromWinding( model, &w, plane, material, -primitiveNum );
2560  }
2561  }
2562  // create the other triangle
2563  d1 = mesh[v3].xyz - mesh[v1].xyz;
2564  d2 = mesh[v4].xyz - mesh[v1].xyz;
2565  plane.SetNormal( d1.Cross(d2) );
2566  if ( plane.Normalize() != 0.0f ) {
2567  plane.FitThroughPoint( mesh[v1].xyz );
2568 
2569  w.Clear();
2570  w += mesh[v1].xyz;
2571  w += mesh[v3].xyz;
2572  w += mesh[v4].xyz;
2573 
2574  PolygonFromWinding( model, &w, plane, material, -primitiveNum );
2575  }
2576  }
2577  }
2578 }
2579 
2580 /*
2581 =================
2582 CM_EstimateVertsAndEdges
2583 =================
2584 */
2585 static void CM_EstimateVertsAndEdges( const idMapEntity *mapEnt, int *numVerts, int *numEdges ) {
2586  int j, width, height;
2587 
2588  *numVerts = *numEdges = 0;
2589  for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) {
2590  const idMapPrimitive *mapPrim;
2591  mapPrim = mapEnt->GetPrimitive(j);
2592  if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) {
2593  // assume maximum tesselation without adding verts
2594  width = static_cast<const idMapPatch*>(mapPrim)->GetWidth();
2595  height = static_cast<const idMapPatch*>(mapPrim)->GetHeight();
2596  *numVerts += width * height;
2597  *numEdges += (width-1) * height + width * (height-1) + (width-1) * (height-1);
2598  continue;
2599  }
2600  if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
2601  // assume cylinder with a polygon with (numSides - 2) edges ontop and on the bottom
2602  *numVerts += (static_cast<const idMapBrush*>(mapPrim)->GetNumSides() - 2) * 2;
2603  *numEdges += (static_cast<const idMapBrush*>(mapPrim)->GetNumSides() - 2) * 3;
2604  continue;
2605  }
2606  }
2607 }
2608 
2609 /*
2610 =================
2611 idCollisionModelManagerLocal::ConverPatch
2612 =================
2613 */
2614 void idCollisionModelManagerLocal::ConvertPatch( cm_model_t *model, const idMapPatch *patch, int primitiveNum ) {
2615  const idMaterial *material;
2616  idSurface_Patch *cp;
2617 
2618  material = declManager->FindMaterial( patch->GetMaterial() );
2619  if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
2620  return;
2621  }
2622 
2623  // copy the patch
2624  cp = new idSurface_Patch( *patch );
2625 
2626  // if the patch has an explicit number of subdivisions use it to avoid cracks
2627  if ( patch->GetExplicitlySubdivided() ) {
2628  cp->SubdivideExplicit( patch->GetHorzSubdivisions(), patch->GetVertSubdivisions(), false, true );
2629  } else {
2631  }
2632 
2633  // create collision polygons for the patch
2634  CreatePatchPolygons( model, *cp, material, primitiveNum );
2635 
2636  delete cp;
2637 }
2638 
2639 /*
2640 ================
2641 idCollisionModelManagerLocal::ConvertBrushSides
2642 ================
2643 */
2644 void idCollisionModelManagerLocal::ConvertBrushSides( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) {
2645  int i, j;
2646  idMapBrushSide *mapSide;
2647  idFixedWinding w;
2648  idPlane *planes;
2649  const idMaterial *material;
2650 
2651  // fix degenerate planes
2652  planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) );
2653  for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
2654  planes[i] = mapBrush->GetSide(i)->GetPlane();
2656  }
2657 
2658  // create a collision polygon for each brush side
2659  for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
2660  mapSide = mapBrush->GetSide(i);
2661  material = declManager->FindMaterial( mapSide->GetMaterial() );
2662  if ( !( material->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
2663  continue;
2664  }
2665  w.BaseForPlane( -planes[i] );
2666  for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) {
2667  if ( i == j ) {
2668  continue;
2669  }
2670  w.ClipInPlace( -planes[j], 0 );
2671  }
2672 
2673  if ( w.GetNumPoints() ) {
2674  PolygonFromWinding( model, &w, planes[i], material, primitiveNum );
2675  }
2676  }
2677 }
2678 
2679 /*
2680 ================
2681 idCollisionModelManagerLocal::ConvertBrush
2682 ================
2683 */
2684 void idCollisionModelManagerLocal::ConvertBrush( cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum ) {
2685  int i, j, contents;
2686  idBounds bounds;
2687  idMapBrushSide *mapSide;
2688  cm_brush_t *brush;
2689  idPlane *planes;
2690  idFixedWinding w;
2691  const idMaterial *material = NULL;
2692 
2693  contents = 0;
2694  bounds.Clear();
2695 
2696  // fix degenerate planes
2697  planes = (idPlane *) _alloca16( mapBrush->GetNumSides() * sizeof( planes[0] ) );
2698  for ( i = 0; i < mapBrush->GetNumSides(); i++ ) {
2699  planes[i] = mapBrush->GetSide(i)->GetPlane();
2701  }
2702 
2703  // we are only getting the bounds for the brush so there's no need
2704  // to create a winding for the last brush side
2705  for ( i = 0; i < mapBrush->GetNumSides() - 1; i++ ) {
2706  mapSide = mapBrush->GetSide(i);
2707  material = declManager->FindMaterial( mapSide->GetMaterial() );
2708  contents |= ( material->GetContentFlags() & CONTENTS_REMOVE_UTIL );
2709  w.BaseForPlane( -planes[i] );
2710  for ( j = 0; j < mapBrush->GetNumSides() && w.GetNumPoints(); j++ ) {
2711  if ( i == j ) {
2712  continue;
2713  }
2714  w.ClipInPlace( -planes[j], 0 );
2715  }
2716 
2717  for ( j = 0; j < w.GetNumPoints(); j++ ) {
2718  bounds.AddPoint( w[j].ToVec3() );
2719  }
2720  }
2721  if ( !contents ) {
2722  return;
2723  }
2724  // create brush for position test
2725  brush = AllocBrush( model, mapBrush->GetNumSides() );
2726  brush->checkcount = 0;
2727  brush->contents = contents;
2728  brush->material = material;
2729  brush->primitiveNum = primitiveNum;
2730  brush->bounds = bounds;
2731  brush->numPlanes = mapBrush->GetNumSides();
2732  for (i = 0; i < mapBrush->GetNumSides(); i++) {
2733  brush->planes[i] = planes[i];
2734  }
2735  AddBrushToNode( model, model->node, brush );
2736 }
2737 
2738 /*
2739 ================
2740 CM_CountNodeBrushes
2741 ================
2742 */
2743 static int CM_CountNodeBrushes( const cm_node_t *node ) {
2744  int count;
2745  cm_brushRef_t *bref;
2746 
2747  count = 0;
2748  for ( bref = node->brushes; bref; bref = bref->next ) {
2749  count++;
2750  }
2751  return count;
2752 }
2753 
2754 /*
2755 ================
2756 CM_R_GetModelBounds
2757 ================
2758 */
2759 static void CM_R_GetNodeBounds( idBounds *bounds, cm_node_t *node ) {
2760  cm_polygonRef_t *pref;
2761  cm_brushRef_t *bref;
2762 
2763  while ( 1 ) {
2764  for ( pref = node->polygons; pref; pref = pref->next ) {
2765  bounds->AddPoint( pref->p->bounds[0] );
2766  bounds->AddPoint( pref->p->bounds[1] );
2767  }
2768  for ( bref = node->brushes; bref; bref = bref->next ) {
2769  bounds->AddPoint( bref->b->bounds[0] );
2770  bounds->AddPoint( bref->b->bounds[1] );
2771  }
2772  if ( node->planeType == -1 ) {
2773  break;
2774  }
2775  CM_R_GetNodeBounds( bounds, node->children[1] );
2776  node = node->children[0];
2777  }
2778 }
2779 
2780 /*
2781 ================
2782 CM_GetNodeBounds
2783 ================
2784 */
2785 void CM_GetNodeBounds( idBounds *bounds, cm_node_t *node ) {
2786  bounds->Clear();
2787  CM_R_GetNodeBounds( bounds, node );
2788  if ( bounds->IsCleared() ) {
2789  bounds->Zero();
2790  }
2791 }
2792 
2793 /*
2794 ================
2795 CM_GetNodeContents
2796 ================
2797 */
2799  int contents;
2800  cm_polygonRef_t *pref;
2801  cm_brushRef_t *bref;
2802 
2803  contents = 0;
2804  while ( 1 ) {
2805  for ( pref = node->polygons; pref; pref = pref->next ) {
2806  contents |= pref->p->contents;
2807  }
2808  for ( bref = node->brushes; bref; bref = bref->next ) {
2809  contents |= bref->b->contents;
2810  }
2811  if ( node->planeType == -1 ) {
2812  break;
2813  }
2814  contents |= CM_GetNodeContents( node->children[1] );
2815  node = node->children[0];
2816  }
2817  return contents;
2818 }
2819 
2820 /*
2821 ==================
2822 idCollisionModelManagerLocal::RemapEdges
2823 ==================
2824 */
2826  cm_polygonRef_t *pref;
2827  cm_polygon_t *p;
2828  int i;
2829 
2830  while ( 1 ) {
2831  for ( pref = node->polygons; pref; pref = pref->next ) {
2832  p = pref->p;
2833  // if we checked this polygon already
2834  if ( p->checkcount == checkCount ) {
2835  continue;
2836  }
2837  p->checkcount = checkCount;
2838  for ( i = 0; i < p->numEdges; i++ ) {
2839  if ( p->edges[i] < 0 ) {
2840  p->edges[i] = -edgeRemap[ abs(p->edges[i]) ];
2841  }
2842  else {
2843  p->edges[i] = edgeRemap[ p->edges[i] ];
2844  }
2845  }
2846  }
2847  if ( node->planeType == -1 ) {
2848  break;
2849  }
2850 
2851  RemapEdges( node->children[1], edgeRemap );
2852  node = node->children[0];
2853  }
2854 }
2855 
2856 /*
2857 ==================
2858 idCollisionModelManagerLocal::OptimizeArrays
2859 
2860  due to polygon merging and polygon removal the vertex and edge array
2861  can have a lot of unused entries.
2862 ==================
2863 */
2865  int i, newNumVertices, newNumEdges, *v;
2866  int *remap;
2867  cm_edge_t *oldEdges;
2868  cm_vertex_t *oldVertices;
2869 
2870  remap = (int *) Mem_ClearedAlloc( Max( model->numVertices, model->numEdges ) * sizeof( int ) );
2871  // get all used vertices
2872  for ( i = 0; i < model->numEdges; i++ ) {
2873  remap[ model->edges[i].vertexNum[0] ] = true;
2874  remap[ model->edges[i].vertexNum[1] ] = true;
2875  }
2876  // create remap index and move vertices
2877  newNumVertices = 0;
2878  for ( i = 0; i < model->numVertices; i++ ) {
2879  if ( remap[ i ] ) {
2880  remap[ i ] = newNumVertices;
2881  model->vertices[ newNumVertices ] = model->vertices[ i ];
2882  newNumVertices++;
2883  }
2884  }
2885  model->numVertices = newNumVertices;
2886  // change edge vertex indexes
2887  for ( i = 1; i < model->numEdges; i++ ) {
2888  v = model->edges[i].vertexNum;
2889  v[0] = remap[ v[0] ];
2890  v[1] = remap[ v[1] ];
2891  }
2892 
2893  // create remap index and move edges
2894  newNumEdges = 1;
2895  for ( i = 1; i < model->numEdges; i++ ) {
2896  // if the edge is used
2897  if ( model->edges[ i ].numUsers ) {
2898  remap[ i ] = newNumEdges;
2899  model->edges[ newNumEdges ] = model->edges[ i ];
2900  newNumEdges++;
2901  }
2902  }
2903  // change polygon edge indexes
2904  checkCount++;
2905  RemapEdges( model->node, remap );
2906  model->numEdges = newNumEdges;
2907 
2908  Mem_Free( remap );
2909 
2910  // realloc vertices
2911  oldVertices = model->vertices;
2912  if ( oldVertices ) {
2913  model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->numVertices * sizeof(cm_vertex_t) );
2914  memcpy( model->vertices, oldVertices, model->numVertices * sizeof(cm_vertex_t) );
2915  Mem_Free( oldVertices );
2916  }
2917 
2918  // realloc edges
2919  oldEdges = model->edges;
2920  if ( oldEdges ) {
2921  model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->numEdges * sizeof(cm_edge_t) );
2922  memcpy( model->edges, oldEdges, model->numEdges * sizeof(cm_edge_t) );
2923  Mem_Free( oldEdges );
2924  }
2925 }
2926 
2927 /*
2928 ================
2929 idCollisionModelManagerLocal::FinishModel
2930 ================
2931 */
2933  // try to merge polygons
2934  checkCount++;
2935  MergeTreePolygons( model, model->node );
2936  // find internal edges (no mesh can ever collide with internal edges)
2937  checkCount++;
2938  FindInternalEdges( model, model->node );
2939  // calculate edge normals
2940  checkCount++;
2941  CalculateEdgeNormals( model, model->node );
2942 
2943  //common->Printf( "%s vertex hash spread is %d\n", model->name.c_str(), cm_vertexHash->GetSpread() );
2944  //common->Printf( "%s edge hash spread is %d\n", model->name.c_str(), cm_edgeHash->GetSpread() );
2945 
2946  // remove all unused vertices and edges
2947  OptimizeArrays( model );
2948  // get model bounds from brush and polygon bounds
2949  CM_GetNodeBounds( &model->bounds, model->node );
2950  // get model contents
2951  model->contents = CM_GetNodeContents( model->node );
2952  // total memory used by this model
2953  model->usedMemory = model->numVertices * sizeof(cm_vertex_t) +
2954  model->numEdges * sizeof(cm_edge_t) +
2955  model->polygonMemory +
2956  model->brushMemory +
2957  model->numNodes * sizeof(cm_node_t) +
2958  model->numPolygonRefs * sizeof(cm_polygonRef_t) +
2959  model->numBrushRefs * sizeof(cm_brushRef_t);
2960 }
2961 
2962 /*
2963 ================
2964 idCollisionModelManagerLocal::LoadRenderModel
2965 ================
2966 */
2968  int i, j;
2969  idRenderModel *renderModel;
2970  const modelSurface_t *surf;
2971  idFixedWinding w;
2972  cm_node_t *node;
2973  cm_model_t *model;
2974  idPlane plane;
2975  idBounds bounds;
2976  bool collisionSurface;
2977  idStr extension;
2978 
2979  // only load ASE and LWO models
2980  idStr( fileName ).ExtractFileExtension( extension );
2981  if ( ( extension.Icmp( "ase" ) != 0 ) && ( extension.Icmp( "lwo" ) != 0 ) && ( extension.Icmp( "ma" ) != 0 ) ) {
2982  return NULL;
2983  }
2984 
2985  if ( !renderModelManager->CheckModel( fileName ) ) {
2986  return NULL;
2987  }
2988 
2989  renderModel = renderModelManager->FindModel( fileName );
2990 
2991  model = AllocModel();
2992  model->name = fileName;
2993  node = AllocNode( model, NODE_BLOCK_SIZE_SMALL );
2994  node->planeType = -1;
2995  model->node = node;
2996 
2997  model->maxVertices = 0;
2998  model->numVertices = 0;
2999  model->maxEdges = 0;
3000  model->numEdges = 0;
3001 
3002  bounds = renderModel->Bounds( NULL );
3003 
3004  collisionSurface = false;
3005  for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
3006  surf = renderModel->Surface( i );
3007  if ( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) {
3008  collisionSurface = true;
3009  }
3010  }
3011 
3012  for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
3013  surf = renderModel->Surface( i );
3014  // if this surface has no contents
3015  if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
3016  continue;
3017  }
3018  // if the model has a collision surface and this surface is not a collision surface
3019  if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) {
3020  continue;
3021  }
3022  // get max verts and edges
3023  model->maxVertices += surf->geometry->numVerts;
3024  model->maxEdges += surf->geometry->numIndexes;
3025  }
3026 
3027  model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) );
3028  model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) );
3029 
3030  // setup hash to speed up finding shared vertices and edges
3031  SetupHash();
3032 
3033  cm_vertexHash->ResizeIndex( model->maxVertices );
3034  cm_edgeHash->ResizeIndex( model->maxEdges );
3035 
3036  ClearHash( bounds );
3037 
3038  for ( i = 0; i < renderModel->NumSurfaces(); i++ ) {
3039  surf = renderModel->Surface( i );
3040  // if this surface has no contents
3041  if ( ! ( surf->shader->GetContentFlags() & CONTENTS_REMOVE_UTIL ) ) {
3042  continue;
3043  }
3044  // if the model has a collision surface and this surface is not a collision surface
3045  if ( collisionSurface && !( surf->shader->GetSurfaceFlags() & SURF_COLLISION ) ) {
3046  continue;
3047  }
3048 
3049  for ( j = 0; j < surf->geometry->numIndexes; j += 3 ) {
3050  w.Clear();
3051  w += surf->geometry->verts[ surf->geometry->indexes[ j + 2 ] ].xyz;
3052  w += surf->geometry->verts[ surf->geometry->indexes[ j + 1 ] ].xyz;
3053  w += surf->geometry->verts[ surf->geometry->indexes[ j + 0 ] ].xyz;
3054  w.GetPlane( plane );
3055  plane = -plane;
3056  PolygonFromWinding( model, &w, plane, surf->shader, 1 );
3057  }
3058  }
3059 
3060  // create a BSP tree for the model
3061  model->node = CreateAxialBSPTree( model, model->node );
3062 
3063  model->isConvex = false;
3064 
3065  FinishModel( model );
3066 
3067  // shutdown the hash
3068  ShutdownHash();
3069 
3070  common->Printf( "loaded collision model %s\n", model->name.c_str() );
3071 
3072  return model;
3073 }
3074 
3075 /*
3076 ================
3077 idCollisionModelManagerLocal::CollisionModelForMapEntity
3078 ================
3079 */
3081  cm_model_t *model;
3082  idBounds bounds;
3083  const char *name;
3084  int i, brushCount;
3085 
3086  // if the entity has no primitives
3087  if ( mapEnt->GetNumPrimitives() < 1 ) {
3088  return NULL;
3089  }
3090 
3091  // get a name for the collision model
3092  mapEnt->epairs.GetString( "model", "", &name );
3093  if ( !name[0] ) {
3094  mapEnt->epairs.GetString( "name", "", &name );
3095  if ( !name[0] ) {
3096  if ( !numModels ) {
3097  // first model is always the world
3098  name = "worldMap";
3099  }
3100  else {
3101  name = "unnamed inline model";
3102  }
3103  }
3104  }
3105 
3106  model = AllocModel();
3107  model->node = AllocNode( model, NODE_BLOCK_SIZE_SMALL );
3108 
3109  CM_EstimateVertsAndEdges( mapEnt, &model->maxVertices, &model->maxEdges );
3110  model->numVertices = 0;
3111  model->numEdges = 0;
3112  model->vertices = (cm_vertex_t *) Mem_ClearedAlloc( model->maxVertices * sizeof(cm_vertex_t) );
3113  model->edges = (cm_edge_t *) Mem_ClearedAlloc( model->maxEdges * sizeof(cm_edge_t) );
3114 
3115  cm_vertexHash->ResizeIndex( model->maxVertices );
3116  cm_edgeHash->ResizeIndex( model->maxEdges );
3117 
3118  model->name = name;
3119  model->isConvex = false;
3120 
3121  // convert brushes
3122  for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) {
3123  idMapPrimitive *mapPrim;
3124 
3125  mapPrim = mapEnt->GetPrimitive(i);
3126  if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
3127  ConvertBrush( model, static_cast<idMapBrush*>(mapPrim), i );
3128  continue;
3129  }
3130  }
3131 
3132  // create an axial bsp tree for the model if it has more than just a bunch brushes
3133  brushCount = CM_CountNodeBrushes( model->node );
3134  if ( brushCount > 4 ) {
3135  model->node = CreateAxialBSPTree( model, model->node );
3136  } else {
3137  model->node->planeType = -1;
3138  }
3139 
3140  // get bounds for hash
3141  if ( brushCount ) {
3142  CM_GetNodeBounds( &bounds, model->node );
3143  } else {
3144  bounds[0].Set( -256, -256, -256 );
3145  bounds[1].Set( 256, 256, 256 );
3146  }
3147 
3148  // different models do not share edges and vertices with each other, so clear the hash
3149  ClearHash( bounds );
3150 
3151  // create polygons from patches and brushes
3152  for ( i = 0; i < mapEnt->GetNumPrimitives(); i++ ) {
3153  idMapPrimitive *mapPrim;
3154 
3155  mapPrim = mapEnt->GetPrimitive(i);
3156  if ( mapPrim->GetType() == idMapPrimitive::TYPE_PATCH ) {
3157  ConvertPatch( model, static_cast<idMapPatch*>(mapPrim), i );
3158  continue;
3159  }
3160  if ( mapPrim->GetType() == idMapPrimitive::TYPE_BRUSH ) {
3161  ConvertBrushSides( model, static_cast<idMapBrush*>(mapPrim), i );
3162  continue;
3163  }
3164  }
3165 
3166  FinishModel( model );
3167 
3168  return model;
3169 }
3170 
3171 /*
3172 ================
3173 idCollisionModelManagerLocal::FindModel
3174 ================
3175 */
3177  int i;
3178 
3179  // check if this model is already loaded
3180  for ( i = 0; i < numModels; i++ ) {
3181  if ( !models[i]->name.Icmp( name ) ) {
3182  break;
3183  }
3184  }
3185  // if the model is already loaded
3186  if ( i < numModels ) {
3187  return i;
3188  }
3189  return -1;
3190 }
3191 
3192 /*
3193 ==================
3194 idCollisionModelManagerLocal::PrintModelInfo
3195 ==================
3196 */
3198  common->Printf( "%6i vertices (%i KB)\n", model->numVertices, (model->numVertices * sizeof(cm_vertex_t))>>10 );
3199  common->Printf( "%6i edges (%i KB)\n", model->numEdges, (model->numEdges * sizeof(cm_edge_t))>>10 );
3200  common->Printf( "%6i polygons (%i KB)\n", model->numPolygons, model->polygonMemory>>10 );
3201  common->Printf( "%6i brushes (%i KB)\n", model->numBrushes, model->brushMemory>>10 );
3202  common->Printf( "%6i nodes (%i KB)\n", model->numNodes, (model->numNodes * sizeof(cm_node_t))>>10 );
3203  common->Printf( "%6i polygon refs (%i KB)\n", model->numPolygonRefs, (model->numPolygonRefs * sizeof(cm_polygonRef_t))>>10 );
3204  common->Printf( "%6i brush refs (%i KB)\n", model->numBrushRefs, (model->numBrushRefs * sizeof(cm_brushRef_t))>>10 );
3205  common->Printf( "%6i internal edges\n", model->numInternalEdges );
3206  common->Printf( "%6i sharp edges\n", model->numSharpEdges );
3207  common->Printf( "%6i contained polygons removed\n", model->numRemovedPolys );
3208  common->Printf( "%6i polygons merged\n", model->numMergedPolys );
3209  common->Printf( "%6i KB total memory used\n", model->usedMemory>>10 );
3210 }
3211 
3212 /*
3213 ================
3214 idCollisionModelManagerLocal::AccumulateModelInfo
3215 ================
3216 */
3218  int i;
3219 
3220  memset( model, 0, sizeof( *model ) );
3221  // accumulate statistics of all loaded models
3222  for ( i = 0; i < numModels; i++ ) {
3223  model->numVertices += models[i]->numVertices;
3224  model->numEdges += models[i]->numEdges;
3225  model->numPolygons += models[i]->numPolygons;
3226  model->polygonMemory += models[i]->polygonMemory;
3227  model->numBrushes += models[i]->numBrushes;
3228  model->brushMemory += models[i]->brushMemory;
3229  model->numNodes += models[i]->numNodes;
3230  model->numBrushRefs += models[i]->numBrushRefs;
3231  model->numPolygonRefs += models[i]->numPolygonRefs;
3233  model->numSharpEdges += models[i]->numSharpEdges;
3235  model->numMergedPolys += models[i]->numMergedPolys;
3236  model->usedMemory += models[i]->usedMemory;
3237  }
3238 }
3239 
3240 /*
3241 ================
3242 idCollisionModelManagerLocal::ModelInfo
3243 ================
3244 */
3246  cm_model_t modelInfo;
3247 
3248  if ( model == -1 ) {
3249  AccumulateModelInfo( &modelInfo );
3250  PrintModelInfo( &modelInfo );
3251  return;
3252  }
3253  if ( model < 0 || model > MAX_SUBMODELS || model > maxModels ) {
3254  common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model handle\n" );
3255  return;
3256  }
3257  if ( !models[model] ) {
3258  common->Printf( "idCollisionModelManagerLocal::ModelInfo: invalid model\n" );
3259  return;
3260  }
3261 
3262  PrintModelInfo( models[model] );
3263 }
3264 
3265 /*
3266 ================
3267 idCollisionModelManagerLocal::ListModels
3268 ================
3269 */
3271  int i, totalMemory;
3272 
3273  totalMemory = 0;
3274  for ( i = 0; i < numModels; i++ ) {
3275  common->Printf( "%4d: %5d KB %s\n", i, (models[i]->usedMemory>>10), models[i]->name.c_str() );
3276  totalMemory += models[i]->usedMemory;
3277  }
3278  common->Printf( "%4d KB in %d models\n", (totalMemory>>10), numModels );
3279 }
3280 
3281 /*
3282 ================
3283 idCollisionModelManagerLocal::BuildModels
3284 ================
3285 */
3287  int i;
3288  const idMapEntity *mapEnt;
3289 
3290  idTimer timer;
3291  timer.Start();
3292 
3293  if ( !LoadCollisionModelFile( mapFile->GetName(), mapFile->GetGeometryCRC() ) ) {
3294 
3295  if ( !mapFile->GetNumEntities() ) {
3296  return;
3297  }
3298 
3299  // load the .proc file bsp for data optimisation
3300  LoadProcBSP( mapFile->GetName() );
3301 
3302  // convert brushes and patches to collision data
3303  for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
3304  mapEnt = mapFile->GetEntity(i);
3305 
3306  if ( numModels >= MAX_SUBMODELS ) {
3307  common->Error( "idCollisionModelManagerLocal::BuildModels: more than %d collision models", MAX_SUBMODELS );
3308  break;
3309  }
3311  if ( models[ numModels] ) {
3312  numModels++;
3313  }
3314  }
3315 
3316  // free the proc bsp which is only used for data optimization
3317  Mem_Free( procNodes );
3318  procNodes = NULL;
3319 
3320  // write the collision models to a file
3321  WriteCollisionModelsToFile( mapFile->GetName(), 0, numModels, mapFile->GetGeometryCRC() );
3322  }
3323 
3324  timer.Stop();
3325 
3326  // print statistics on collision data
3327  cm_model_t model;
3328  AccumulateModelInfo( &model );
3329  common->Printf( "collision data:\n" );
3330  common->Printf( "%6i models\n", numModels );
3331  PrintModelInfo( &model );
3332  common->Printf( "%.0f msec to load collision data.\n", timer.Milliseconds() );
3333 }
3334 
3335 
3336 /*
3337 ================
3338 idCollisionModelManagerLocal::LoadMap
3339 ================
3340 */
3342 
3343  if ( mapFile == NULL ) {
3344  common->Error( "idCollisionModelManagerLocal::LoadMap: NULL mapFile" );
3345  }
3346 
3347  // check whether we can keep the current collision map based on the mapName and mapFileTime
3348  if ( loaded ) {
3349  if ( mapName.Icmp( mapFile->GetName() ) == 0 ) {
3350  if ( mapFile->GetFileTime() == mapFileTime ) {
3351  common->DPrintf( "Using loaded version\n" );
3352  return;
3353  }
3354  common->DPrintf( "Reloading modified map\n" );
3355  }
3356  FreeMap();
3357  }
3358 
3359  // clear the collision map
3360  Clear();
3361 
3362  // models
3364  numModels = 0;
3365  models = (cm_model_t **) Mem_ClearedAlloc( (maxModels+1) * sizeof(cm_model_t *) );
3366 
3367  // setup hash to speed up finding shared vertices and edges
3368  SetupHash();
3369 
3370  // setup trace model structure
3372 
3373  // build collision models
3374  BuildModels( mapFile );
3375 
3376  // save name and time stamp
3377  mapName = mapFile->GetName();
3378  mapFileTime = mapFile->GetFileTime();
3379  loaded = true;
3380 
3381  // shutdown the hash
3382  ShutdownHash();
3383 }
3384 
3385 /*
3386 ===================
3387 idCollisionModelManagerLocal::GetModelName
3388 ===================
3389 */
3391  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3392  common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" );
3393  return "";
3394  }
3395  return models[model]->name.c_str();
3396 }
3397 
3398 /*
3399 ===================
3400 idCollisionModelManagerLocal::GetModelBounds
3401 ===================
3402 */
3404 
3405  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3406  common->Printf( "idCollisionModelManagerLocal::GetModelBounds: invalid model handle\n" );
3407  return false;
3408  }
3409 
3410  bounds = models[model]->bounds;
3411  return true;
3412 }
3413 
3414 /*
3415 ===================
3416 idCollisionModelManagerLocal::GetModelContents
3417 ===================
3418 */
3420  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3421  common->Printf( "idCollisionModelManagerLocal::GetModelContents: invalid model handle\n" );
3422  return false;
3423  }
3424 
3425  contents = models[model]->contents;
3426 
3427  return true;
3428 }
3429 
3430 /*
3431 ===================
3432 idCollisionModelManagerLocal::GetModelVertex
3433 ===================
3434 */
3435 bool idCollisionModelManagerLocal::GetModelVertex( cmHandle_t model, int vertexNum, idVec3 &vertex ) const {
3436  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3437  common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid model handle\n" );
3438  return false;
3439  }
3440 
3441  if ( vertexNum < 0 || vertexNum >= models[model]->numVertices ) {
3442  common->Printf( "idCollisionModelManagerLocal::GetModelVertex: invalid vertex number\n" );
3443  return false;
3444  }
3445 
3446  vertex = models[model]->vertices[vertexNum].p;
3447 
3448  return true;
3449 }
3450 
3451 /*
3452 ===================
3453 idCollisionModelManagerLocal::GetModelEdge
3454 ===================
3455 */
3457  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3458  common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid model handle\n" );
3459  return false;
3460  }
3461 
3462  edgeNum = abs( edgeNum );
3463  if ( edgeNum >= models[model]->numEdges ) {
3464  common->Printf( "idCollisionModelManagerLocal::GetModelEdge: invalid edge number\n" );
3465  return false;
3466  }
3467 
3468  start = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[0]].p;
3469  end = models[model]->vertices[models[model]->edges[edgeNum].vertexNum[1]].p;
3470 
3471  return true;
3472 }
3473 
3474 /*
3475 ===================
3476 idCollisionModelManagerLocal::GetModelPolygon
3477 ===================
3478 */
3479 bool idCollisionModelManagerLocal::GetModelPolygon( cmHandle_t model, int polygonNum, idFixedWinding &winding ) const {
3480  int i, edgeNum;
3481  cm_polygon_t *poly;
3482 
3483  if ( model < 0 || model > MAX_SUBMODELS || model >= numModels || !models[model] ) {
3484  common->Printf( "idCollisionModelManagerLocal::GetModelPolygon: invalid model handle\n" );
3485  return false;
3486  }
3487 
3488  poly = *reinterpret_cast<cm_polygon_t **>(&polygonNum);
3489  winding.Clear();
3490  for ( i = 0; i < poly->numEdges; i++ ) {
3491  edgeNum = poly->edges[i];
3492  winding += models[model]->vertices[ models[model]->edges[abs(edgeNum)].vertexNum[INTSIGNBITSET(edgeNum)] ].p;
3493  }
3494 
3495  return true;
3496 }
3497 
3498 /*
3499 ==================
3500 idCollisionModelManagerLocal::LoadModel
3501 ==================
3502 */
3503 cmHandle_t idCollisionModelManagerLocal::LoadModel( const char *modelName, const bool precache ) {
3504  int handle;
3505 
3506  handle = FindModel( modelName );
3507  if ( handle >= 0 ) {
3508  return handle;
3509  }
3510 
3511  if ( numModels >= MAX_SUBMODELS ) {
3512  common->Error( "idCollisionModelManagerLocal::LoadModel: no free slots\n" );
3513  return 0;
3514  }
3515 
3516  // try to load a .cm file
3517  if ( LoadCollisionModelFile( modelName, 0 ) ) {
3518  handle = FindModel( modelName );
3519  if ( handle >= 0 ) {
3520  return handle;
3521  } else {
3522  common->Warning( "idCollisionModelManagerLocal::LoadModel: collision file for '%s' contains different model", modelName );
3523  }
3524  }
3525 
3526  // if only precaching .cm files do not waste memory converting render models
3527  if ( precache ) {
3528  return 0;
3529  }
3530 
3531  // try to load a .ASE or .LWO model and convert it to a collision model
3532  models[numModels] = LoadRenderModel( modelName );
3533  if ( models[numModels] != NULL ) {
3534  numModels++;
3535  return ( numModels - 1 );
3536  }
3537 
3538  return 0;
3539 }
3540 
3541 /*
3542 ==================
3543 idCollisionModelManagerLocal::TrmFromModel_r
3544 ==================
3545 */
3547  cm_polygonRef_t *pref;
3548  cm_polygon_t *p;
3549  int i;
3550 
3551  while ( 1 ) {
3552  for ( pref = node->polygons; pref; pref = pref->next ) {
3553  p = pref->p;
3554 
3555  if ( p->checkcount == checkCount ) {
3556  continue;
3557  }
3558 
3559  p->checkcount = checkCount;
3560 
3561  if ( trm.numPolys >= MAX_TRACEMODEL_POLYS ) {
3562  return false;
3563  }
3564  // copy polygon properties
3565  trm.polys[ trm.numPolys ].bounds = p->bounds;
3566  trm.polys[ trm.numPolys ].normal = p->plane.Normal();
3567  trm.polys[ trm.numPolys ].dist = p->plane.Dist();
3568  trm.polys[ trm.numPolys ].numEdges = p->numEdges;
3569  // copy edge index
3570  for ( i = 0; i < p->numEdges; i++ ) {
3571  trm.polys[ trm.numPolys ].edges[ i ] = p->edges[ i ];
3572  }
3573  trm.numPolys++;
3574  }
3575  if ( node->planeType == -1 ) {
3576  break;
3577  }
3578  if ( !TrmFromModel_r( trm, node->children[1] ) ) {
3579  return false;
3580  }
3581  node = node->children[0];
3582  }
3583  return true;
3584 }
3585 
3586 /*
3587 ==================
3588 idCollisionModelManagerLocal::TrmFromModel
3589 
3590  NOTE: polygon merging can merge colinear edges and as such might cause dangling edges.
3591 ==================
3592 */
3594  int i, j, numEdgeUsers[MAX_TRACEMODEL_EDGES+1];
3595 
3596  // if the model has too many vertices to fit in a trace model
3597  if ( model->numVertices > MAX_TRACEMODEL_VERTS ) {
3598  common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many vertices.\n", model->name.c_str() );
3599  PrintModelInfo( model );
3600  return false;
3601  }
3602 
3603  // plus one because the collision model accounts for the first unused edge
3604  if ( model->numEdges > MAX_TRACEMODEL_EDGES+1 ) {
3605  common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many edges.\n", model->name.c_str() );
3606  PrintModelInfo( model );
3607  return false;
3608  }
3609 
3610  trm.type = TRM_CUSTOM;
3611  trm.numVerts = 0;
3612  trm.numEdges = 1;
3613  trm.numPolys = 0;
3614  trm.bounds.Clear();
3615 
3616  // copy polygons
3617  checkCount++;
3618  if ( !TrmFromModel_r( trm, model->node ) ) {
3619  common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has too many polygons.\n", model->name.c_str() );
3620  PrintModelInfo( model );
3621  return false;
3622  }
3623 
3624  // copy vertices
3625  for ( i = 0; i < model->numVertices; i++ ) {
3626  trm.verts[ i ] = model->vertices[ i ].p;
3627  trm.bounds.AddPoint( trm.verts[ i ] );
3628  }
3629  trm.numVerts = model->numVertices;
3630 
3631  // copy edges
3632  for ( i = 0; i < model->numEdges; i++ ) {
3633  trm.edges[ i ].v[0] = model->edges[ i ].vertexNum[0];
3634  trm.edges[ i ].v[1] = model->edges[ i ].vertexNum[1];
3635  }
3636  // minus one because the collision model accounts for the first unused edge
3637  trm.numEdges = model->numEdges - 1;
3638 
3639  // each edge should be used exactly twice
3640  memset( numEdgeUsers, 0, sizeof(numEdgeUsers) );
3641  for ( i = 0; i < trm.numPolys; i++ ) {
3642  for ( j = 0; j < trm.polys[i].numEdges; j++ ) {
3643  numEdgeUsers[ abs( trm.polys[i].edges[j] ) ]++;
3644  }
3645  }
3646  for ( i = 1; i <= trm.numEdges; i++ ) {
3647  if ( numEdgeUsers[i] != 2 ) {
3648  common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s has dangling edges, the model has to be an enclosed hull.\n", model->name.c_str() );
3649  PrintModelInfo( model );
3650  return false;
3651  }
3652  }
3653 
3654  // assume convex
3655  trm.isConvex = true;
3656  // check if really convex
3657  for ( i = 0; i < trm.numPolys; i++ ) {
3658  // to be convex no vertices should be in front of any polygon plane
3659  for ( j = 0; j < trm.numVerts; j++ ) {
3660  if ( trm.polys[ i ].normal * trm.verts[ j ] - trm.polys[ i ].dist > 0.01f ) {
3661  trm.isConvex = false;
3662  break;
3663  }
3664  }
3665  if ( j < trm.numVerts ) {
3666  break;
3667  }
3668  }
3669 
3670  // offset to center of model
3671  trm.offset = trm.bounds.GetCenter();
3672 
3673  trm.GenerateEdgeNormals();
3674 
3675  return true;
3676 }
3677 
3678 /*
3679 ==================
3680 idCollisionModelManagerLocal::TrmFromModel
3681 ==================
3682 */
3683 bool idCollisionModelManagerLocal::TrmFromModel( const char *modelName, idTraceModel &trm ) {
3684  cmHandle_t handle;
3685 
3686  handle = LoadModel( modelName, false );
3687  if ( !handle ) {
3688  common->Printf( "idCollisionModelManagerLocal::TrmFromModel: model %s not found.\n", modelName );
3689  return false;
3690  }
3691 
3692  return TrmFromModel( models[ handle ], trm );
3693 }
int GetNumSides(void) const
Definition: MapFile.h:106
void WriteCollisionModelsToFile(const char *filename, int firstModel, int lastModel, unsigned int mapFileCRC)
traceModel_t type
Definition: TraceModel.h:86
const idPlane & GetPlane(void) const
Definition: MapFile.h:78
void GetBounds(idBounds &bounds) const
Definition: Winding.cpp:700
void ResizeIndex(const int newIndexSize)
Definition: HashIndex.cpp:92
int GetType(void) const
Definition: MapFile.h:63
struct cm_vertex_s cm_vertex_t
struct cm_node_s * children[2]
const float DEFAULT_CURVE_MAX_ERROR_CD
Definition: MapFile.h:50
idMapEntity * GetEntity(int i) const
Definition: MapFile.h:198
void MergeTreePolygons(cm_model_t *model, cm_node_t *node)
float Normalize(void)
Definition: Vector.h:646
cmHandle_t LoadModel(const char *modelName, const bool precache)
idHashIndex * cm_edgeHash
idStr & SetFileExtension(const char *extension)
Definition: Str.cpp:743
cm_nodeBlock_t * nodeBlocks
unsigned int GetGeometryCRC(void) const
Definition: MapFile.h:205
assert(prefInfo.fullscreenBtn)
bool MergePolygonWithTreePolygons(cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon)
const idVec3 & Normal(void) const
Definition: Plane.h:239
idMapBrushSide * GetSide(int i) const
Definition: MapFile.h:108
#define SIDE_CROSS
Definition: Plane.h:50
idVec3 GetCenter(void) const
Definition: Bounds.h:211
int GetVertSubdivisions(void) const
Definition: MapFile.h:127
#define CHOP_EPSILON
unsigned short numUsers
void FreeBrushReference(cm_brushRef_t *bref)
int Next(const int index) const
Definition: HashIndex.h:247
void FreePolygonReference(cm_polygonRef_t *pref)
#define CONTINUOUS_EPSILON
int numVerts
Definition: Model.h:98
Definition: Timer.h:40
void Zero(void)
Definition: Bounds.h:206
GLdouble GLdouble GLint vn
Definition: qgl.h:345
cmHandle_t SetupTrmModel(const idTraceModel &trm, const idMaterial *material)
void FindInternalEdges(cm_model_t *model, cm_node_t *node)
traceModelVert_t verts[MAX_TRACEMODEL_VERTS]
Definition: TraceModel.h:88
const GLdouble * v
Definition: glext.h:2936
#define SIDE_BACK
Definition: Plane.h:48
void FreePolygon(cm_model_t *model, cm_polygon_t *poly)
ID_INLINE T Max(T x, T y)
Definition: Lib.h:158
float Distance(const idVec3 &v) const
Definition: Plane.h:324
int CM_GetNodeContents(cm_node_t *node)
int Parse1DMatrix(int x, float *m)
Definition: Lexer.cpp:1300
idVec3 xyz
Definition: DrawVert.h:42
GLenum GLint GLint y
Definition: glext.h:2849
void RemoveBrushReferences_r(cm_node_t *node, cm_brush_t *b)
GLenum GLsizei n
Definition: glext.h:3705
void ConvertBrushSides(cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum)
case const int
Definition: Callbacks.cpp:52
cm_polygon_t * AllocPolygon(cm_model_t *model, int numEdges)
const char * GetModelName(cmHandle_t model) const
const idMaterial * shader
Definition: Model.h:146
int SkipBracedSection(bool parseFirstBrace=true)
Definition: Lexer.cpp:1134
struct cm_nodeBlock_s * next
struct cm_polygonRef_s * next
int GetHorzSubdivisions(void) const
Definition: MapFile.h:126
void RemovePolygonReferences_r(cm_node_t *node, cm_polygon_t *p)
const float * ToFloatPtr(void) const
Definition: Plane.h:383
unsigned short internal
Definition: Vector.h:316
case const float
Definition: Callbacks.cpp:62
const idMaterial * material
const char * GetMaterial(void) const
Definition: MapFile.h:124
void ReverseSelf(void)
Definition: Winding.cpp:495
void Start(void)
Definition: Timer.h:144
void FreeBrush(cm_model_t *model, cm_brush_t *brush)
void Clear(void)
Definition: Bounds.h:201
GLuint GLuint GLsizei GLenum type
Definition: glext.h:2845
int cmHandle_t
#define VERTEX_EPSILON
Definition: Token.h:71
traceModelEdge_t edges[MAX_TRACEMODEL_EDGES+1]
Definition: TraceModel.h:90
cm_brushRefBlock_t * brushRefBlocks
virtual const idMaterial * FindMaterial(const char *name, bool makeDefault=true)=0
GLdouble s
Definition: glext.h:2935
GLuint src
Definition: glext.h:5390
void SetNormal(const idVec3 &normal)
Definition: Plane.h:233
double Milliseconds(void) const
Definition: Timer.h:191
idVec3 Cross(const idVec3 &a) const
Definition: Vector.h:619
cm_polygonRef_t * trmPolygons[MAX_TRACEMODEL_POLYS]
static float Rint(float f)
Definition: Math.h:793
GLenum GLint x
Definition: glext.h:2849
void PrintModelInfo(const cm_model_t *model)
int i
Definition: process.py:33
int ParseInt(void)
Definition: Lexer.cpp:1227
void R_FilterPolygonIntoTree(cm_model_t *model, cm_node_t *node, cm_polygonRef_t *pref, cm_polygon_t *p)
#define REFERENCE_BLOCK_SIZE_LARGE
cm_polygonRef_t * nextRef
int Split(idFixedWinding *back, const idPlane &plane, const float epsilon=ON_EPSILON)
Definition: Winding.cpp:1484
int Icmp(const char *text) const
Definition: Str.h:667
void SubdivideExplicit(int horzSubdivisions, int vertSubdivisions, bool genNormals, bool removeLinear=false)
#define DEGENERATE_DIST_EPSILON
Definition: Plane.h:45
#define PROC_FILE_EXT
Definition: RenderWorld.h:40
bool PointInsidePolygon(cm_model_t *model, cm_polygon_t *p, idVec3 &v)
void LoadProcBSP(const char *name)
int First(const int key) const
Definition: HashIndex.h:238
idDict epairs
Definition: MapFile.h:166
#define PROC_FILE_ID
Definition: RenderWorld.h:41
void ReplacePolygons(cm_model_t *model, cm_node_t *node, cm_polygon_t *p1, cm_polygon_t *p2, cm_polygon_t *newp)
int GetVertex(cm_model_t *model, const idVec3 &v, int *vertexNum)
#define VERTEX_HASH_BOXSIZE
int GetHeight(void) const
#define MAX_TRACEMODEL_POLYS
Definition: TraceModel.h:65
#define NORMAL_EPSILON
int R_ChoppedAwayByProcBSP(int nodeNum, idFixedWinding *w, const idVec3 &normal, const idVec3 &origin, const float radius)
#define PLANESIDE_FRONT
Definition: Plane.h:53
void AccumulateModelInfo(cm_model_t *model)
idVec3 offset
Definition: TraceModel.h:93
bool AddPoint(const idVec3 &v)
Definition: Bounds.h:226
GLfloat GLfloat GLfloat v2
Definition: glext.h:3608
struct cm_model_s cm_model_t
Definition: Lexer.h:137
void Error(const char *str,...) id_attribute((format(printf
Definition: Lexer.cpp:215
void CM_GetNodeBounds(idBounds *bounds, cm_node_t *node)
#define MAX_SUBMODELS
bool LoadCollisionModelFile(const char *name, unsigned int mapFileCRC)
cm_node_t * node
GLuint GLuint GLsizei count
Definition: glext.h:2845
cm_vertex_t * vertices
void Subdivide(float maxHorizontalError, float maxVerticalError, float maxLength, bool genNormals=false)
void FreeTree_r(cm_model_t *model, cm_node_t *headNode, cm_node_t *node)
#define NODE_BLOCK_SIZE_LARGE
int GetNumPoints(void) const
Definition: Winding.h:238
#define MAX_POINTS_ON_WINDING
Definition: Winding.h:279
void FinishModel(cm_model_t *model)
const char * GetString(const char *key, const char *defaultString="") const
Definition: Dict.h:240
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:3454
struct cm_polygonRef_s cm_polygonRef_t
float Length(void) const
Definition: Vector.h:631
const cullType_t GetCullType(void) const
Definition: Material.h:531
void SetDist(const float dist)
Definition: Plane.h:275
void ConvertPatch(cm_model_t *model, const idMapPatch *patch, int primitiveNum)
const idMaterial * material
bool GetModelPolygon(cmHandle_t model, int polygonNum, idFixedWinding &winding) const
cm_polygon_t * TryMergePolygons(cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2)
int GetNumEntities(void) const
Definition: MapFile.h:196
bool IsHuge(void) const
Definition: Winding.cpp:1226
const float DEFAULT_CURVE_MAX_LENGTH_CD
Definition: MapFile.h:52
cm_brushBlock_t * brushBlock
GLuint GLuint end
Definition: glext.h:2845
idFixedWinding * WindingOutsideBrushes(idFixedWinding *w, const idPlane &plane, int contents, int patch, cm_node_t *headNode)
int ChoppedAwayByProcBSP(const idFixedWinding &w, const idPlane &plane, int contents)
static float Fabs(float f)
Definition: Math.h:779
#define INTSIGNBITNOTSET(i)
Definition: Math.h:72
idCommon * common
Definition: Common.cpp:206
#define EDGE_HASH_SIZE
#define PLANESIDE_BACK
Definition: Plane.h:54
#define NULL
Definition: Lib.h:88
srfTriangles_t * geometry
Definition: Model.h:147
ID_TIME_T GetFileTime(void) const
Definition: MapFile.h:202
int edges[MAX_TRACEMODEL_POLYEDGES]
Definition: TraceModel.h:80
virtual idRenderModel * FindModel(const char *modelName)=0
void R_FilterBrushIntoTree(cm_model_t *model, cm_node_t *node, cm_brushRef_t *pref, cm_brush_t *b)
const int GetContentFlags(void) const
Definition: Material.h:497
virtual void Clear(void)
Definition: Winding.h:398
cm_polygonRef_t * AllocPolygonReference(cm_model_t *model, int blockSize)
void FreeModel(cm_model_t *model)
cm_brushRef_t * brushes
cm_node_t * R_CreateAxialBSPTree(cm_model_t *model, cm_node_t *node, const idBounds &bounds)
virtual const modelSurface_t * Surface(int surfaceNum) const =0
struct cm_polygonRefBlock_s * next
void GetPlane(idVec3 &normal, float &dist) const
Definition: Winding.cpp:656
virtual void virtual void FatalError(const char *fmt,...) id_attribute((format(printf
Definition: Plane.h:71
idCollisionModelManager * collisionModelManager
cm_polygonBlock_t * polygonBlock
const int GetSurfaceFlags(void) const
Definition: Material.h:500
bool GetExplicitlySubdivided(void) const
Definition: MapFile.h:128
struct cm_brush_s cm_brush_t
void AddBrushToNode(cm_model_t *model, cm_node_t *node, cm_brush_t *b)
void Mem_Free(void *ptr)
Definition: Heap.cpp:1087
int GetEdge(cm_model_t *model, const idVec3 &v1, const idVec3 &v2, int *edgeNum, int v1num)
void CalculateEdgeNormals(cm_model_t *model, cm_node_t *node)
void Clear(void)
Definition: Str.h:724
void FindInternalEdgesOnPolygon(cm_model_t *model, cm_polygon_t *p1, cm_polygon_t *p2)
#define CM_MAX_POLYGON_EDGES
void RemapEdges(cm_node_t *node, int *edgeRemap)
GLenum GLsizei width
Definition: glext.h:2846
void FindInternalPolygonEdges(cm_model_t *model, cm_node_t *node, cm_polygon_t *polygon)
idBounds cm_modelBounds
virtual void Printf(const char *fmt,...) id_attribute((format(printf
idBounds bounds
Definition: TraceModel.h:78
bool isConvex
Definition: TraceModel.h:95
float Normalize(bool fixDegenerate=true)
Definition: Plane.h:247
#define NODE_BLOCK_SIZE_SMALL
virtual idBounds Bounds(const struct renderEntity_s *ent=NULL) const =0
cm_node_t * AllocNode(cm_model_t *model, int blockSize)
void CreatePolygon(cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum)
cm_windingList_t * cm_windingList
GLfloat GLfloat v1
Definition: glext.h:3607
GLenum GLsizei GLsizei height
Definition: glext.h:2856
cm_edge_t * edges
idDeclManager * declManager
int GenerateKey(const char *string, bool caseSensitive=true) const
Definition: HashIndex.h:379
GLubyte GLubyte b
Definition: glext.h:4662
void Stop(void)
Definition: Timer.h:155
bool GetModelBounds(cmHandle_t model, idBounds &bounds) const
struct cm_polygon_s cm_polygon_t
void AddPolygonToNode(cm_model_t *model, cm_node_t *node, cm_polygon_t *p)
int ExpectTokenString(const char *string)
Definition: Lexer.cpp:919
#define MAX_TRACEMODEL_EDGES
Definition: TraceModel.h:64
idFixedWinding w[MAX_WINDING_LIST]
GLfloat GLfloat GLfloat GLfloat v3
Definition: glext.h:3609
#define SIDE_ON
Definition: Plane.h:49
cmHandle_t FindModel(const char *name)
bool GetModelEdge(cmHandle_t model, int edgeNum, idVec3 &start, idVec3 &end) const
idRenderModelManager * renderModelManager
cm_model_t * CollisionModelForMapEntity(const idMapEntity *mapEnt)
idMapPrimitive * GetPrimitive(int i) const
Definition: MapFile.h:174
#define REFERENCE_BLOCK_SIZE_SMALL
void R_ChopWindingListWithTreeBrushes(cm_windingList_t *list, cm_node_t *node)
#define INTSIGNBITSET(i)
Definition: Math.h:71
tuple f
Definition: idal.py:89
struct cm_brushRefBlock_s * next
cm_brushRef_t * AllocBrushReference(cm_model_t *model, int blockSize)
bool IsCleared(void) const
Definition: Bounds.h:222
void PolygonFromWinding(cm_model_t *model, idFixedWinding *w, const idPlane &plane, const idMaterial *material, int primitiveNum)
unsigned char byte
Definition: Lib.h:75
bool TrmFromModel_r(idTraceModel &trm, cm_node_t *node)
void FindContainedEdges(cm_model_t *model, cm_polygon_t *p)
const GLcharARB * name
Definition: glext.h:3629
GLsizeiptr size
Definition: glext.h:3112
struct cm_node_s cm_node_t
bool ShouldCreateBackSides(void) const
Definition: Material.h:412
struct cm_node_s * parent
void BaseForPlane(const idVec3 &normal, const float dist)
Definition: Winding.cpp:66
Definition: Str.h:116
bool GetModelVertex(cmHandle_t model, int vertexNum, idVec3 &vertex) const
struct cm_brushRef_s * next
void OptimizeArrays(cm_model_t *model)
void * Mem_ClearedAlloc(const int size)
Definition: Heap.cpp:1149
const char * GetMaterial(void) const
Definition: MapFile.h:76
const char * c_str(void) const
Definition: Str.h:487
traceModelPoly_t polys[MAX_TRACEMODEL_POLYS]
Definition: TraceModel.h:92
glIndex_t * indexes
Definition: Model.h:102
#define INTEGRAL_EPSILON
int GenerateEdgeNormals(void)
Definition: TraceModel.cpp:971
idHashIndex * cm_vertexHash
void Zero(void)
Definition: Plane.h:229
void Clear(void)
Definition: HashIndex.h:328
#define VERTEX_HASH_SIZE
#define MAX_TRACEMODEL_VERTS
Definition: TraceModel.h:63
GLuint res
Definition: glext.h:5385
unsigned char bool
Definition: setup.h:74
void Add(const int key, const int index)
Definition: HashIndex.h:193
void * Mem_Alloc(const int size)
Definition: Heap.cpp:1067
idCollisionModelManagerLocal collisionModelManagerLocal
GLint j
Definition: qgl.h:264
float dot(float a[], float b[])
Definition: Model_lwo.cpp:3883
virtual idRenderModel * CheckModel(const char *modelName)=0
cm_windingList_t * cm_outList
void ChopWindingListWithBrush(cm_windingList_t *list, cm_brush_t *b)
int numIndexes
Definition: Model.h:101
struct cm_brushRef_s cm_brushRef_t
int IsLoaded(void)
Definition: Lexer.h:158
if(!ValidDisplayID(prefInfo.prefDisplayID)) prefInfo.prefDisplayID
void CreatePatchPolygons(cm_model_t *model, idSurface_Patch &mesh, const idMaterial *material, int primitiveNum)
#define MAX_TRACEMODEL_POLYEDGES
Definition: TraceModel.h:66
bool FixDegeneracies(float distEpsilon)
Definition: Plane.h:260
bool TrmFromModel(const char *modelName, idTraceModel &trm)
#define MAX_NODE_POLYGONS
void ConvertBrush(cm_model_t *model, const idMapBrush *mapBrush, int primitiveNum)
void ExtractFileExtension(idStr &dest) const
Definition: Str.cpp:965
int PlaneSide(const idPlane &plane, const float epsilon=ON_EPSILON) const
Definition: Bounds.cpp:108
cm_windingList_t * cm_tmpList
cm_polygonRef_t * polygons
cm_polygonRefBlock_t * polygonRefBlocks
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
#define max(x, y)
Definition: os.h:70
virtual void Error(const char *fmt,...) id_attribute((format(printf
void LoadMap(const idMapFile *mapFile)
GLfloat GLfloat p
Definition: glext.h:4674
int GetNumPrimitives(void) const
Definition: MapFile.h:173
cm_node_t * CreateAxialBSPTree(cm_model_t *model, cm_node_t *node)
#define SIDE_FRONT
Definition: Plane.h:47
GLdouble GLdouble z
Definition: glext.h:3067
bool ClipInPlace(const idPlane &plane, const float epsilon=ON_EPSILON, const bool keepOn=false)
Definition: Winding.cpp:349
void Zero(void)
Definition: Vector.h:415
int cm_vertexShift
virtual void virtual void Warning(const char *fmt,...) id_attribute((format(printf
const char * GetName(void) const
Definition: MapFile.h:200
cm_model_t * LoadRenderModel(const char *fileName)
idBounds bounds
Definition: TraceModel.h:94
bool GetModelContents(cmHandle_t model, int &contents) const
int ReadToken(idToken *token)
Definition: Lexer.cpp:820
#define TRACE_MODEL_HANDLE
void BuildModels(const idMapFile *mapFile)
GLuint start
Definition: glext.h:2845
float Dist(void) const
Definition: Plane.h:271
idDrawVert * verts
Definition: Model.h:99
struct cm_windingList_s cm_windingList_t
int GetWidth(void) const
#define MAX_WINDING_LIST
void FitThroughPoint(const idVec3 &p)
Definition: Plane.h:297
GLdouble GLdouble t
Definition: glext.h:2943
#define MIN_NODE_SIZE
#define SHARP_EDGE_DOT
cm_brush_t * AllocBrush(cm_model_t *model, int numPlanes)
virtual int NumSurfaces() const =0