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 &centerOfMass, 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