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