DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

CollisionModel_load.cpp (116238B)


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