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 }