DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Clip.cpp (47815B)


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