Clip.cpp (47815B)
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 #pragma hdrstop 30 #include "../../idlib/precompiled.h" 31 32 33 #include "../Game_local.h" 34 35 #define MAX_SECTOR_DEPTH 12 36 #define MAX_SECTORS ((1<<(MAX_SECTOR_DEPTH+1))-1) 37 38 typedef struct clipSector_s { 39 int axis; // -1 = leaf node 40 float dist; 41 struct clipSector_s * children[2]; 42 struct clipLink_s * clipLinks; 43 } clipSector_t; 44 45 typedef struct clipLink_s { 46 idClipModel * clipModel; 47 struct clipSector_s * sector; 48 struct clipLink_s * prevInSector; 49 struct clipLink_s * nextInSector; 50 struct clipLink_s * nextLink; 51 } clipLink_t; 52 53 typedef struct trmCache_s { 54 idTraceModel trm; 55 int refCount; 56 float volume; 57 idVec3 centerOfMass; 58 idMat3 inertiaTensor; 59 } trmCache_t; 60 61 idVec3 vec3_boxEpsilon( CM_BOX_EPSILON, CM_BOX_EPSILON, CM_BOX_EPSILON ); 62 63 idBlockAlloc<clipLink_t, 1024> clipLinkAllocator; 64 65 66 /* 67 =============================================================== 68 69 idClipModel trace model cache 70 71 =============================================================== 72 */ 73 74 static idList<trmCache_s*> traceModelCache; 75 static idList<trmCache_s*> traceModelCache_Unsaved; 76 static idHashIndex traceModelHash; 77 static idHashIndex traceModelHash_Unsaved; 78 const static int TRACE_MODEL_SAVED = BIT( 16 ); 79 80 81 /* 82 =============== 83 idClipModel::ClearTraceModelCache 84 =============== 85 */ 86 void idClipModel::ClearTraceModelCache() { 87 traceModelCache.DeleteContents( true ); 88 traceModelCache_Unsaved.DeleteContents( true ); 89 traceModelHash.Free(); 90 traceModelHash_Unsaved.Free(); 91 } 92 93 /* 94 =============== 95 idClipModel::TraceModelCacheSize 96 =============== 97 */ 98 int idClipModel::TraceModelCacheSize() { 99 return traceModelCache.Num() * sizeof( idTraceModel ); 100 } 101 102 /* 103 =============== 104 idClipModel::AllocTraceModel 105 =============== 106 */ 107 int idClipModel::AllocTraceModel( const idTraceModel &trm, bool persistantThroughSaves ) { 108 int i, hashKey, traceModelIndex; 109 trmCache_t *entry; 110 111 hashKey = GetTraceModelHashKey( trm ); 112 113 if( persistantThroughSaves ) { 114 // Look Inside the saved list. 115 for ( i = traceModelHash.First( hashKey ); i >= 0; i = traceModelHash.Next( i ) ) { 116 if ( traceModelCache[i]->trm == trm ) { 117 traceModelCache[i]->refCount++; 118 int flagged_index = i | TRACE_MODEL_SAVED; 119 return flagged_index; 120 } 121 } 122 } else { 123 124 // Look inside the unsaved list. 125 for ( i = traceModelHash_Unsaved.First( hashKey ); i >= 0; i = traceModelHash_Unsaved.Next( i ) ) { 126 if ( traceModelCache_Unsaved[i]->trm == trm ) { 127 traceModelCache_Unsaved[i]->refCount++; 128 return i; 129 } 130 } 131 } 132 133 134 entry = new (TAG_PHYSICS_CLIP) trmCache_t; 135 entry->trm = trm; 136 entry->trm.GetMassProperties( 1.0f, entry->volume, entry->centerOfMass, entry->inertiaTensor ); 137 entry->refCount = 1; 138 139 if( persistantThroughSaves ) { 140 traceModelIndex = traceModelCache.Append( entry ); 141 traceModelHash.Add( hashKey, traceModelIndex ); 142 143 // Set the saved bit. 144 traceModelIndex |= TRACE_MODEL_SAVED; 145 146 } else { 147 traceModelIndex = traceModelCache_Unsaved.Append( entry ); 148 traceModelHash_Unsaved.Add( hashKey, traceModelIndex ); 149 150 // remove the saved bit 151 traceModelIndex &= ~TRACE_MODEL_SAVED; 152 153 } 154 155 return traceModelIndex; 156 } 157 158 /* 159 =============== 160 idClipModel::FreeTraceModel 161 =============== 162 */ 163 void idClipModel::FreeTraceModel( int traceModelIndex ) { 164 165 int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED; 166 167 // Check which cache we are using. 168 if( traceModelIndex & TRACE_MODEL_SAVED ) { 169 170 if ( realTraceModelIndex < 0 || realTraceModelIndex >= traceModelCache.Num() || traceModelCache[realTraceModelIndex]->refCount <= 0 ) { 171 gameLocal.Warning( "idClipModel::FreeTraceModel: tried to free uncached trace model" ); 172 return; 173 } 174 traceModelCache[realTraceModelIndex]->refCount--; 175 176 } else { 177 178 if ( realTraceModelIndex < 0 || realTraceModelIndex >= traceModelCache_Unsaved.Num() || traceModelCache_Unsaved[realTraceModelIndex]->refCount <= 0 ) { 179 gameLocal.Warning( "idClipModel::FreeTraceModel: tried to free uncached trace model" ); 180 return; 181 } 182 traceModelCache_Unsaved[realTraceModelIndex]->refCount--; 183 184 } 185 } 186 187 /* 188 =============== 189 idClipModel::GetCachedTraceModel 190 =============== 191 */ 192 idTraceModel *idClipModel::GetCachedTraceModel( int traceModelIndex ) { 193 int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED; 194 195 if( traceModelIndex & TRACE_MODEL_SAVED ) { 196 return &traceModelCache[realTraceModelIndex]->trm; 197 } else { 198 return &traceModelCache_Unsaved[realTraceModelIndex]->trm; 199 } 200 } 201 202 /* 203 =============== 204 idClipModel::GetCachedTraceModel 205 =============== 206 */ 207 trmCache_t * idClipModel::GetTraceModelEntry( int traceModelIndex ) { 208 209 int realTraceModelIndex = traceModelIndex & ~TRACE_MODEL_SAVED; 210 211 if( traceModelIndex & TRACE_MODEL_SAVED ) { 212 return traceModelCache[realTraceModelIndex]; 213 } else { 214 return traceModelCache_Unsaved[realTraceModelIndex]; 215 } 216 } 217 218 /* 219 =============== 220 idClipModel::GetTraceModelHashKey 221 =============== 222 */ 223 int idClipModel::GetTraceModelHashKey( const idTraceModel &trm ) { 224 const idVec3 &v = trm.bounds[0]; 225 return ( trm.type << 8 ) ^ ( trm.numVerts << 4 ) ^ ( trm.numEdges << 2 ) ^ ( trm.numPolys << 0 ) ^ idMath::FloatHash( v.ToFloatPtr(), v.GetDimension() ); 226 } 227 228 /* 229 =============== 230 idClipModel::SaveTraceModels 231 =============== 232 */ 233 void idClipModel::SaveTraceModels( idSaveGame *savefile ) { 234 int i; 235 236 savefile->WriteInt( traceModelCache.Num() ); 237 for ( i = 0; i < traceModelCache.Num(); i++ ) { 238 trmCache_t *entry = traceModelCache[i]; 239 240 savefile->WriteTraceModel( entry->trm ); 241 savefile->WriteFloat( entry->volume ); 242 savefile->WriteVec3( entry->centerOfMass ); 243 savefile->WriteMat3( entry->inertiaTensor ); 244 } 245 } 246 247 /* 248 =============== 249 idClipModel::RestoreTraceModels 250 =============== 251 */ 252 void idClipModel::RestoreTraceModels( idRestoreGame *savefile ) { 253 int i, num; 254 255 ClearTraceModelCache(); 256 257 savefile->ReadInt( num ); 258 traceModelCache.SetNum( num ); 259 260 for ( i = 0; i < num; i++ ) { 261 trmCache_t *entry = new (TAG_PHYSICS_CLIP) trmCache_t; 262 263 savefile->ReadTraceModel( entry->trm ); 264 265 savefile->ReadFloat( entry->volume ); 266 savefile->ReadVec3( entry->centerOfMass ); 267 savefile->ReadMat3( entry->inertiaTensor ); 268 entry->refCount = 0; 269 270 traceModelCache[i] = entry; 271 traceModelHash.Add( GetTraceModelHashKey( entry->trm ), i ); 272 } 273 } 274 275 276 /* 277 =============================================================== 278 279 idClipModel 280 281 =============================================================== 282 */ 283 284 /* 285 ================ 286 idClipModel::LoadModel 287 ================ 288 */ 289 bool idClipModel::LoadModel( const char *name ) { 290 renderModelHandle = -1; 291 if ( traceModelIndex != -1 ) { 292 FreeTraceModel( traceModelIndex ); 293 traceModelIndex = -1; 294 } 295 collisionModelHandle = collisionModelManager->LoadModel( name ); 296 if ( collisionModelHandle ) { 297 collisionModelManager->GetModelBounds( collisionModelHandle, bounds ); 298 collisionModelManager->GetModelContents( collisionModelHandle, contents ); 299 return true; 300 } else { 301 bounds.Zero(); 302 return false; 303 } 304 } 305 306 /* 307 ================ 308 idClipModel::LoadModel 309 ================ 310 */ 311 void idClipModel::LoadModel( const idTraceModel &trm, bool persistantThroughSave ) { 312 collisionModelHandle = 0; 313 renderModelHandle = -1; 314 if ( traceModelIndex != -1 ) { 315 FreeTraceModel( traceModelIndex ); 316 } 317 traceModelIndex = AllocTraceModel( trm, persistantThroughSave ); 318 bounds = trm.bounds; 319 } 320 321 /* 322 ================ 323 idClipModel::LoadModel 324 ================ 325 */ 326 void idClipModel::LoadModel( const int renderModelHandle ) { 327 collisionModelHandle = 0; 328 this->renderModelHandle = renderModelHandle; 329 if ( renderModelHandle != -1 ) { 330 const renderEntity_t *renderEntity = gameRenderWorld->GetRenderEntity( renderModelHandle ); 331 if ( renderEntity ) { 332 bounds = renderEntity->bounds; 333 } 334 } 335 if ( traceModelIndex != -1 ) { 336 FreeTraceModel( traceModelIndex ); 337 traceModelIndex = -1; 338 } 339 } 340 341 /* 342 ================ 343 idClipModel::Init 344 ================ 345 */ 346 void idClipModel::Init() { 347 enabled = true; 348 entity = NULL; 349 id = 0; 350 owner = NULL; 351 origin.Zero(); 352 axis.Identity(); 353 bounds.Zero(); 354 absBounds.Zero(); 355 material = NULL; 356 contents = CONTENTS_BODY; 357 collisionModelHandle = 0; 358 renderModelHandle = -1; 359 traceModelIndex = -1; 360 clipLinks = NULL; 361 touchCount = -1; 362 } 363 364 /* 365 ================ 366 idClipModel::idClipModel 367 ================ 368 */ 369 idClipModel::idClipModel() { 370 Init(); 371 } 372 373 /* 374 ================ 375 idClipModel::idClipModel 376 ================ 377 */ 378 idClipModel::idClipModel( const char *name ) { 379 Init(); 380 LoadModel( name ); 381 } 382 383 /* 384 ================ 385 idClipModel::idClipModel 386 ================ 387 */ 388 idClipModel::idClipModel( const idTraceModel &trm ) { 389 Init(); 390 LoadModel( trm, true ); 391 } 392 393 /* 394 ================ 395 idClipModel::idClipModel 396 ================ 397 */ 398 idClipModel::idClipModel( const idTraceModel &trm, bool persistantThroughSave ) { 399 Init(); 400 LoadModel( trm, persistantThroughSave ); 401 } 402 403 /* 404 ================ 405 idClipModel::idClipModel 406 ================ 407 */ 408 idClipModel::idClipModel( const int renderModelHandle ) { 409 Init(); 410 contents = CONTENTS_RENDERMODEL; 411 LoadModel( renderModelHandle ); 412 } 413 414 /* 415 ================ 416 idClipModel::idClipModel 417 ================ 418 */ 419 idClipModel::idClipModel( const idClipModel *model ) { 420 enabled = model->enabled; 421 entity = model->entity; 422 id = model->id; 423 owner = model->owner; 424 origin = model->origin; 425 axis = model->axis; 426 bounds = model->bounds; 427 absBounds = model->absBounds; 428 material = model->material; 429 contents = model->contents; 430 collisionModelHandle = model->collisionModelHandle; 431 traceModelIndex = -1; 432 if ( model->traceModelIndex != -1 ) { 433 LoadModel( *GetCachedTraceModel( model->traceModelIndex ) ); 434 } 435 renderModelHandle = model->renderModelHandle; 436 clipLinks = NULL; 437 touchCount = -1; 438 } 439 440 /* 441 ================ 442 idClipModel::~idClipModel 443 ================ 444 */ 445 idClipModel::~idClipModel() { 446 // make sure the clip model is no longer linked 447 Unlink(); 448 if ( traceModelIndex != -1 ) { 449 FreeTraceModel( traceModelIndex ); 450 } 451 } 452 453 /* 454 ================ 455 idClipModel::Save 456 ================ 457 */ 458 void idClipModel::Save( idSaveGame *savefile ) const { 459 savefile->WriteBool( enabled ); 460 savefile->WriteObject( entity ); 461 savefile->WriteInt( id ); 462 savefile->WriteObject( owner ); 463 savefile->WriteVec3( origin ); 464 savefile->WriteMat3( axis ); 465 savefile->WriteBounds( bounds ); 466 savefile->WriteBounds( absBounds ); 467 savefile->WriteMaterial( material ); 468 savefile->WriteInt( contents ); 469 if ( collisionModelHandle >= 0 ) { 470 savefile->WriteString( collisionModelManager->GetModelName( collisionModelHandle ) ); 471 } else { 472 savefile->WriteString( "" ); 473 } 474 savefile->WriteInt( traceModelIndex ); 475 savefile->WriteInt( renderModelHandle ); 476 savefile->WriteBool( clipLinks != NULL ); 477 savefile->WriteInt( touchCount ); 478 } 479 480 /* 481 ================ 482 idClipModel::Restore 483 ================ 484 */ 485 void idClipModel::Restore( idRestoreGame *savefile ) { 486 idStr collisionModelName; 487 bool linked; 488 489 savefile->ReadBool( enabled ); 490 savefile->ReadObject( reinterpret_cast<idClass *&>( entity ) ); 491 savefile->ReadInt( id ); 492 savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) ); 493 savefile->ReadVec3( origin ); 494 savefile->ReadMat3( axis ); 495 savefile->ReadBounds( bounds ); 496 savefile->ReadBounds( absBounds ); 497 savefile->ReadMaterial( material ); 498 savefile->ReadInt( contents ); 499 savefile->ReadString( collisionModelName ); 500 if ( collisionModelName.Length() ) { 501 collisionModelHandle = collisionModelManager->LoadModel( collisionModelName ); 502 } else { 503 collisionModelHandle = -1; 504 } 505 savefile->ReadInt( traceModelIndex ); 506 if ( traceModelIndex >= 0 ) { 507 int realIndex = traceModelIndex & ~TRACE_MODEL_SAVED; 508 traceModelCache[realIndex]->refCount++; 509 } 510 savefile->ReadInt( renderModelHandle ); 511 savefile->ReadBool( linked ); 512 savefile->ReadInt( touchCount ); 513 514 // the render model will be set when the clip model is linked 515 renderModelHandle = -1; 516 clipLinks = NULL; 517 touchCount = -1; 518 519 if ( linked ) { 520 Link( gameLocal.clip, entity, id, origin, axis, renderModelHandle ); 521 } 522 } 523 524 /* 525 ================ 526 idClipModel::SetPosition 527 ================ 528 */ 529 void idClipModel::SetPosition( const idVec3 &newOrigin, const idMat3 &newAxis ) { 530 if ( clipLinks ) { 531 Unlink(); // unlink from old position 532 } 533 origin = newOrigin; 534 axis = newAxis; 535 } 536 537 /* 538 ================ 539 idClipModel::Handle 540 ================ 541 */ 542 cmHandle_t idClipModel::Handle() const { 543 assert( renderModelHandle == -1 ); 544 if ( collisionModelHandle ) { 545 return collisionModelHandle; 546 } else if ( traceModelIndex != -1 ) { 547 return collisionModelManager->SetupTrmModel( *GetCachedTraceModel( traceModelIndex ), material ); 548 } else { 549 // this happens in multiplayer on the combat models 550 gameLocal.Warning( "idClipModel::Handle: clip model %d on '%s' (%x) is not a collision or trace model", id, entity->name.c_str(), entity->entityNumber ); 551 return 0; 552 } 553 } 554 555 /* 556 ================ 557 idClipModel::GetMassProperties 558 ================ 559 */ 560 void idClipModel::GetMassProperties( const float density, float &mass, idVec3 ¢erOfMass, idMat3 &inertiaTensor ) const { 561 if ( traceModelIndex == -1 ) { 562 gameLocal.Error( "idClipModel::GetMassProperties: clip model %d on '%s' is not a trace model\n", id, entity->name.c_str() ); 563 } 564 565 trmCache_t *entry = GetTraceModelEntry( traceModelIndex ); //traceModelCache[traceModelIndex]; 566 mass = entry->volume * density; 567 centerOfMass = entry->centerOfMass; 568 inertiaTensor = density * entry->inertiaTensor; 569 } 570 571 /* 572 =============== 573 idClipModel::Unlink 574 =============== 575 */ 576 void idClipModel::Unlink() { 577 clipLink_t *link; 578 579 for ( link = clipLinks; link; link = clipLinks ) { 580 clipLinks = link->nextLink; 581 if ( link->prevInSector ) { 582 link->prevInSector->nextInSector = link->nextInSector; 583 } else { 584 link->sector->clipLinks = link->nextInSector; 585 } 586 if ( link->nextInSector ) { 587 link->nextInSector->prevInSector = link->prevInSector; 588 } 589 clipLinkAllocator.Free( link ); 590 } 591 } 592 593 /* 594 =============== 595 idClipModel::Link_r 596 =============== 597 */ 598 void idClipModel::Link_r( struct clipSector_s *node ) { 599 clipLink_t *link; 600 601 while( node->axis != -1 ) { 602 if ( absBounds[0][node->axis] > node->dist ) { 603 node = node->children[0]; 604 } else if ( absBounds[1][node->axis] < node->dist ) { 605 node = node->children[1]; 606 } else { 607 Link_r( node->children[0] ); 608 node = node->children[1]; 609 } 610 } 611 612 link = clipLinkAllocator.Alloc(); 613 link->clipModel = this; 614 link->sector = node; 615 link->nextInSector = node->clipLinks; 616 link->prevInSector = NULL; 617 if ( node->clipLinks ) { 618 node->clipLinks->prevInSector = link; 619 } 620 node->clipLinks = link; 621 link->nextLink = clipLinks; 622 clipLinks = link; 623 } 624 625 /* 626 =============== 627 idClipModel::Link 628 =============== 629 */ 630 void idClipModel::Link( idClip &clp ) { 631 632 assert( idClipModel::entity ); 633 if ( !idClipModel::entity ) { 634 return; 635 } 636 637 if ( clipLinks ) { 638 Unlink(); // unlink from old position 639 } 640 641 if ( bounds.IsCleared() ) { 642 return; 643 } 644 645 // set the abs box 646 if ( axis.IsRotated() ) { 647 // expand for rotation 648 absBounds.FromTransformedBounds( bounds, origin, axis ); 649 } else { 650 // normal 651 absBounds[0] = bounds[0] + origin; 652 absBounds[1] = bounds[1] + origin; 653 } 654 655 // because movement is clipped an epsilon away from an actual edge, 656 // we must fully check even when bounding boxes don't quite touch 657 absBounds[0] -= vec3_boxEpsilon; 658 absBounds[1] += vec3_boxEpsilon; 659 660 Link_r( clp.clipSectors ); 661 } 662 663 /* 664 =============== 665 idClipModel::Link 666 =============== 667 */ 668 void idClipModel::Link( idClip &clp, idEntity *ent, int newId, const idVec3 &newOrigin, const idMat3 &newAxis, int renderModelHandle ) { 669 670 this->entity = ent; 671 this->id = newId; 672 this->origin = newOrigin; 673 this->axis = newAxis; 674 if ( renderModelHandle != -1 ) { 675 this->renderModelHandle = renderModelHandle; 676 const renderEntity_t *renderEntity = gameRenderWorld->GetRenderEntity( renderModelHandle ); 677 if ( renderEntity ) { 678 this->bounds = renderEntity->bounds; 679 } 680 } 681 this->Link( clp ); 682 } 683 684 /* 685 ============ 686 idClipModel::CheckModel 687 ============ 688 */ 689 cmHandle_t idClipModel::CheckModel( const char *name ) { 690 return collisionModelManager->LoadModel( name ); 691 } 692 693 694 /* 695 =============================================================== 696 697 idClip 698 699 =============================================================== 700 */ 701 702 /* 703 =============== 704 idClip::idClip 705 =============== 706 */ 707 idClip::idClip() { 708 numClipSectors = 0; 709 clipSectors = NULL; 710 worldBounds.Zero(); 711 numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0; 712 } 713 714 /* 715 =============== 716 idClip::CreateClipSectors_r 717 718 Builds a uniformly subdivided tree for the given world size 719 =============== 720 */ 721 clipSector_t *idClip::CreateClipSectors_r( const int depth, const idBounds &bounds, idVec3 &maxSector ) { 722 int i; 723 clipSector_t *anode; 724 idVec3 size; 725 idBounds front, back; 726 727 anode = &clipSectors[idClip::numClipSectors]; 728 idClip::numClipSectors++; 729 730 if ( depth == MAX_SECTOR_DEPTH ) { 731 anode->axis = -1; 732 anode->children[0] = anode->children[1] = NULL; 733 734 for ( i = 0; i < 3; i++ ) { 735 if ( bounds[1][i] - bounds[0][i] > maxSector[i] ) { 736 maxSector[i] = bounds[1][i] - bounds[0][i]; 737 } 738 } 739 return anode; 740 } 741 742 size = bounds[1] - bounds[0]; 743 if ( size[0] >= size[1] && size[0] >= size[2] ) { 744 anode->axis = 0; 745 } else if ( size[1] >= size[0] && size[1] >= size[2] ) { 746 anode->axis = 1; 747 } else { 748 anode->axis = 2; 749 } 750 751 anode->dist = 0.5f * ( bounds[1][anode->axis] + bounds[0][anode->axis] ); 752 753 front = bounds; 754 back = bounds; 755 756 front[0][anode->axis] = back[1][anode->axis] = anode->dist; 757 758 anode->children[0] = CreateClipSectors_r( depth+1, front, maxSector ); 759 anode->children[1] = CreateClipSectors_r( depth+1, back, maxSector ); 760 761 return anode; 762 } 763 764 /* 765 =============== 766 idClip::Init 767 =============== 768 */ 769 void idClip::Init() { 770 cmHandle_t h; 771 idVec3 size, maxSector = vec3_origin; 772 773 // clear clip sectors 774 clipSectors = new (TAG_PHYSICS_CLIP) clipSector_t[MAX_SECTORS]; 775 memset( clipSectors, 0, MAX_SECTORS * sizeof( clipSector_t ) ); 776 numClipSectors = 0; 777 touchCount = -1; 778 // get world map bounds 779 h = collisionModelManager->LoadModel( "worldMap" ); 780 collisionModelManager->GetModelBounds( h, worldBounds ); 781 // create world sectors 782 CreateClipSectors_r( 0, worldBounds, maxSector ); 783 784 size = worldBounds[1] - worldBounds[0]; 785 gameLocal.Printf( "map bounds are (%1.1f, %1.1f, %1.1f)\n", size[0], size[1], size[2] ); 786 gameLocal.Printf( "max clip sector is (%1.1f, %1.1f, %1.1f)\n", maxSector[0], maxSector[1], maxSector[2] ); 787 788 // initialize a default clip model 789 defaultClipModel.LoadModel( idTraceModel( idBounds( idVec3( 0, 0, 0 ) ).Expand( 8 ) ) ); 790 791 // set counters to zero 792 numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0; 793 } 794 795 /* 796 =============== 797 idClip::Shutdown 798 =============== 799 */ 800 void idClip::Shutdown() { 801 delete[] clipSectors; 802 clipSectors = NULL; 803 804 // free the trace model used for the temporaryClipModel 805 if ( temporaryClipModel.traceModelIndex != -1 ) { 806 idClipModel::FreeTraceModel( temporaryClipModel.traceModelIndex ); 807 temporaryClipModel.traceModelIndex = -1; 808 } 809 810 // free the trace model used for the defaultClipModel 811 if ( defaultClipModel.traceModelIndex != -1 ) { 812 idClipModel::FreeTraceModel( defaultClipModel.traceModelIndex ); 813 defaultClipModel.traceModelIndex = -1; 814 } 815 816 clipLinkAllocator.Shutdown(); 817 } 818 819 /* 820 ==================== 821 idClip::ClipModelsTouchingBounds_r 822 ==================== 823 */ 824 typedef struct listParms_s { 825 idBounds bounds; 826 int contentMask; 827 idClipModel ** list; 828 int count; 829 int maxCount; 830 } listParms_t; 831 832 void idClip::ClipModelsTouchingBounds_r( const struct clipSector_s *node, listParms_t &parms ) const { 833 834 while( node->axis != -1 ) { 835 if ( parms.bounds[0][node->axis] > node->dist ) { 836 node = node->children[0]; 837 } else if ( parms.bounds[1][node->axis] < node->dist ) { 838 node = node->children[1]; 839 } else { 840 ClipModelsTouchingBounds_r( node->children[0], parms ); 841 node = node->children[1]; 842 } 843 } 844 845 for ( clipLink_t *link = node->clipLinks; link; link = link->nextInSector ) { 846 idClipModel *check = link->clipModel; 847 848 // if the clip model is enabled 849 if ( !check->enabled ) { 850 continue; 851 } 852 853 // avoid duplicates in the list 854 if ( check->touchCount == touchCount ) { 855 continue; 856 } 857 858 // if the clip model does not have any contents we are looking for 859 if ( !( check->contents & parms.contentMask ) ) { 860 continue; 861 } 862 863 // if the bounds really do overlap 864 if ( check->absBounds[0][0] > parms.bounds[1][0] || 865 check->absBounds[1][0] < parms.bounds[0][0] || 866 check->absBounds[0][1] > parms.bounds[1][1] || 867 check->absBounds[1][1] < parms.bounds[0][1] || 868 check->absBounds[0][2] > parms.bounds[1][2] || 869 check->absBounds[1][2] < parms.bounds[0][2] ) { 870 continue; 871 } 872 873 if ( parms.count >= parms.maxCount ) { 874 gameLocal.Warning( "idClip::ClipModelsTouchingBounds_r: max count" ); 875 return; 876 } 877 878 check->touchCount = touchCount; 879 parms.list[parms.count] = check; 880 parms.count++; 881 } 882 } 883 884 /* 885 ================ 886 idClip::ClipModelsTouchingBounds 887 ================ 888 */ 889 int idClip::ClipModelsTouchingBounds( const idBounds &bounds, int contentMask, idClipModel **clipModelList, int maxCount ) const { 890 listParms_t parms; 891 892 if ( bounds[0][0] > bounds[1][0] || 893 bounds[0][1] > bounds[1][1] || 894 bounds[0][2] > bounds[1][2] ) { 895 // we should not go through the tree for degenerate or backwards bounds 896 assert( false ); 897 return 0; 898 } 899 900 parms.bounds[0] = bounds[0] - vec3_boxEpsilon; 901 parms.bounds[1] = bounds[1] + vec3_boxEpsilon; 902 parms.contentMask = contentMask; 903 parms.list = clipModelList; 904 parms.count = 0; 905 parms.maxCount = maxCount; 906 907 touchCount++; 908 ClipModelsTouchingBounds_r( clipSectors, parms ); 909 910 return parms.count; 911 } 912 913 /* 914 ================ 915 idClip::EntitiesTouchingBounds 916 ================ 917 */ 918 int idClip::EntitiesTouchingBounds( const idBounds &bounds, int contentMask, idEntity **entityList, int maxCount ) const { 919 idClipModel *clipModelList[MAX_GENTITIES]; 920 int i, j, count, entCount; 921 922 count = idClip::ClipModelsTouchingBounds( bounds, contentMask, clipModelList, MAX_GENTITIES ); 923 entCount = 0; 924 for ( i = 0; i < count; i++ ) { 925 // entity could already be in the list because an entity can use multiple clip models 926 for ( j = 0; j < entCount; j++ ) { 927 if ( entityList[j] == clipModelList[i]->entity ) { 928 break; 929 } 930 } 931 if ( j >= entCount ) { 932 if ( entCount >= maxCount ) { 933 gameLocal.Warning( "idClip::EntitiesTouchingBounds: max count" ); 934 return entCount; 935 } 936 entityList[entCount] = clipModelList[i]->entity; 937 entCount++; 938 } 939 } 940 941 return entCount; 942 } 943 944 /* 945 ==================== 946 idClip::GetTraceClipModels 947 948 an ent will be excluded from testing if: 949 cm->entity == passEntity ( don't clip against the pass entity ) 950 cm->entity == passOwner ( missiles don't clip with owner ) 951 cm->owner == passEntity ( don't interact with your own missiles ) 952 cm->owner == passOwner ( don't interact with other missiles from same owner ) 953 ==================== 954 */ 955 int idClip::GetTraceClipModels( const idBounds &bounds, int contentMask, const idEntity *passEntity, idClipModel **clipModelList ) const { 956 int i, num; 957 idClipModel *cm; 958 idEntity *passOwner; 959 960 num = ClipModelsTouchingBounds( bounds, contentMask, clipModelList, MAX_GENTITIES ); 961 962 if ( !passEntity ) { 963 return num; 964 } 965 966 if ( passEntity->GetPhysics()->GetNumClipModels() > 0 ) { 967 passOwner = passEntity->GetPhysics()->GetClipModel()->GetOwner(); 968 } else { 969 passOwner = NULL; 970 } 971 972 for ( i = 0; i < num; i++ ) { 973 974 cm = clipModelList[i]; 975 976 // check if we should ignore this entity 977 if ( cm->entity == passEntity ) { 978 clipModelList[i] = NULL; // don't clip against the pass entity 979 } else if ( cm->entity == passOwner ) { 980 clipModelList[i] = NULL; // missiles don't clip with their owner 981 } else if ( cm->owner ) { 982 if ( cm->owner == passEntity ) { 983 clipModelList[i] = NULL; // don't clip against own missiles 984 } else if ( cm->owner == passOwner ) { 985 clipModelList[i] = NULL; // don't clip against other missiles from same owner 986 } 987 } 988 } 989 990 return num; 991 } 992 993 /* 994 ============ 995 idClip::TraceRenderModel 996 ============ 997 */ 998 void idClip::TraceRenderModel( trace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, const idMat3 &axis, idClipModel *touch ) const { 999 trace.fraction = 1.0f; 1000 1001 // if the trace is passing through the bounds 1002 if ( touch->absBounds.Expand( radius ).LineIntersection( start, end ) ) { 1003 modelTrace_t modelTrace; 1004 1005 // test with exact render model and modify trace_t structure accordingly 1006 if ( gameRenderWorld->ModelTrace( modelTrace, touch->renderModelHandle, start, end, radius ) ) { 1007 trace.fraction = modelTrace.fraction; 1008 trace.endAxis = axis; 1009 trace.endpos = modelTrace.point; 1010 trace.c.normal = modelTrace.normal; 1011 trace.c.dist = modelTrace.point * modelTrace.normal; 1012 trace.c.point = modelTrace.point; 1013 trace.c.type = CONTACT_TRMVERTEX; 1014 trace.c.modelFeature = 0; 1015 trace.c.trmFeature = 0; 1016 trace.c.contents = modelTrace.material->GetContentFlags(); 1017 trace.c.material = modelTrace.material; 1018 // NOTE: trace.c.id will be the joint number 1019 touch->id = JOINT_HANDLE_TO_CLIPMODEL_ID( modelTrace.jointNumber ); 1020 } 1021 } 1022 } 1023 1024 /* 1025 ============ 1026 idClip::TraceModelForClipModel 1027 ============ 1028 */ 1029 const idTraceModel *idClip::TraceModelForClipModel( const idClipModel *mdl ) const { 1030 if ( !mdl ) { 1031 return NULL; 1032 } else { 1033 if ( !mdl->IsTraceModel() ) { 1034 if ( mdl->GetEntity() ) { 1035 gameLocal.Error( "TraceModelForClipModel: clip model %d on '%s' is not a trace model\n", mdl->GetId(), mdl->GetEntity()->name.c_str() ); 1036 } else { 1037 gameLocal.Error( "TraceModelForClipModel: clip model %d is not a trace model\n", mdl->GetId() ); 1038 } 1039 } 1040 return idClipModel::GetCachedTraceModel( mdl->traceModelIndex ); 1041 } 1042 } 1043 1044 /* 1045 ============ 1046 idClip::TestHugeTranslation 1047 ============ 1048 */ 1049 ID_INLINE bool TestHugeTranslation( trace_t &results, const idClipModel *mdl, const idVec3 &start, const idVec3 &end, const idMat3 &trmAxis ) { 1050 if ( mdl != NULL && ( end - start ).LengthSqr() > Square( CM_MAX_TRACE_DIST ) ) { 1051 #ifndef CTF 1052 // May be important: This occurs in CTF when a player connects and spawns 1053 // in the PVS of a player that has a flag that is spawning the idMoveableItem 1054 // "nuggets". The error seems benign and the assert was getting in the way 1055 // of testing. 1056 assert( 0 ); 1057 #endif 1058 1059 results.fraction = 0.0f; 1060 results.endpos = start; 1061 results.endAxis = trmAxis; 1062 memset( &results.c, 0, sizeof( results.c ) ); 1063 results.c.point = start; 1064 1065 if ( mdl->GetEntity() ) { 1066 gameLocal.Printf( "huge translation for clip model %d on entity %d '%s'\n", mdl->GetId(), mdl->GetEntity()->entityNumber, mdl->GetEntity()->GetName() ); 1067 } else { 1068 gameLocal.Printf( "huge translation for clip model %d\n", mdl->GetId() ); 1069 } 1070 return true; 1071 } 1072 return false; 1073 } 1074 1075 /* 1076 ============ 1077 idClip::TranslationEntities 1078 ============ 1079 */ 1080 void idClip::TranslationEntities( trace_t &results, const idVec3 &start, const idVec3 &end, 1081 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1082 int i, num; 1083 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1084 idBounds traceBounds; 1085 float radius; 1086 trace_t trace; 1087 const idTraceModel *trm; 1088 1089 if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) { 1090 return; 1091 } 1092 1093 trm = TraceModelForClipModel( mdl ); 1094 1095 results.fraction = 1.0f; 1096 results.endpos = end; 1097 results.endAxis = trmAxis; 1098 1099 if ( !trm ) { 1100 traceBounds.FromPointTranslation( start, end - start ); 1101 radius = 0.0f; 1102 } else { 1103 traceBounds.FromBoundsTranslation( trm->bounds, start, trmAxis, end - start ); 1104 radius = trm->bounds.GetRadius(); 1105 } 1106 1107 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1108 1109 for ( i = 0; i < num; i++ ) { 1110 touch = clipModelList[i]; 1111 1112 if ( !touch ) { 1113 continue; 1114 } 1115 1116 if ( touch->renderModelHandle != -1 ) { 1117 idClip::numRenderModelTraces++; 1118 TraceRenderModel( trace, start, end, radius, trmAxis, touch ); 1119 } else { 1120 idClip::numTranslations++; 1121 collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask, 1122 touch->Handle(), touch->origin, touch->axis ); 1123 } 1124 1125 if ( trace.fraction < results.fraction ) { 1126 results = trace; 1127 results.c.entityNum = touch->entity->entityNumber; 1128 results.c.id = touch->id; 1129 if ( results.fraction == 0.0f ) { 1130 break; 1131 } 1132 } 1133 } 1134 } 1135 1136 /* 1137 ============ 1138 idClip::Translation 1139 ============ 1140 */ 1141 bool idClip::Translation( trace_t &results, const idVec3 &start, const idVec3 &end, 1142 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1143 int i, num; 1144 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1145 idBounds traceBounds; 1146 float radius; 1147 trace_t trace; 1148 const idTraceModel *trm; 1149 1150 if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) { 1151 return true; 1152 } 1153 1154 trm = TraceModelForClipModel( mdl ); 1155 1156 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1157 // test world 1158 idClip::numTranslations++; 1159 collisionModelManager->Translation( &results, start, end, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1160 results.c.entityNum = results.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; 1161 if ( results.fraction == 0.0f ) { 1162 return true; // blocked immediately by the world 1163 } 1164 } else { 1165 memset( &results, 0, sizeof( results ) ); 1166 results.fraction = 1.0f; 1167 results.endpos = end; 1168 results.endAxis = trmAxis; 1169 } 1170 1171 if ( !trm ) { 1172 traceBounds.FromPointTranslation( start, results.endpos - start ); 1173 radius = 0.0f; 1174 } else { 1175 traceBounds.FromBoundsTranslation( trm->bounds, start, trmAxis, results.endpos - start ); 1176 radius = trm->bounds.GetRadius(); 1177 } 1178 1179 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1180 1181 for ( i = 0; i < num; i++ ) { 1182 touch = clipModelList[i]; 1183 1184 if ( !touch ) { 1185 continue; 1186 } 1187 1188 if ( touch->renderModelHandle != -1 ) { 1189 idClip::numRenderModelTraces++; 1190 TraceRenderModel( trace, start, end, radius, trmAxis, touch ); 1191 } else { 1192 idClip::numTranslations++; 1193 collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask, 1194 touch->Handle(), touch->origin, touch->axis ); 1195 } 1196 1197 if ( trace.fraction < results.fraction ) { 1198 results = trace; 1199 results.c.entityNum = touch->entity->entityNumber; 1200 results.c.id = touch->id; 1201 if ( results.fraction == 0.0f ) { 1202 break; 1203 } 1204 } 1205 } 1206 1207 return ( results.fraction < 1.0f ); 1208 } 1209 1210 /* 1211 ============ 1212 idClip::Rotation 1213 ============ 1214 */ 1215 bool idClip::Rotation( trace_t &results, const idVec3 &start, const idRotation &rotation, 1216 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1217 int i, num; 1218 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1219 idBounds traceBounds; 1220 trace_t trace; 1221 const idTraceModel *trm; 1222 1223 trm = TraceModelForClipModel( mdl ); 1224 1225 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1226 // test world 1227 idClip::numRotations++; 1228 collisionModelManager->Rotation( &results, start, rotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1229 results.c.entityNum = results.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; 1230 if ( results.fraction == 0.0f ) { 1231 return true; // blocked immediately by the world 1232 } 1233 } else { 1234 memset( &results, 0, sizeof( results ) ); 1235 results.fraction = 1.0f; 1236 results.endpos = start; 1237 results.endAxis = trmAxis * rotation.ToMat3(); 1238 } 1239 1240 if ( !trm ) { 1241 traceBounds.FromPointRotation( start, rotation ); 1242 } else { 1243 traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation ); 1244 } 1245 1246 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1247 1248 for ( i = 0; i < num; i++ ) { 1249 touch = clipModelList[i]; 1250 1251 if ( !touch ) { 1252 continue; 1253 } 1254 1255 // no rotational collision with render models 1256 if ( touch->renderModelHandle != -1 ) { 1257 continue; 1258 } 1259 1260 idClip::numRotations++; 1261 collisionModelManager->Rotation( &trace, start, rotation, trm, trmAxis, contentMask, 1262 touch->Handle(), touch->origin, touch->axis ); 1263 1264 if ( trace.fraction < results.fraction ) { 1265 results = trace; 1266 results.c.entityNum = touch->entity->entityNumber; 1267 results.c.id = touch->id; 1268 if ( results.fraction == 0.0f ) { 1269 break; 1270 } 1271 } 1272 } 1273 1274 return ( results.fraction < 1.0f ); 1275 } 1276 1277 /* 1278 ============ 1279 idClip::Motion 1280 ============ 1281 */ 1282 bool idClip::Motion( trace_t &results, const idVec3 &start, const idVec3 &end, const idRotation &rotation, 1283 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1284 int i, num; 1285 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1286 idVec3 dir, endPosition; 1287 idBounds traceBounds; 1288 float radius; 1289 trace_t translationalTrace, rotationalTrace, trace; 1290 idRotation endRotation; 1291 const idTraceModel *trm; 1292 1293 assert( rotation.GetOrigin() == start ); 1294 1295 if ( TestHugeTranslation( results, mdl, start, end, trmAxis ) ) { 1296 return true; 1297 } 1298 1299 if ( mdl != NULL && rotation.GetAngle() != 0.0f && rotation.GetVec() != vec3_origin ) { 1300 // if no translation 1301 if ( start == end ) { 1302 // pure rotation 1303 return Rotation( results, start, rotation, mdl, trmAxis, contentMask, passEntity ); 1304 } 1305 } else if ( start != end ) { 1306 // pure translation 1307 return Translation( results, start, end, mdl, trmAxis, contentMask, passEntity ); 1308 } else { 1309 // no motion 1310 results.fraction = 1.0f; 1311 results.endpos = start; 1312 results.endAxis = trmAxis; 1313 return false; 1314 } 1315 1316 trm = TraceModelForClipModel( mdl ); 1317 1318 radius = trm->bounds.GetRadius(); 1319 1320 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1321 // translational collision with world 1322 idClip::numTranslations++; 1323 collisionModelManager->Translation( &translationalTrace, start, end, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1324 translationalTrace.c.entityNum = translationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; 1325 } else { 1326 memset( &translationalTrace, 0, sizeof( translationalTrace ) ); 1327 translationalTrace.fraction = 1.0f; 1328 translationalTrace.endpos = end; 1329 translationalTrace.endAxis = trmAxis; 1330 } 1331 1332 if ( translationalTrace.fraction != 0.0f ) { 1333 1334 traceBounds.FromBoundsRotation( trm->bounds, start, trmAxis, rotation ); 1335 dir = translationalTrace.endpos - start; 1336 for ( i = 0; i < 3; i++ ) { 1337 if ( dir[i] < 0.0f ) { 1338 traceBounds[0][i] += dir[i]; 1339 } 1340 else { 1341 traceBounds[1][i] += dir[i]; 1342 } 1343 } 1344 1345 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1346 1347 for ( i = 0; i < num; i++ ) { 1348 touch = clipModelList[i]; 1349 1350 if ( !touch ) { 1351 continue; 1352 } 1353 1354 if ( touch->renderModelHandle != -1 ) { 1355 idClip::numRenderModelTraces++; 1356 TraceRenderModel( trace, start, end, radius, trmAxis, touch ); 1357 } else { 1358 idClip::numTranslations++; 1359 collisionModelManager->Translation( &trace, start, end, trm, trmAxis, contentMask, 1360 touch->Handle(), touch->origin, touch->axis ); 1361 } 1362 1363 if ( trace.fraction < translationalTrace.fraction ) { 1364 translationalTrace = trace; 1365 translationalTrace.c.entityNum = touch->entity->entityNumber; 1366 translationalTrace.c.id = touch->id; 1367 if ( translationalTrace.fraction == 0.0f ) { 1368 break; 1369 } 1370 } 1371 } 1372 } else { 1373 num = -1; 1374 } 1375 1376 endPosition = translationalTrace.endpos; 1377 endRotation = rotation; 1378 endRotation.SetOrigin( endPosition ); 1379 1380 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1381 // rotational collision with world 1382 idClip::numRotations++; 1383 collisionModelManager->Rotation( &rotationalTrace, endPosition, endRotation, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1384 rotationalTrace.c.entityNum = rotationalTrace.fraction != 1.0f ? ENTITYNUM_WORLD : ENTITYNUM_NONE; 1385 } else { 1386 memset( &rotationalTrace, 0, sizeof( rotationalTrace ) ); 1387 rotationalTrace.fraction = 1.0f; 1388 rotationalTrace.endpos = endPosition; 1389 rotationalTrace.endAxis = trmAxis * rotation.ToMat3(); 1390 } 1391 1392 if ( rotationalTrace.fraction != 0.0f ) { 1393 1394 if ( num == -1 ) { 1395 traceBounds.FromBoundsRotation( trm->bounds, endPosition, trmAxis, endRotation ); 1396 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1397 } 1398 1399 for ( i = 0; i < num; i++ ) { 1400 touch = clipModelList[i]; 1401 1402 if ( !touch ) { 1403 continue; 1404 } 1405 1406 // no rotational collision detection with render models 1407 if ( touch->renderModelHandle != -1 ) { 1408 continue; 1409 } 1410 1411 idClip::numRotations++; 1412 collisionModelManager->Rotation( &trace, endPosition, endRotation, trm, trmAxis, contentMask, 1413 touch->Handle(), touch->origin, touch->axis ); 1414 1415 if ( trace.fraction < rotationalTrace.fraction ) { 1416 rotationalTrace = trace; 1417 rotationalTrace.c.entityNum = touch->entity->entityNumber; 1418 rotationalTrace.c.id = touch->id; 1419 if ( rotationalTrace.fraction == 0.0f ) { 1420 break; 1421 } 1422 } 1423 } 1424 } 1425 1426 if ( rotationalTrace.fraction < 1.0f ) { 1427 results = rotationalTrace; 1428 } else { 1429 results = translationalTrace; 1430 results.endAxis = rotationalTrace.endAxis; 1431 } 1432 1433 results.fraction = Max( translationalTrace.fraction, rotationalTrace.fraction ); 1434 1435 return ( translationalTrace.fraction < 1.0f || rotationalTrace.fraction < 1.0f ); 1436 } 1437 1438 /* 1439 ============ 1440 idClip::Contacts 1441 ============ 1442 */ 1443 int idClip::Contacts( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, 1444 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1445 int i, j, num, n, numContacts; 1446 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1447 idBounds traceBounds; 1448 const idTraceModel *trm; 1449 1450 trm = TraceModelForClipModel( mdl ); 1451 1452 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1453 // test world 1454 idClip::numContacts++; 1455 numContacts = collisionModelManager->Contacts( contacts, maxContacts, start, dir, depth, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1456 } else { 1457 numContacts = 0; 1458 } 1459 1460 for ( i = 0; i < numContacts; i++ ) { 1461 contacts[i].entityNum = ENTITYNUM_WORLD; 1462 contacts[i].id = 0; 1463 } 1464 1465 if ( numContacts >= maxContacts ) { 1466 return numContacts; 1467 } 1468 1469 if ( !trm ) { 1470 traceBounds = idBounds( start ).Expand( depth ); 1471 } else { 1472 traceBounds.FromTransformedBounds( trm->bounds, start, trmAxis ); 1473 traceBounds.ExpandSelf( depth ); 1474 } 1475 1476 num = GetTraceClipModels( traceBounds, contentMask, passEntity, clipModelList ); 1477 1478 for ( i = 0; i < num; i++ ) { 1479 touch = clipModelList[i]; 1480 1481 if ( !touch ) { 1482 continue; 1483 } 1484 1485 // no contacts with render models 1486 if ( touch->renderModelHandle != -1 ) { 1487 continue; 1488 } 1489 1490 idClip::numContacts++; 1491 n = collisionModelManager->Contacts( contacts + numContacts, maxContacts - numContacts, 1492 start, dir, depth, trm, trmAxis, contentMask, 1493 touch->Handle(), touch->origin, touch->axis ); 1494 1495 for ( j = 0; j < n; j++ ) { 1496 contacts[numContacts].entityNum = touch->entity->entityNumber; 1497 contacts[numContacts].id = touch->id; 1498 numContacts++; 1499 } 1500 1501 if ( numContacts >= maxContacts ) { 1502 break; 1503 } 1504 } 1505 1506 return numContacts; 1507 } 1508 1509 /* 1510 ============ 1511 idClip::Contents 1512 ============ 1513 */ 1514 int idClip::Contents( const idVec3 &start, const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, const idEntity *passEntity ) { 1515 int i, num, contents; 1516 idClipModel *touch, *clipModelList[MAX_GENTITIES]; 1517 idBounds traceBounds; 1518 const idTraceModel *trm; 1519 1520 trm = TraceModelForClipModel( mdl ); 1521 1522 if ( !passEntity || passEntity->entityNumber != ENTITYNUM_WORLD ) { 1523 // test world 1524 idClip::numContents++; 1525 contents = collisionModelManager->Contents( start, trm, trmAxis, contentMask, 0, vec3_origin, mat3_default ); 1526 } else { 1527 contents = 0; 1528 } 1529 1530 if ( !trm ) { 1531 traceBounds[0] = start; 1532 traceBounds[1] = start; 1533 } else if ( trmAxis.IsRotated() ) { 1534 traceBounds.FromTransformedBounds( trm->bounds, start, trmAxis ); 1535 } else { 1536 traceBounds[0] = trm->bounds[0] + start; 1537 traceBounds[1] = trm->bounds[1] + start; 1538 } 1539 1540 num = GetTraceClipModels( traceBounds, -1, passEntity, clipModelList ); 1541 1542 for ( i = 0; i < num; i++ ) { 1543 touch = clipModelList[i]; 1544 1545 if ( !touch ) { 1546 continue; 1547 } 1548 1549 // no contents test with render models 1550 if ( touch->renderModelHandle != -1 ) { 1551 continue; 1552 } 1553 1554 // if the entity does not have any contents we are looking for 1555 if ( ( touch->contents & contentMask ) == 0 ) { 1556 continue; 1557 } 1558 1559 // if the entity has no new contents flags 1560 if ( ( touch->contents & contents ) == touch->contents ) { 1561 continue; 1562 } 1563 1564 idClip::numContents++; 1565 if ( collisionModelManager->Contents( start, trm, trmAxis, contentMask, touch->Handle(), touch->origin, touch->axis ) ) { 1566 contents |= ( touch->contents & contentMask ); 1567 } 1568 } 1569 1570 return contents; 1571 } 1572 1573 /* 1574 ============ 1575 idClip::TranslationModel 1576 ============ 1577 */ 1578 void idClip::TranslationModel( trace_t &results, const idVec3 &start, const idVec3 &end, 1579 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, 1580 cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { 1581 const idTraceModel *trm = TraceModelForClipModel( mdl ); 1582 idClip::numTranslations++; 1583 collisionModelManager->Translation( &results, start, end, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); 1584 } 1585 1586 /* 1587 ============ 1588 idClip::RotationModel 1589 ============ 1590 */ 1591 void idClip::RotationModel( trace_t &results, const idVec3 &start, const idRotation &rotation, 1592 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, 1593 cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { 1594 const idTraceModel *trm = TraceModelForClipModel( mdl ); 1595 idClip::numRotations++; 1596 collisionModelManager->Rotation( &results, start, rotation, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); 1597 } 1598 1599 /* 1600 ============ 1601 idClip::ContactsModel 1602 ============ 1603 */ 1604 int idClip::ContactsModel( contactInfo_t *contacts, const int maxContacts, const idVec3 &start, const idVec6 &dir, const float depth, 1605 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, 1606 cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { 1607 const idTraceModel *trm = TraceModelForClipModel( mdl ); 1608 idClip::numContacts++; 1609 return collisionModelManager->Contacts( contacts, maxContacts, start, dir, depth, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); 1610 } 1611 1612 /* 1613 ============ 1614 idClip::ContentsModel 1615 ============ 1616 */ 1617 int idClip::ContentsModel( const idVec3 &start, 1618 const idClipModel *mdl, const idMat3 &trmAxis, int contentMask, 1619 cmHandle_t model, const idVec3 &modelOrigin, const idMat3 &modelAxis ) { 1620 const idTraceModel *trm = TraceModelForClipModel( mdl ); 1621 idClip::numContents++; 1622 return collisionModelManager->Contents( start, trm, trmAxis, contentMask, model, modelOrigin, modelAxis ); 1623 } 1624 1625 /* 1626 ============ 1627 idClip::GetModelContactFeature 1628 ============ 1629 */ 1630 bool idClip::GetModelContactFeature( const contactInfo_t &contact, const idClipModel *clipModel, idFixedWinding &winding ) const { 1631 int i; 1632 cmHandle_t handle; 1633 idVec3 start, end; 1634 1635 handle = -1; 1636 winding.Clear(); 1637 1638 if ( clipModel == NULL ) { 1639 handle = 0; 1640 } else { 1641 if ( clipModel->renderModelHandle != -1 ) { 1642 winding += contact.point; 1643 return true; 1644 } else if ( clipModel->traceModelIndex != -1 ) { 1645 handle = collisionModelManager->SetupTrmModel( *idClipModel::GetCachedTraceModel( clipModel->traceModelIndex ), clipModel->material ); 1646 } else { 1647 handle = clipModel->collisionModelHandle; 1648 } 1649 } 1650 1651 // if contact with a collision model 1652 if ( handle != -1 ) { 1653 switch( contact.type ) { 1654 case CONTACT_EDGE: { 1655 // the model contact feature is a collision model edge 1656 collisionModelManager->GetModelEdge( handle, contact.modelFeature, start, end ); 1657 winding += start; 1658 winding += end; 1659 break; 1660 } 1661 case CONTACT_MODELVERTEX: { 1662 // the model contact feature is a collision model vertex 1663 collisionModelManager->GetModelVertex( handle, contact.modelFeature, start ); 1664 winding += start; 1665 break; 1666 } 1667 case CONTACT_TRMVERTEX: { 1668 // the model contact feature is a collision model polygon 1669 collisionModelManager->GetModelPolygon( handle, contact.modelFeature, winding ); 1670 break; 1671 } 1672 } 1673 } 1674 1675 // transform the winding to world space 1676 if ( clipModel ) { 1677 for ( i = 0; i < winding.GetNumPoints(); i++ ) { 1678 winding[i].ToVec3() *= clipModel->axis; 1679 winding[i].ToVec3() += clipModel->origin; 1680 } 1681 } 1682 1683 return true; 1684 } 1685 1686 /* 1687 ============ 1688 idClip::PrintStatistics 1689 ============ 1690 */ 1691 void idClip::PrintStatistics() { 1692 gameLocal.Printf( "t = %-3d, r = %-3d, m = %-3d, render = %-3d, contents = %-3d, contacts = %-3d\n", 1693 numTranslations, numRotations, numMotions, numRenderModelTraces, numContents, numContacts ); 1694 numRotations = numTranslations = numMotions = numRenderModelTraces = numContents = numContacts = 0; 1695 } 1696 1697 /* 1698 ============ 1699 idClip::DrawClipModels 1700 ============ 1701 */ 1702 void idClip::DrawClipModels( const idVec3 &eye, const float radius, const idEntity *passEntity ) { 1703 int i, num; 1704 idBounds bounds; 1705 idClipModel *clipModelList[MAX_GENTITIES]; 1706 idClipModel *clipModel; 1707 1708 bounds = idBounds( eye ).Expand( radius ); 1709 1710 num = idClip::ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES ); 1711 1712 for ( i = 0; i < num; i++ ) { 1713 clipModel = clipModelList[i]; 1714 if ( clipModel->GetEntity() == passEntity ) { 1715 continue; 1716 } 1717 if ( clipModel->renderModelHandle != -1 ) { 1718 gameRenderWorld->DebugBounds( colorCyan, clipModel->GetAbsBounds() ); 1719 } else { 1720 collisionModelManager->DrawModel( clipModel->Handle(), clipModel->GetOrigin(), clipModel->GetAxis(), eye, radius ); 1721 } 1722 } 1723 } 1724 1725 /* 1726 ============ 1727 idClip::DrawModelContactFeature 1728 ============ 1729 */ 1730 bool idClip::DrawModelContactFeature( const contactInfo_t &contact, const idClipModel *clipModel, int lifetime ) const { 1731 int i; 1732 idMat3 axis; 1733 idFixedWinding winding; 1734 1735 if ( !GetModelContactFeature( contact, clipModel, winding ) ) { 1736 return false; 1737 } 1738 1739 axis = contact.normal.ToMat3(); 1740 1741 if ( winding.GetNumPoints() == 1 ) { 1742 gameRenderWorld->DebugLine( colorCyan, winding[0].ToVec3(), winding[0].ToVec3() + 2.0f * axis[0], lifetime ); 1743 gameRenderWorld->DebugLine( colorWhite, winding[0].ToVec3() - 1.0f * axis[1], winding[0].ToVec3() + 1.0f * axis[1], lifetime ); 1744 gameRenderWorld->DebugLine( colorWhite, winding[0].ToVec3() - 1.0f * axis[2], winding[0].ToVec3() + 1.0f * axis[2], lifetime ); 1745 } else { 1746 for ( i = 0; i < winding.GetNumPoints(); i++ ) { 1747 gameRenderWorld->DebugLine( colorCyan, winding[i].ToVec3(), winding[(i+1)%winding.GetNumPoints()].ToVec3(), lifetime ); 1748 } 1749 } 1750 1751 axis[0] = -axis[0]; 1752 axis[2] = -axis[2]; 1753 gameRenderWorld->DrawText( contact.material->GetName(), winding.GetCenter() - 4.0f * axis[2], 0.1f, colorWhite, axis, 1, 5000 ); 1754 1755 return true; 1756 }