DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

RenderWorld.cpp (58861B)


      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 #include "tr_local.h"
     33 
     34 /*
     35 ===================
     36 R_ListRenderLightDefs_f
     37 ===================
     38 */
     39 void R_ListRenderLightDefs_f( const idCmdArgs &args ) {
     40 	int			i;
     41 	idRenderLightLocal	*ldef;
     42 
     43 	if ( !tr.primaryWorld ) {
     44 		return;
     45 	}
     46 	int active = 0;
     47 	int	totalRef = 0;
     48 	int	totalIntr = 0;
     49 
     50 	for ( i = 0; i < tr.primaryWorld->lightDefs.Num(); i++ ) {
     51 		ldef = tr.primaryWorld->lightDefs[i];
     52 		if ( !ldef ) {
     53 			common->Printf( "%4i: FREED\n", i );
     54 			continue;
     55 		}
     56 
     57 		// count up the interactions
     58 		int	iCount = 0;
     59 		for ( idInteraction *inter = ldef->firstInteraction; inter != NULL; inter = inter->lightNext ) {
     60 			iCount++;
     61 		}
     62 		totalIntr += iCount;
     63 
     64 		// count up the references
     65 		int	rCount = 0;
     66 		for ( areaReference_t *ref = ldef->references; ref; ref = ref->ownerNext ) {
     67 			rCount++;
     68 		}
     69 		totalRef += rCount;
     70 
     71 		common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, ldef->lightShader->GetName());
     72 		active++;
     73 	}
     74 
     75 	common->Printf( "%i lightDefs, %i interactions, %i areaRefs\n", active, totalIntr, totalRef );
     76 }
     77 
     78 /*
     79 ===================
     80 R_ListRenderEntityDefs_f
     81 ===================
     82 */
     83 void R_ListRenderEntityDefs_f( const idCmdArgs &args ) {
     84 	int			i;
     85 	idRenderEntityLocal	*mdef;
     86 
     87 	if ( !tr.primaryWorld ) {
     88 		return;
     89 	}
     90 	int active = 0;
     91 	int	totalRef = 0;
     92 	int	totalIntr = 0;
     93 
     94 	for ( i = 0; i < tr.primaryWorld->entityDefs.Num(); i++ ) {
     95 		mdef = tr.primaryWorld->entityDefs[i];
     96 		if ( !mdef ) {
     97 			common->Printf( "%4i: FREED\n", i );
     98 			continue;
     99 		}
    100 
    101 		// count up the interactions
    102 		int	iCount = 0;
    103 		for ( idInteraction *inter = mdef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
    104 			iCount++;
    105 		}
    106 		totalIntr += iCount;
    107 
    108 		// count up the references
    109 		int	rCount = 0;
    110 		for ( areaReference_t *ref = mdef->entityRefs; ref; ref = ref->ownerNext ) {
    111 			rCount++;
    112 		}
    113 		totalRef += rCount;
    114 
    115 		common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, mdef->parms.hModel->Name());
    116 		active++;
    117 	}
    118 
    119 	common->Printf( "total active: %i\n", active );
    120 }
    121 
    122 /*
    123 ===================
    124 idRenderWorldLocal::idRenderWorldLocal
    125 ===================
    126 */
    127 idRenderWorldLocal::idRenderWorldLocal() {
    128 	mapName.Clear();
    129 	mapTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
    130 
    131 	generateAllInteractionsCalled = false;
    132 
    133 	areaNodes = NULL;
    134 	numAreaNodes = 0;
    135 
    136 	portalAreas = NULL;
    137 	numPortalAreas = 0;
    138 
    139 	doublePortals = NULL;
    140 	numInterAreaPortals = 0;
    141 
    142 	interactionTable = 0;
    143 	interactionTableWidth = 0;
    144 	interactionTableHeight = 0;
    145 
    146 	for ( int i = 0; i < decals.Num(); i++ ) {
    147 		decals[i].entityHandle = -1;
    148 		decals[i].lastStartTime = 0;
    149 		decals[i].decals = new (TAG_MODEL) idRenderModelDecal();
    150 	}
    151 
    152 	for ( int i = 0; i < overlays.Num(); i++ ) {
    153 		overlays[i].entityHandle = -1;
    154 		overlays[i].lastStartTime = 0;
    155 		overlays[i].overlays = new (TAG_MODEL) idRenderModelOverlay();
    156 	}
    157 }
    158 
    159 /*
    160 ===================
    161 idRenderWorldLocal::~idRenderWorldLocal
    162 ===================
    163 */
    164 idRenderWorldLocal::~idRenderWorldLocal() {
    165 	// free all the entityDefs, lightDefs, portals, etc
    166 	FreeWorld();
    167 
    168 	for ( int i = 0; i < decals.Num(); i++ ) {
    169 		delete decals[i].decals;
    170 	}
    171 
    172 	for ( int i = 0; i < overlays.Num(); i++ ) {
    173 		delete overlays[i].overlays;
    174 	}
    175 
    176 	// free up the debug lines, polys, and text
    177 	RB_ClearDebugPolygons( 0 );
    178 	RB_ClearDebugLines( 0 );
    179 	RB_ClearDebugText( 0 );
    180 }
    181 
    182 /*
    183 ===================
    184 ResizeInteractionTable
    185 ===================
    186 */
    187 void idRenderWorldLocal::ResizeInteractionTable() {
    188 	// we overflowed the interaction table, so make it larger
    189 	common->Printf( "idRenderWorldLocal::ResizeInteractionTable: overflowed interactionTable, resizing\n" );
    190 
    191 	const int oldInteractionTableWidth = interactionTableWidth;
    192 	const int oldIinteractionTableHeight = interactionTableHeight;
    193 	idInteraction ** oldInteractionTable = interactionTable;
    194 
    195 	// build the interaction table
    196 	// this will be dynamically resized if the entity / light counts grow too much
    197 	interactionTableWidth = entityDefs.Num() + 100;
    198 	interactionTableHeight = lightDefs.Num() + 100;
    199 	const int	size =  interactionTableWidth * interactionTableHeight * sizeof( *interactionTable );
    200 	interactionTable = (idInteraction **)R_ClearedStaticAlloc( size );
    201 	for ( int l = 0; l < oldIinteractionTableHeight; l++ ) {
    202 		for ( int e = 0; e < oldInteractionTableWidth; e++ ) {
    203 			interactionTable[ l * interactionTableWidth + e ] = oldInteractionTable[ l * oldInteractionTableWidth + e ];
    204 		}
    205 	}
    206 
    207 	R_StaticFree( oldInteractionTable );
    208 }
    209 
    210 /*
    211 ===================
    212 AddEntityDef
    213 ===================
    214 */
    215 qhandle_t idRenderWorldLocal::AddEntityDef( const renderEntity_t *re ){
    216 	// try and reuse a free spot
    217 	int entityHandle = entityDefs.FindNull();
    218 	if ( entityHandle == -1 ) {
    219 		entityHandle = entityDefs.Append( NULL );
    220 
    221 		if ( interactionTable && entityDefs.Num() > interactionTableWidth ) {
    222 			ResizeInteractionTable();
    223 		}
    224 	}
    225 
    226 	UpdateEntityDef( entityHandle, re );
    227 	
    228 	return entityHandle;
    229 }
    230 
    231 /*
    232 ==============
    233 UpdateEntityDef
    234 
    235 Does not write to the demo file, which will only be updated for
    236 visible entities
    237 ==============
    238 */
    239 int c_callbackUpdate;
    240 
    241 void idRenderWorldLocal::UpdateEntityDef( qhandle_t entityHandle, const renderEntity_t *re ) {
    242 	if ( r_skipUpdates.GetBool() ) {
    243 		return;
    244 	}
    245 
    246 	tr.pc.c_entityUpdates++;
    247 
    248 	if ( !re->hModel && !re->callback ) {
    249 		common->Error( "idRenderWorld::UpdateEntityDef: NULL hModel" );
    250 	}
    251 
    252 	// create new slots if needed
    253 	if ( entityHandle < 0 || entityHandle > LUDICROUS_INDEX ) {
    254 		common->Error( "idRenderWorld::UpdateEntityDef: index = %i", entityHandle );
    255 	}
    256 	while ( entityHandle >= entityDefs.Num() ) {
    257 		entityDefs.Append( NULL );
    258 	}
    259 
    260 	idRenderEntityLocal	*def = entityDefs[entityHandle];
    261 	if ( def != NULL ) {
    262 
    263 		if ( !re->forceUpdate ) {
    264 
    265 			// check for exact match (OPTIMIZE: check through pointers more)
    266 			if ( !re->joints && !re->callbackData && !def->dynamicModel && !memcmp( re, &def->parms, sizeof( *re ) ) ) {
    267 				return;
    268 			}
    269 
    270 			// if the only thing that changed was shaderparms, we can just leave things as they are
    271 			// after updating parms
    272 
    273 			// if we have a callback function and the bounds, origin, axis and model match,
    274 			// then we can leave the references as they are
    275 			if ( re->callback ) {
    276 
    277 				bool axisMatch = ( re->axis == def->parms.axis );
    278 				bool originMatch = ( re->origin == def->parms.origin );
    279 				bool boundsMatch = ( re->bounds == def->localReferenceBounds );
    280 				bool modelMatch = ( re->hModel == def->parms.hModel );
    281 
    282 				if ( boundsMatch && originMatch && axisMatch && modelMatch ) {
    283 					// only clear the dynamic model and interaction surfaces if they exist
    284 					c_callbackUpdate++;
    285 					R_ClearEntityDefDynamicModel( def );
    286 					def->parms = *re;
    287 					return;
    288 				}
    289 			}
    290 		}
    291 
    292 		// save any decals if the model is the same, allowing marks to move with entities
    293 		if ( def->parms.hModel == re->hModel ) {
    294 			R_FreeEntityDefDerivedData( def, true, true );
    295 		} else {
    296 			R_FreeEntityDefDerivedData( def, false, false );
    297 		}
    298 	} else {
    299 		// creating a new one
    300 		def = new (TAG_RENDER_ENTITY) idRenderEntityLocal;
    301 		entityDefs[entityHandle] = def;
    302 
    303 		def->world = this;
    304 		def->index = entityHandle;
    305 	}
    306 
    307 	def->parms = *re;
    308 
    309 	def->lastModifiedFrameNum = tr.frameCount;
    310 	if ( common->WriteDemo() && def->archived ) {
    311 		WriteFreeEntity( entityHandle );
    312 		def->archived = false;
    313 	}
    314 
    315 	// optionally immediately issue any callbacks
    316 	if ( !r_useEntityCallbacks.GetBool() && def->parms.callback != NULL ) {
    317 		R_IssueEntityDefCallback( def );
    318 	}
    319 
    320 	// trigger entities don't need to get linked in and processed,
    321 	// they only exist for editor use
    322 	if ( def->parms.hModel != NULL && !def->parms.hModel->ModelHasDrawingSurfaces() ) {
    323 		return;
    324 	}
    325 
    326 	// based on the model bounds, add references in each area
    327 	// that may contain the updated surface
    328 	R_CreateEntityRefs( def );
    329 }
    330 
    331 /*
    332 ===================
    333 FreeEntityDef
    334 
    335 Frees all references and lit surfaces from the model, and
    336 NULL's out it's entry in the world list
    337 ===================
    338 */
    339 void idRenderWorldLocal::FreeEntityDef( qhandle_t entityHandle ) {
    340 	idRenderEntityLocal	*def;
    341 
    342 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
    343 		common->Printf( "idRenderWorld::FreeEntityDef: handle %i > %i\n", entityHandle, entityDefs.Num() );
    344 		return;
    345 	}
    346 
    347 	def = entityDefs[entityHandle];
    348 	if ( !def ) {
    349 		common->Printf( "idRenderWorld::FreeEntityDef: handle %i is NULL\n", entityHandle );
    350 		return;
    351 	}
    352 
    353 	R_FreeEntityDefDerivedData( def, false, false );
    354 
    355 	if ( common->WriteDemo() && def->archived ) {
    356 		WriteFreeEntity( entityHandle );
    357 	}
    358 
    359 	// if we are playing a demo, these will have been freed
    360 	// in R_FreeEntityDefDerivedData(), otherwise the gui
    361 	// object still exists in the game
    362 
    363 	def->parms.gui[ 0 ] = NULL;
    364 	def->parms.gui[ 1 ] = NULL;
    365 	def->parms.gui[ 2 ] = NULL;
    366 
    367 	delete def;
    368 	entityDefs[ entityHandle ] = NULL;
    369 }
    370 
    371 /*
    372 ==================
    373 GetRenderEntity
    374 ==================
    375 */
    376 const renderEntity_t *idRenderWorldLocal::GetRenderEntity( qhandle_t entityHandle ) const {
    377 	idRenderEntityLocal	*def;
    378 
    379 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
    380 		common->Printf( "idRenderWorld::GetRenderEntity: invalid handle %i [0, %i]\n", entityHandle, entityDefs.Num() );
    381 		return NULL;
    382 	}
    383 
    384 	def = entityDefs[entityHandle];
    385 	if ( !def ) {
    386 		common->Printf( "idRenderWorld::GetRenderEntity: handle %i is NULL\n", entityHandle );
    387 		return NULL;
    388 	}
    389 
    390 	return &def->parms;
    391 }
    392 
    393 /*
    394 ==================
    395 AddLightDef
    396 ==================
    397 */
    398 qhandle_t idRenderWorldLocal::AddLightDef( const renderLight_t *rlight ) {
    399 	// try and reuse a free spot
    400 	int lightHandle = lightDefs.FindNull();
    401 
    402 	if ( lightHandle == -1 ) {
    403 		lightHandle = lightDefs.Append( NULL );
    404 		if ( interactionTable && lightDefs.Num() > interactionTableHeight ) {
    405 			ResizeInteractionTable();
    406 		}
    407 	}
    408 	UpdateLightDef( lightHandle, rlight );
    409 
    410 	return lightHandle;
    411 }
    412 
    413 /*
    414 =================
    415 UpdateLightDef
    416 
    417 The generation of all the derived interaction data will
    418 usually be deferred until it is visible in a scene
    419 
    420 Does not write to the demo file, which will only be done for visible lights
    421 =================
    422 */
    423 void idRenderWorldLocal::UpdateLightDef( qhandle_t lightHandle, const renderLight_t *rlight ) {
    424 	if ( r_skipUpdates.GetBool() ) {
    425 		return;
    426 	}
    427 
    428 	tr.pc.c_lightUpdates++;
    429 
    430 	// create new slots if needed
    431 	if ( lightHandle < 0 || lightHandle > LUDICROUS_INDEX ) {
    432 		common->Error( "idRenderWorld::UpdateLightDef: index = %i", lightHandle );
    433 	}
    434 	while ( lightHandle >= lightDefs.Num() ) {
    435 		lightDefs.Append( NULL );
    436 	}
    437 
    438 	bool justUpdate = false;
    439 	idRenderLightLocal *light = lightDefs[lightHandle];
    440 	if ( light ) {
    441 		// if the shape of the light stays the same, we don't need to dump
    442 		// any of our derived data, because shader parms are calculated every frame
    443 		if ( rlight->axis == light->parms.axis && rlight->end == light->parms.end &&
    444 			 rlight->lightCenter == light->parms.lightCenter && rlight->lightRadius == light->parms.lightRadius &&
    445 			 rlight->noShadows == light->parms.noShadows && rlight->origin == light->parms.origin &&
    446 			 rlight->parallel == light->parms.parallel && rlight->pointLight == light->parms.pointLight &&
    447 			 rlight->right == light->parms.right && rlight->start == light->parms.start &&
    448 			 rlight->target == light->parms.target && rlight->up == light->parms.up && 
    449 			 rlight->shader == light->lightShader && rlight->prelightModel == light->parms.prelightModel ) {
    450 			justUpdate = true;
    451 		} else {
    452 			// if we are updating shadows, the prelight model is no longer valid
    453 			light->lightHasMoved = true;
    454 			R_FreeLightDefDerivedData( light );
    455 		}
    456 	} else {
    457 		// create a new one
    458 		light = new (TAG_RENDER_LIGHT) idRenderLightLocal;
    459 		lightDefs[lightHandle] = light;
    460 
    461 		light->world = this;
    462 		light->index = lightHandle;
    463 	}
    464 
    465 	light->parms = *rlight;
    466 	light->lastModifiedFrameNum = tr.frameCount;
    467 	if ( common->WriteDemo() && light->archived ) {
    468 		WriteFreeLight( lightHandle );
    469 		light->archived = false;
    470 	}
    471 
    472 	// new for BFG edition: force noShadows on spectrum lights so teleport spawns
    473 	// don't cause such a slowdown.  Hell writing shouldn't be shadowed anyway...
    474 	if ( light->parms.shader && light->parms.shader->Spectrum() ) {
    475 		light->parms.noShadows = true;
    476 	}
    477 
    478 	if ( light->lightHasMoved ) {
    479 		light->parms.prelightModel = NULL;
    480 	}
    481 
    482 	if ( !justUpdate ) {
    483 		R_CreateLightRefs( light );
    484 	}
    485 }
    486 
    487 /*
    488 ====================
    489 FreeLightDef
    490 
    491 Frees all references and lit surfaces from the light, and
    492 NULL's out it's entry in the world list
    493 ====================
    494 */
    495 void idRenderWorldLocal::FreeLightDef( qhandle_t lightHandle ) {
    496 	idRenderLightLocal	*light;
    497 
    498 	if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) {
    499 		common->Printf( "idRenderWorld::FreeLightDef: invalid handle %i [0, %i]\n", lightHandle, lightDefs.Num() );
    500 		return;
    501 	}
    502 
    503 	light = lightDefs[lightHandle];
    504 	if ( !light ) {
    505 		common->Printf( "idRenderWorld::FreeLightDef: handle %i is NULL\n", lightHandle );
    506 		return;
    507 	}
    508 
    509 	R_FreeLightDefDerivedData( light );
    510 
    511 	if ( common->WriteDemo() && light->archived ) {
    512 		WriteFreeLight( lightHandle );
    513 	}
    514 
    515 	delete light;
    516 	lightDefs[lightHandle] = NULL;
    517 }
    518 
    519 /*
    520 ==================
    521 GetRenderLight
    522 ==================
    523 */
    524 const renderLight_t *idRenderWorldLocal::GetRenderLight( qhandle_t lightHandle ) const {
    525 	idRenderLightLocal *def;
    526 
    527 	if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) {
    528 		common->Printf( "idRenderWorld::GetRenderLight: handle %i > %i\n", lightHandle, lightDefs.Num() );
    529 		return NULL;
    530 	}
    531 
    532 	def = lightDefs[lightHandle];
    533 	if ( !def ) {
    534 		common->Printf( "idRenderWorld::GetRenderLight: handle %i is NULL\n", lightHandle );
    535 		return NULL;
    536 	}
    537 
    538 	return &def->parms;
    539 }
    540 
    541 /*
    542 ================
    543 idRenderWorldLocal::ProjectDecalOntoWorld
    544 ================
    545 */
    546 void idRenderWorldLocal::ProjectDecalOntoWorld( const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) {
    547 	decalProjectionParms_t globalParms;
    548 
    549 	if ( !idRenderModelDecal::CreateProjectionParms( globalParms, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) {
    550 		return;
    551 	}
    552 
    553 	// get the world areas touched by the projection volume
    554 	int areas[10];
    555 	int numAreas = BoundsInAreas( globalParms.projectionBounds, areas, 10 );
    556 
    557 	// check all areas for models
    558 	for ( int i = 0; i < numAreas; i++ ) {
    559 
    560 		const portalArea_t * area = &portalAreas[ areas[i] ];
    561 
    562 		// check all models in this area
    563 		for ( const areaReference_t * ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) {
    564 			idRenderEntityLocal * def = ref->entity;
    565 
    566 			if ( def->parms.noOverlays ) {
    567 				continue;
    568 			}
    569 
    570 			if ( def->parms.customShader != NULL && !def->parms.customShader->AllowOverlays() ) {
    571 				continue;
    572 			}
    573 
    574 			// completely ignore any dynamic or callback models
    575 			const idRenderModel * model = def->parms.hModel;
    576 			if ( def->parms.callback != NULL || model == NULL || model->IsDynamicModel() != DM_STATIC ) {
    577 				continue;
    578 			}
    579 
    580 			idBounds bounds;
    581 			bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
    582 
    583 			// if the model bounds do not overlap with the projection bounds
    584 			decalProjectionParms_t localParms;
    585 			if ( !globalParms.projectionBounds.IntersectsBounds( bounds ) ) {
    586 				continue;
    587 			}
    588 
    589 			// transform the bounding planes, fade planes and texture axis into local space
    590 			idRenderModelDecal::GlobalProjectionParmsToLocal( localParms, globalParms, def->parms.origin, def->parms.axis );
    591 			localParms.force = ( def->parms.customShader != NULL );
    592 
    593 			if ( def->decals == NULL ) {
    594 				def->decals = AllocDecal( def->index, startTime );
    595 			}
    596 			def->decals->AddDeferredDecal( localParms );
    597 		}
    598 	}
    599 }
    600 
    601 /*
    602 ====================
    603 idRenderWorldLocal::ProjectDecal
    604 ====================
    605 */
    606 void idRenderWorldLocal::ProjectDecal( qhandle_t entityHandle, const idFixedWinding &winding, const idVec3 &projectionOrigin, const bool parallel, const float fadeDepth, const idMaterial *material, const int startTime ) {
    607 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
    608 		common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
    609 		return;
    610 	}
    611 
    612 	idRenderEntityLocal	*def = entityDefs[ entityHandle ];
    613 	if ( def == NULL ) {
    614 		return;
    615 	}
    616 
    617 	const idRenderModel *model = def->parms.hModel;
    618 
    619 	if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback != NULL ) {
    620 		return;
    621 	}
    622 
    623 	decalProjectionParms_t globalParms;
    624 	if ( !idRenderModelDecal::CreateProjectionParms( globalParms, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) {
    625 		return;
    626 	}
    627 
    628 	idBounds bounds;
    629 	bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
    630 
    631 	// if the model bounds do not overlap with the projection bounds
    632 	if ( !globalParms.projectionBounds.IntersectsBounds( bounds ) ) {
    633 		return;
    634 	}
    635 
    636 	// transform the bounding planes, fade planes and texture axis into local space
    637 	decalProjectionParms_t localParms;
    638 	idRenderModelDecal::GlobalProjectionParmsToLocal( localParms, globalParms, def->parms.origin, def->parms.axis );
    639 	localParms.force = ( def->parms.customShader != NULL );
    640 
    641 	if ( def->decals == NULL ) {
    642 		def->decals = AllocDecal( def->index, startTime );
    643 	}
    644 	def->decals->AddDeferredDecal( localParms );
    645 }
    646 
    647 /*
    648 ====================
    649 idRenderWorldLocal::ProjectOverlay
    650 ====================
    651 */
    652 void idRenderWorldLocal::ProjectOverlay( qhandle_t entityHandle, const idPlane localTextureAxis[2], const idMaterial *material, const int startTime ) {
    653 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
    654 		common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
    655 		return;
    656 	}
    657 
    658 	idRenderEntityLocal	*def = entityDefs[ entityHandle ];
    659 	if ( def == NULL ) {
    660 		return;
    661 	}
    662 
    663 	const idRenderModel * model = def->parms.hModel;
    664 	if ( model->IsDynamicModel() != DM_CACHED ) {	// FIXME: probably should be MD5 only
    665 		return;
    666 	}
    667 
    668 	overlayProjectionParms_t localParms;
    669 	localParms.localTextureAxis[0] = localTextureAxis[0];
    670 	localParms.localTextureAxis[1] = localTextureAxis[1];
    671 	localParms.material = material;
    672 	localParms.startTime = startTime;
    673 
    674 	if ( def->overlays == NULL ) {
    675 		def->overlays = AllocOverlay( def->index, startTime );
    676 	}
    677 	def->overlays->AddDeferredOverlay( localParms );
    678 }
    679 
    680 /*
    681 ====================
    682 idRenderWorldLocal::AllocDecal
    683 ====================
    684 */
    685 idRenderModelDecal * idRenderWorldLocal::AllocDecal( qhandle_t newEntityHandle, int startTime ) {
    686 	int oldest = 0;
    687 	int oldestTime = MAX_TYPE( oldestTime );
    688 	for ( int i = 0; i < decals.Num(); i++ ) {
    689 		if ( decals[i].lastStartTime < oldestTime ) {
    690 			oldestTime = decals[i].lastStartTime;
    691 			oldest = i;
    692 		}
    693 	}
    694 
    695 	// remove any reference another model may still have to this decal
    696 	if ( decals[oldest].entityHandle >= 0 && decals[oldest].entityHandle < entityDefs.Num() ) {
    697 		idRenderEntityLocal	*def = entityDefs[decals[oldest].entityHandle];
    698 		if ( def != NULL && def->decals == decals[oldest].decals ) {
    699 			def->decals = NULL;
    700 		}
    701 	}
    702 
    703 	decals[oldest].entityHandle = newEntityHandle;
    704 	decals[oldest].lastStartTime = startTime;
    705 	decals[oldest].decals->ReUse();
    706 
    707 	return decals[oldest].decals;
    708 }
    709 
    710 /*
    711 ====================
    712 idRenderWorldLocal::AllocOverlay
    713 ====================
    714 */
    715 idRenderModelOverlay * idRenderWorldLocal::AllocOverlay( qhandle_t newEntityHandle, int startTime ) {
    716 	int oldest = 0;
    717 	int oldestTime = MAX_TYPE( oldestTime );
    718 	for ( int i = 0; i < overlays.Num(); i++ ) {
    719 		if ( overlays[i].lastStartTime < oldestTime ) {
    720 			oldestTime = overlays[i].lastStartTime;
    721 			oldest = i;
    722 		}
    723 	}
    724 
    725 	// remove any reference another model may still have to this overlay
    726 	if ( overlays[oldest].entityHandle >= 0 && overlays[oldest].entityHandle < entityDefs.Num() ) {
    727 		idRenderEntityLocal	*def = entityDefs[overlays[oldest].entityHandle];
    728 		if ( def != NULL && def->overlays == overlays[oldest].overlays ) {
    729 			def->overlays = NULL;
    730 		}
    731 	}
    732 
    733 	overlays[oldest].entityHandle = newEntityHandle;
    734 	overlays[oldest].lastStartTime = startTime;
    735 	overlays[oldest].overlays->ReUse();
    736 
    737 	return overlays[oldest].overlays;
    738 }
    739 
    740 /*
    741 ====================
    742 idRenderWorldLocal::RemoveDecals
    743 ====================
    744 */
    745 void idRenderWorldLocal::RemoveDecals( qhandle_t entityHandle ) {
    746 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
    747 		common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
    748 		return;
    749 	}
    750 
    751 	idRenderEntityLocal	*def = entityDefs[ entityHandle ];
    752 	if ( !def ) {
    753 		return;
    754 	}
    755 
    756 	R_FreeEntityDefDecals( def );
    757 	R_FreeEntityDefOverlay( def );
    758 }
    759 
    760 /*
    761 ====================
    762 idRenderWorldLocal::SetRenderView
    763 
    764 Sets the current view so any calls to the render world will use the correct parms.
    765 ====================
    766 */
    767 void idRenderWorldLocal::SetRenderView( const renderView_t *renderView ) {
    768 	tr.primaryRenderView = *renderView;
    769 }
    770 
    771 /*
    772 ====================
    773 idRenderWorldLocal::RenderScene
    774 
    775 Draw a 3D view into a part of the window, then return
    776 to 2D drawing.
    777 
    778 Rendering a scene may require multiple views to be rendered
    779 to handle mirrors,
    780 ====================
    781 */
    782 void idRenderWorldLocal::RenderScene( const renderView_t *renderView ) {
    783 	if ( !R_IsInitialized() ) {
    784 		return;
    785 	}
    786 
    787 	renderView_t copy = *renderView;
    788 
    789 	// skip front end rendering work, which will result
    790 	// in only gui drawing
    791 	if ( r_skipFrontEnd.GetBool() ) {
    792 		return;
    793 	}
    794 
    795 	SCOPED_PROFILE_EVENT( "RenderWorld::RenderScene" );
    796 
    797 	if ( renderView->fov_x <= 0 || renderView->fov_y <= 0 ) {
    798 		common->Error( "idRenderWorld::RenderScene: bad FOVs: %f, %f", renderView->fov_x, renderView->fov_y );
    799 	}
    800 
    801 	// close any gui drawing
    802 	tr.guiModel->EmitFullScreen();
    803 	tr.guiModel->Clear();
    804 
    805 	int startTime = Sys_Microseconds();
    806 
    807 	// setup view parms for the initial view
    808 	viewDef_t * parms = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *parms ), FRAME_ALLOC_VIEW_DEF );
    809 	parms->renderView = *renderView;
    810 
    811 	if ( tr.takingScreenshot ) {
    812 		parms->renderView.forceUpdate = true;
    813 	}
    814 
    815 	int windowWidth = tr.GetWidth();
    816 	int windowHeight = tr.GetHeight();
    817 	tr.PerformResolutionScaling( windowWidth, windowHeight );
    818 
    819 	// screenFraction is just for quickly testing fill rate limitations
    820 	if ( r_screenFraction.GetInteger() != 100 ) {
    821 		windowWidth = ( windowWidth * r_screenFraction.GetInteger() ) / 100;
    822 		windowHeight = ( windowHeight * r_screenFraction.GetInteger() ) / 100;
    823 	}
    824 	tr.CropRenderSize( windowWidth, windowHeight );
    825 	tr.GetCroppedViewport( &parms->viewport );
    826 
    827 	// the scissor bounds may be shrunk in subviews even if
    828 	// the viewport stays the same
    829 	// this scissor range is local inside the viewport
    830 	parms->scissor.x1 = 0;
    831 	parms->scissor.y1 = 0;
    832 	parms->scissor.x2 = parms->viewport.x2 - parms->viewport.x1;
    833 	parms->scissor.y2 = parms->viewport.y2 - parms->viewport.y1;
    834 
    835 	parms->isSubview = false;
    836 	parms->initialViewAreaOrigin = renderView->vieworg;
    837 	parms->renderWorld = this;
    838 
    839 	// see if the view needs to reverse the culling sense in mirrors
    840 	// or environment cube sides
    841 	idVec3	cross;
    842 	cross = parms->renderView.viewaxis[1].Cross( parms->renderView.viewaxis[2] );
    843 	if ( cross * parms->renderView.viewaxis[0] > 0 ) {
    844 		parms->isMirror = false;
    845 	} else {
    846 		parms->isMirror = true;
    847 	}
    848 
    849 	// save this world for use by some console commands
    850 	tr.primaryWorld = this;
    851 	tr.primaryRenderView = *renderView;
    852 	tr.primaryView = parms;
    853 
    854 	// rendering this view may cause other views to be rendered
    855 	// for mirrors / portals / shadows / environment maps
    856 	// this will also cause any necessary entities and lights to be
    857 	// updated to the demo file
    858 	R_RenderView( parms );
    859 
    860 	// render any post processing after the view and all its subviews has been draw
    861 	R_RenderPostProcess( parms );
    862 
    863 	// now write delete commands for any modified-but-not-visible entities, and
    864 	// add the renderView command to the demo
    865 	if ( common->WriteDemo() ) {
    866 		WriteRenderView( renderView );
    867 	}
    868 
    869 #if 0
    870 	for ( int i = 0; i < entityDefs.Num(); i++ ) {
    871 		idRenderEntityLocal	*def = entityDefs[i];
    872 		if ( !def ) {
    873 			continue;
    874 		}
    875 		if ( def->parms.callback ) {
    876 			continue;
    877 		}
    878 		if ( def->parms.hModel->IsDynamicModel() == DM_CONTINUOUS ) {
    879 		}
    880 	}
    881 #endif
    882 
    883 	tr.UnCrop();
    884 
    885 	int endTime = Sys_Microseconds();
    886 
    887 	tr.pc.frontEndMicroSec += endTime - startTime;
    888 
    889 	// prepare for any 2D drawing after this
    890 	tr.guiModel->Clear();
    891 }
    892 
    893 /*
    894 ===================
    895 idRenderWorldLocal::NumAreas
    896 ===================
    897 */
    898 int idRenderWorldLocal::NumAreas() const {
    899 	return numPortalAreas;
    900 }
    901 
    902 /*
    903 ===================
    904 idRenderWorldLocal::NumPortalsInArea
    905 ===================
    906 */
    907 int idRenderWorldLocal::NumPortalsInArea( int areaNum ) {
    908 	portalArea_t	*area;
    909 	int				count;
    910 	portal_t		*portal;
    911 
    912 	if ( areaNum >= numPortalAreas || areaNum < 0 ) {
    913 		common->Error( "idRenderWorld::NumPortalsInArea: bad areanum %i", areaNum );
    914 	}
    915 	area = &portalAreas[areaNum];
    916 
    917 	count = 0;
    918 	for ( portal = area->portals; portal; portal = portal->next ) {
    919 		count++;
    920 	}
    921 	return count;
    922 }
    923 
    924 /*
    925 ===================
    926 idRenderWorldLocal::GetPortal
    927 ===================
    928 */
    929 exitPortal_t idRenderWorldLocal::GetPortal( int areaNum, int portalNum ) {
    930 	portalArea_t	*area;
    931 	int				count;
    932 	portal_t		*portal;
    933 	exitPortal_t	ret;
    934 
    935 	if ( areaNum > numPortalAreas ) {
    936 		common->Error( "idRenderWorld::GetPortal: areaNum > numAreas" );
    937 	}
    938 	area = &portalAreas[areaNum];
    939 
    940 	count = 0;
    941 	for ( portal = area->portals; portal; portal = portal->next ) {
    942 		if ( count == portalNum ) {
    943 			ret.areas[0] = areaNum;
    944 			ret.areas[1] = portal->intoArea;
    945 			ret.w = portal->w;
    946 			ret.blockingBits = portal->doublePortal->blockingBits;
    947 			ret.portalHandle = portal->doublePortal - doublePortals + 1;
    948 			return ret;
    949 		}
    950 		count++;
    951 	}
    952 
    953 	common->Error( "idRenderWorld::GetPortal: portalNum > numPortals" );
    954 
    955 	memset( &ret, 0, sizeof( ret ) );
    956 	return ret;
    957 }
    958 
    959 /*
    960 ===============
    961 idRenderWorldLocal::PointInAreaNum
    962 
    963 Will return -1 if the point is not in an area, otherwise
    964 it will return 0 <= value < tr.world->numPortalAreas
    965 ===============
    966 */
    967 int idRenderWorldLocal::PointInArea( const idVec3 &point ) const {
    968 	areaNode_t	*node;
    969 	int			nodeNum;
    970 	float		d;
    971 	
    972 	node = areaNodes;
    973 	if ( !node ) {
    974 		return -1;
    975 	}
    976 	while( 1 ) {
    977 		d = point * node->plane.Normal() + node->plane[3];
    978 		if (d > 0) {
    979 			nodeNum = node->children[0];
    980 		} else {
    981 			nodeNum = node->children[1];
    982 		}
    983 		if ( nodeNum == 0 ) {
    984 			return -1;		// in solid
    985 		}
    986 		if ( nodeNum < 0 ) {
    987 			nodeNum = -1 - nodeNum;
    988 			if ( nodeNum >= numPortalAreas ) {
    989 				common->Error( "idRenderWorld::PointInArea: area out of range" );
    990 			}
    991 			return nodeNum;
    992 		}
    993 		node = areaNodes + nodeNum;
    994 	}
    995 	
    996 	return -1;
    997 }
    998 
    999 /*
   1000 ===================
   1001 idRenderWorldLocal::BoundsInAreas_r
   1002 ===================
   1003 */
   1004 void idRenderWorldLocal::BoundsInAreas_r( int nodeNum, const idBounds &bounds, int *areas, int *numAreas, int maxAreas ) const {
   1005 	int side, i;
   1006 	areaNode_t *node;
   1007 
   1008 	do {
   1009 		if ( nodeNum < 0 ) {
   1010 			nodeNum = -1 - nodeNum;
   1011 
   1012 			for ( i = 0; i < (*numAreas); i++ ) {
   1013 				if ( areas[i] == nodeNum ) {
   1014 					break;
   1015 				}
   1016 			}
   1017 			if ( i >= (*numAreas) && (*numAreas) < maxAreas ) {
   1018 				areas[(*numAreas)++] = nodeNum;
   1019 			}
   1020 			return;
   1021 		}
   1022 
   1023 		node = areaNodes + nodeNum;
   1024 
   1025 		side = bounds.PlaneSide( node->plane );
   1026 		if ( side == PLANESIDE_FRONT ) {
   1027 			nodeNum = node->children[0];
   1028 		}
   1029 		else if ( side == PLANESIDE_BACK ) {
   1030 			nodeNum = node->children[1];
   1031 		}
   1032 		else {
   1033 			if ( node->children[1] != 0 ) {
   1034 				BoundsInAreas_r( node->children[1], bounds, areas, numAreas, maxAreas );
   1035 				if ( (*numAreas) >= maxAreas ) {
   1036 					return;
   1037 				}
   1038 			}
   1039 			nodeNum = node->children[0];
   1040 		}
   1041 	} while( nodeNum != 0 );
   1042 
   1043 	return;
   1044 }
   1045 
   1046 /*
   1047 ===================
   1048 idRenderWorldLocal::BoundsInAreas
   1049 
   1050   fills the *areas array with the number of the areas the bounds are in
   1051   returns the total number of areas the bounds are in
   1052 ===================
   1053 */
   1054 int idRenderWorldLocal::BoundsInAreas( const idBounds &bounds, int *areas, int maxAreas ) const {
   1055 	int numAreas = 0;
   1056 
   1057 	assert( areas );
   1058 	assert( bounds[0][0] <= bounds[1][0] && bounds[0][1] <= bounds[1][1] && bounds[0][2] <= bounds[1][2] );
   1059 	assert( bounds[1][0] - bounds[0][0] < 1e4f && bounds[1][1] - bounds[0][1] < 1e4f && bounds[1][2] - bounds[0][2] < 1e4f );
   1060 
   1061 	if ( !areaNodes ) {
   1062 		return numAreas;
   1063 	}
   1064 	BoundsInAreas_r( 0, bounds, areas, &numAreas, maxAreas );
   1065 	return numAreas;
   1066 }
   1067 
   1068 /*
   1069 ================
   1070 idRenderWorldLocal::GuiTrace
   1071 
   1072 checks a ray trace against any gui surfaces in an entity, returning the
   1073 fraction location of the trace on the gui surface, or -1,-1 if no hit.
   1074 this doesn't do any occlusion testing, simply ignoring non-gui surfaces.
   1075 start / end are in global world coordinates.
   1076 ================
   1077 */
   1078 guiPoint_t idRenderWorldLocal::GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const {
   1079 	guiPoint_t	pt;
   1080 	pt.x = pt.y = -1;
   1081 	pt.guiId = 0;
   1082 
   1083 	if ( ( entityHandle < 0 ) || ( entityHandle >= entityDefs.Num() ) ) {
   1084 		common->Printf( "idRenderWorld::GuiTrace: invalid handle %i\n", entityHandle );
   1085 		return pt;
   1086 	}
   1087 
   1088 	idRenderEntityLocal * def = entityDefs[entityHandle];	
   1089 	if ( def == NULL ) {
   1090 		common->Printf( "idRenderWorld::GuiTrace: handle %i is NULL\n", entityHandle );
   1091 		return pt;
   1092 	}
   1093 
   1094 	idRenderModel * model = def->parms.hModel;
   1095 	if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback != NULL ) {
   1096 		return pt;
   1097 	}
   1098 
   1099 	// transform the points into local space
   1100 	idVec3 localStart, localEnd;
   1101 	R_GlobalPointToLocal( def->modelMatrix, start, localStart );
   1102 	R_GlobalPointToLocal( def->modelMatrix, end, localEnd );
   1103 
   1104 	for ( int i = 0; i < model->NumSurfaces(); i++ ) {
   1105 		const modelSurface_t *surf = model->Surface( i );
   1106 
   1107 		const srfTriangles_t * tri = surf->geometry;
   1108 		if ( tri == NULL ) {
   1109 			continue;
   1110 		}
   1111 
   1112 		const idMaterial * shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
   1113 		if ( shader == NULL ) {
   1114 			continue;
   1115 		}
   1116 		// only trace against gui surfaces
   1117 		if ( !shader->HasGui() ) {
   1118 			continue;
   1119 		}
   1120 
   1121 		localTrace_t local = R_LocalTrace( localStart, localEnd, 0.0f, tri );
   1122 		if ( local.fraction < 1.0f ) {
   1123 			idVec3 origin, axis[3];
   1124 
   1125 			R_SurfaceToTextureAxis( tri, origin, axis );
   1126 			const idVec3 cursor = local.point - origin;
   1127 
   1128 			float axisLen[2];
   1129 			axisLen[0] = axis[0].Length();
   1130 			axisLen[1] = axis[1].Length();
   1131 
   1132 			pt.x = ( cursor * axis[0] ) / ( axisLen[0] * axisLen[0] );
   1133 			pt.y = ( cursor * axis[1] ) / ( axisLen[1] * axisLen[1] );
   1134 			pt.guiId = shader->GetEntityGui();
   1135 
   1136 			return pt;
   1137 		}
   1138 	}
   1139 
   1140 	return pt;
   1141 }
   1142 
   1143 /*
   1144 ===================
   1145 idRenderWorldLocal::ModelTrace
   1146 ===================
   1147 */
   1148 bool idRenderWorldLocal::ModelTrace( modelTrace_t &trace, qhandle_t entityHandle, const idVec3 &start, const idVec3 &end, const float radius ) const {
   1149 
   1150 	memset( &trace, 0, sizeof( trace ) );
   1151 	trace.fraction = 1.0f;
   1152 	trace.point = end;
   1153 
   1154 	if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
   1155 		return false;
   1156 	}
   1157 
   1158 	idRenderEntityLocal	*def = entityDefs[entityHandle];
   1159 	if ( def == NULL ) {
   1160 		return false;
   1161 	}
   1162 
   1163 	renderEntity_t *refEnt = &def->parms;
   1164 
   1165 	idRenderModel *model = R_EntityDefDynamicModel( def );
   1166 	if ( model == NULL ) {
   1167 		return false;
   1168 	}
   1169 
   1170 	// transform the points into local space
   1171 	float modelMatrix[16];
   1172 	idVec3 localStart;
   1173 	idVec3 localEnd;
   1174 	R_AxisToModelMatrix( refEnt->axis, refEnt->origin, modelMatrix );
   1175 	R_GlobalPointToLocal( modelMatrix, start, localStart );
   1176 	R_GlobalPointToLocal( modelMatrix, end, localEnd );
   1177 
   1178 	// if we have explicit collision surfaces, only collide against them
   1179 	// (FIXME, should probably have a parm to control this)
   1180 	bool collisionSurface = false;
   1181 	for ( int i = 0; i < model->NumBaseSurfaces(); i++ ) {
   1182 		const modelSurface_t *surf = model->Surface( i );
   1183 
   1184 		const idMaterial *shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
   1185 
   1186 		if ( shader->GetSurfaceFlags() & SURF_COLLISION ) {
   1187 			collisionSurface = true;
   1188 			break;
   1189 		}
   1190 	}
   1191 
   1192 	// only use baseSurfaces, not any overlays
   1193 	for ( int i = 0; i < model->NumBaseSurfaces(); i++ ) {
   1194 		const modelSurface_t *surf = model->Surface( i );
   1195 
   1196 		const idMaterial *shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
   1197 
   1198 		if ( surf->geometry == NULL || shader == NULL ) {
   1199 			continue;
   1200 		}
   1201 
   1202 		if ( collisionSurface ) {
   1203 			// only trace vs collision surfaces
   1204 			if ( ( shader->GetSurfaceFlags() & SURF_COLLISION ) == 0 ) {
   1205 				continue;
   1206 			}
   1207 		} else {
   1208 			// skip if not drawn or translucent
   1209 			if ( !shader->IsDrawn() || ( shader->Coverage() != MC_OPAQUE && shader->Coverage() != MC_PERFORATED ) ) {
   1210 				continue;
   1211 			}
   1212 		}
   1213 
   1214 		localTrace_t localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry );
   1215 
   1216 		if ( localTrace.fraction < trace.fraction ) {
   1217 			trace.fraction = localTrace.fraction;
   1218 			R_LocalPointToGlobal( modelMatrix, localTrace.point, trace.point );
   1219 			trace.normal = localTrace.normal * refEnt->axis;
   1220 			trace.material = shader;
   1221 			trace.entity = &def->parms;
   1222 			trace.jointNumber = refEnt->hModel->NearestJoint( i, localTrace.indexes[0], localTrace.indexes[1], localTrace.indexes[2] );
   1223 		}
   1224 	}
   1225 
   1226 	return ( trace.fraction < 1.0f );
   1227 }
   1228 
   1229 /*
   1230 ===================
   1231 idRenderWorldLocal::Trace
   1232 ===================
   1233 */
   1234 // FIXME: _D3XP added those.
   1235 const char * playerModelExcludeList[] = {
   1236 	"models/md5/characters/player/d3xp_spplayer.md5mesh",
   1237 	"models/md5/characters/player/head/d3xp_head.md5mesh",
   1238 	"models/md5/weapons/pistol_world/worldpistol.md5mesh",
   1239 	NULL
   1240 };
   1241 
   1242 const char * playerMaterialExcludeList[] = {
   1243 	"muzzlesmokepuff",
   1244 	NULL
   1245 };
   1246 
   1247 bool idRenderWorldLocal::Trace( modelTrace_t &trace, const idVec3 &start, const idVec3 &end, const float radius, bool skipDynamic, bool skipPlayer /*_D3XP*/ ) const {
   1248 	trace.fraction = 1.0f;
   1249 	trace.point = end;
   1250 
   1251 	// bounds for the whole trace
   1252 	idBounds traceBounds;
   1253 	traceBounds.Clear();
   1254 	traceBounds.AddPoint( start );
   1255 	traceBounds.AddPoint( end );
   1256 
   1257 	// get the world areas the trace is in
   1258 	int areas[128];
   1259 	int numAreas = BoundsInAreas( traceBounds, areas, 128 );
   1260 
   1261 	int numSurfaces = 0;
   1262 
   1263 	// check all areas for models
   1264 	for ( int i = 0; i < numAreas; i++ ) {
   1265 
   1266 		portalArea_t * area = &portalAreas[ areas[i] ];
   1267 
   1268 		// check all models in this area
   1269 		for ( areaReference_t * ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) {
   1270 			idRenderEntityLocal * def = ref->entity;
   1271 
   1272 			idRenderModel * model = def->parms.hModel;
   1273 			if ( model == NULL ) {
   1274 				continue;
   1275 			}
   1276 
   1277 			if ( model->IsDynamicModel() != DM_STATIC ) {
   1278 				if ( skipDynamic ) {
   1279 					continue;
   1280 				}
   1281 
   1282 #if 1	/* _D3XP addition. could use a cleaner approach */
   1283 				if ( skipPlayer ) {
   1284 					bool exclude = false;
   1285 					for ( int k = 0; playerModelExcludeList[k] != NULL; k++ ) {
   1286 						if ( idStr::Cmp( model->Name(), playerModelExcludeList[k] ) == 0 ) {
   1287 							exclude = true;
   1288 							break;
   1289 						}
   1290 					}
   1291 					if ( exclude ) {
   1292 						continue;
   1293 					}
   1294 				}
   1295 #endif
   1296 
   1297 				model = R_EntityDefDynamicModel( def );
   1298 				if ( !model ) {
   1299 					continue;	// can happen with particle systems, which don't instantiate without a valid view
   1300 				}
   1301 			}
   1302 
   1303 			idBounds bounds;
   1304 			bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
   1305 
   1306 			// if the model bounds do not overlap with the trace bounds
   1307 			if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) {
   1308 				continue;
   1309 			}
   1310 
   1311 			// check all model surfaces
   1312 			for ( int j = 0; j < model->NumSurfaces(); j++ ) {
   1313 				const modelSurface_t *surf = model->Surface( j );
   1314 
   1315 				const idMaterial * shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
   1316 
   1317 				// if no geometry or no shader
   1318 				if ( surf->geometry == NULL || shader == NULL ) {
   1319 					continue;
   1320 				}
   1321 
   1322 #if 1 /* _D3XP addition. could use a cleaner approach */
   1323 				if ( skipPlayer ) {
   1324 					bool exclude = false;
   1325 					for ( int k = 0; playerMaterialExcludeList[k] != NULL; k++ ) {
   1326 						if ( idStr::Cmp( shader->GetName(), playerMaterialExcludeList[k] ) == 0 ) {
   1327 							exclude = true;
   1328 							break;
   1329 						}
   1330 					}
   1331 					if ( exclude ) {
   1332 						continue;
   1333 					}
   1334 				}
   1335 #endif
   1336 
   1337 				const srfTriangles_t * tri = surf->geometry;
   1338 
   1339 				bounds.FromTransformedBounds( tri->bounds, def->parms.origin, def->parms.axis );
   1340 
   1341 				// if triangle bounds do not overlap with the trace bounds
   1342 				if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) {
   1343 					continue;
   1344 				}
   1345 
   1346 				numSurfaces++;
   1347 
   1348 				// transform the points into local space
   1349 				float modelMatrix[16];
   1350 				idVec3 localStart, localEnd;
   1351 				R_AxisToModelMatrix( def->parms.axis, def->parms.origin, modelMatrix );
   1352 				R_GlobalPointToLocal( modelMatrix, start, localStart );
   1353 				R_GlobalPointToLocal( modelMatrix, end, localEnd );
   1354 
   1355 				localTrace_t localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry );
   1356 
   1357 				if ( localTrace.fraction < trace.fraction ) {
   1358 					trace.fraction = localTrace.fraction;
   1359 					R_LocalPointToGlobal( modelMatrix, localTrace.point, trace.point );
   1360 					trace.normal = localTrace.normal * def->parms.axis;
   1361 					trace.material = shader;
   1362 					trace.entity = &def->parms;
   1363 					trace.jointNumber = model->NearestJoint( j, localTrace.indexes[0], localTrace.indexes[1], localTrace.indexes[2] );
   1364 
   1365 					traceBounds.Clear();
   1366 					traceBounds.AddPoint( start );
   1367 					traceBounds.AddPoint( start + trace.fraction * (end - start) );
   1368 				}
   1369 			}
   1370 		}
   1371 	}
   1372 	return ( trace.fraction < 1.0f );
   1373 }
   1374 
   1375 /*
   1376 ==================
   1377 idRenderWorldLocal::RecurseProcBSP
   1378 ==================
   1379 */
   1380 void idRenderWorldLocal::RecurseProcBSP_r( modelTrace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ) const {
   1381 	float		t1, t2;
   1382 	float		frac;
   1383 	idVec3		mid;
   1384 	int			side;
   1385 	float		midf;
   1386 	areaNode_t *node;
   1387 
   1388 	if ( results->fraction <= p1f) {
   1389 		return;		// already hit something nearer
   1390 	}
   1391 	// empty leaf
   1392 	if ( nodeNum < 0 ) {
   1393 		return;
   1394 	}
   1395 	// if solid leaf node
   1396 	if ( nodeNum == 0 ) {
   1397 		if ( parentNodeNum != -1 ) {
   1398 
   1399 			results->fraction = p1f;
   1400 			results->point = p1;
   1401 			node = &areaNodes[parentNodeNum];
   1402 			results->normal = node->plane.Normal();
   1403 			return;
   1404 		}
   1405 	}
   1406 	node = &areaNodes[nodeNum];
   1407 
   1408 	// distance from plane for trace start and end
   1409 	t1 = node->plane.Normal() * p1 + node->plane[3];
   1410 	t2 = node->plane.Normal() * p2 + node->plane[3];
   1411 
   1412 	if ( t1 >= 0.0f && t2 >= 0.0f ) {
   1413 		RecurseProcBSP_r( results, nodeNum, node->children[0], p1f, p2f, p1, p2 );
   1414 		return;
   1415 	}
   1416 	if ( t1 < 0.0f && t2 < 0.0f ) {
   1417 		RecurseProcBSP_r( results, nodeNum, node->children[1], p1f, p2f, p1, p2 );
   1418 		return;
   1419 	}
   1420 	side = t1 < t2;
   1421 	frac = t1 / (t1 - t2);
   1422 	midf = p1f + frac*(p2f - p1f);
   1423 	mid[0] = p1[0] + frac*(p2[0] - p1[0]);
   1424 	mid[1] = p1[1] + frac*(p2[1] - p1[1]);
   1425 	mid[2] = p1[2] + frac*(p2[2] - p1[2]);
   1426 	RecurseProcBSP_r( results, nodeNum, node->children[side], p1f, midf, p1, mid );
   1427 	RecurseProcBSP_r( results, nodeNum, node->children[side^1], midf, p2f, mid, p2 );
   1428 }
   1429 
   1430 /*
   1431 ==================
   1432 idRenderWorldLocal::FastWorldTrace
   1433 ==================
   1434 */
   1435 bool idRenderWorldLocal::FastWorldTrace( modelTrace_t &results, const idVec3 &start, const idVec3 &end ) const {
   1436 	memset( &results, 0, sizeof( modelTrace_t ) );
   1437 	results.fraction = 1.0f;
   1438 	if ( areaNodes != NULL ) {
   1439 		RecurseProcBSP_r( &results, -1, 0, 0.0f, 1.0f, start, end );
   1440 		return ( results.fraction < 1.0f );
   1441 	}
   1442 	return false;
   1443 }
   1444 
   1445 /*
   1446 =================================================================================
   1447 
   1448 CREATE MODEL REFS
   1449 
   1450 =================================================================================
   1451 */
   1452 
   1453 /*
   1454 =================
   1455 idRenderWorldLocal::AddEntityRefToArea
   1456 
   1457 This is called by R_PushVolumeIntoTree and also directly
   1458 for the world model references that are precalculated.
   1459 =================
   1460 */
   1461 void idRenderWorldLocal::AddEntityRefToArea( idRenderEntityLocal *def, portalArea_t *area ) {
   1462 	areaReference_t	*ref;
   1463 
   1464 	if ( def == NULL ) {
   1465 		common->Error( "idRenderWorldLocal::AddEntityRefToArea: NULL def" );
   1466 		return;
   1467 	}
   1468 
   1469 	for ( ref = def->entityRefs; ref != NULL; ref = ref->ownerNext ) {
   1470 		if ( ref->area == area ) {
   1471 			return;
   1472 		}
   1473 	}
   1474 
   1475 	ref = areaReferenceAllocator.Alloc();
   1476 
   1477 	tr.pc.c_entityReferences++;
   1478 
   1479 	ref->entity = def;
   1480 
   1481 	// link to entityDef
   1482 	ref->ownerNext = def->entityRefs;
   1483 	def->entityRefs = ref;
   1484 
   1485 	// link to end of area list
   1486 	ref->area = area;
   1487 	ref->areaNext = &area->entityRefs;
   1488 	ref->areaPrev = area->entityRefs.areaPrev;
   1489 	ref->areaNext->areaPrev = ref;
   1490 	ref->areaPrev->areaNext = ref;
   1491 }
   1492 
   1493 /*
   1494 ===================
   1495 idRenderWorldLocal::AddLightRefToArea
   1496 ===================
   1497 */
   1498 void idRenderWorldLocal::AddLightRefToArea( idRenderLightLocal *light, portalArea_t *area ) {
   1499 	areaReference_t	*lref;
   1500 
   1501 	for ( lref = light->references; lref != NULL; lref = lref->ownerNext ) {
   1502 		if ( lref->area == area ) {
   1503 			return;
   1504 		}
   1505 	}
   1506 
   1507 	// add a lightref to this area
   1508 	lref = areaReferenceAllocator.Alloc();
   1509 	lref->light = light;
   1510 	lref->area = area;
   1511 	lref->ownerNext = light->references;
   1512 	light->references = lref;
   1513 	tr.pc.c_lightReferences++;
   1514 
   1515 	// doubly linked list so we can free them easily later
   1516 	area->lightRefs.areaNext->areaPrev = lref;
   1517 	lref->areaNext = area->lightRefs.areaNext;
   1518 	lref->areaPrev = &area->lightRefs;
   1519 	area->lightRefs.areaNext = lref;
   1520 }
   1521 
   1522 /*
   1523 ===================
   1524 idRenderWorldLocal::GenerateAllInteractions
   1525 
   1526 Force the generation of all light / surface interactions at the start of a level
   1527 If this isn't called, they will all be dynamically generated
   1528 ===================
   1529 */
   1530 void idRenderWorldLocal::GenerateAllInteractions() {
   1531 	if ( !R_IsInitialized() ) {
   1532 		return;
   1533 	}
   1534 
   1535 	int start = Sys_Milliseconds();
   1536 
   1537 	generateAllInteractionsCalled = false;
   1538 
   1539 	// let the interaction creation code know that it shouldn't
   1540 	// try and do any view specific optimizations
   1541 	tr.viewDef = NULL;
   1542 
   1543 	// build the interaction table
   1544 	// this will be dynamically resized if the entity / light counts grow too much
   1545 	interactionTableWidth = entityDefs.Num() + 100;
   1546 	interactionTableHeight = lightDefs.Num() + 100;
   1547 	int	size =  interactionTableWidth * interactionTableHeight * sizeof( *interactionTable );
   1548 	interactionTable = (idInteraction **)R_ClearedStaticAlloc( size );
   1549 
   1550 	// itterate through all lights
   1551 	int	count = 0;
   1552 	for ( int i = 0; i < this->lightDefs.Num(); i++ ) {
   1553 		idRenderLightLocal	*ldef = this->lightDefs[i];
   1554 		if ( ldef == NULL ) {
   1555 			continue;
   1556 		}
   1557 
   1558 		// check all areas the light touches
   1559 		for ( areaReference_t *lref = ldef->references; lref; lref = lref->ownerNext ) {
   1560 			portalArea_t *area = lref->area;
   1561 
   1562 			// check all the models in this area
   1563 			for ( areaReference_t *eref = area->entityRefs.areaNext; eref != &area->entityRefs; eref = eref->areaNext ) {
   1564 				idRenderEntityLocal	 *edef = eref->entity;
   1565 
   1566 				// scan the doubly linked lists, which may have several dozen entries
   1567 				idInteraction	*inter;
   1568 
   1569 				// we could check either model refs or light refs for matches, but it is
   1570 				// assumed that there will be less lights in an area than models
   1571 				// so the entity chains should be somewhat shorter (they tend to be fairly close).
   1572 				for ( inter = edef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
   1573 					if ( inter->lightDef == ldef ) {
   1574 						break;
   1575 					}
   1576 				}
   1577 
   1578 				// if we already have an interaction, we don't need to do anything
   1579 				if ( inter != NULL ) {
   1580 					continue;
   1581 				}
   1582 
   1583 				// make an interaction for this light / entity pair
   1584 				// and add a pointer to it in the table
   1585 				inter = idInteraction::AllocAndLink( edef, ldef );
   1586 				count++;
   1587 
   1588 				// the interaction may create geometry
   1589 				inter->CreateStaticInteraction();
   1590 			}
   1591 		}
   1592 
   1593 		session->Pump();
   1594 	}
   1595 
   1596 	int end = Sys_Milliseconds();
   1597 	int	msec = end - start;
   1598 
   1599 	common->Printf( "idRenderWorld::GenerateAllInteractions, msec = %i\n", msec );
   1600 	common->Printf( "interactionTable size: %i bytes\n", size );
   1601 	common->Printf( "%i interactions take %i bytes\n", count, count * sizeof( idInteraction ) );
   1602 
   1603 	// entities flagged as noDynamicInteractions will no longer make any
   1604 	generateAllInteractionsCalled = true;
   1605 }
   1606 
   1607 /*
   1608 ===================
   1609 idRenderWorldLocal::FreeInteractions
   1610 ===================
   1611 */
   1612 void idRenderWorldLocal::FreeInteractions() {
   1613 	int			i;
   1614 	idRenderEntityLocal	*def;
   1615 
   1616 	for ( i = 0; i < entityDefs.Num(); i++ ) {
   1617 		def = entityDefs[i];
   1618 		if ( !def ) {
   1619 			continue;
   1620 		}
   1621 		// free all the interactions
   1622 		while ( def->firstInteraction != NULL ) {
   1623 			def->firstInteraction->UnlinkAndFree();
   1624 		}
   1625 	}
   1626 }
   1627 
   1628 /*
   1629 ==================
   1630 idRenderWorldLocal::PushFrustumIntoTree_r
   1631 
   1632 Used for both light volumes and model volumes.
   1633 
   1634 This does not clip the points by the planes, so some slop
   1635 occurs.
   1636 
   1637 tr.viewCount should be bumped before calling, allowing it
   1638 to prevent double checking areas.
   1639 
   1640 We might alternatively choose to do this with an area flow.
   1641 ==================
   1642 */
   1643 void idRenderWorldLocal::PushFrustumIntoTree_r( idRenderEntityLocal *def, idRenderLightLocal *light,
   1644 												const frustumCorners_t & corners, int nodeNum ) {
   1645 	if ( nodeNum < 0 ) {
   1646 		int areaNum = -1 - nodeNum;
   1647 		portalArea_t * area = &portalAreas[ areaNum ];
   1648 		if ( area->viewCount == tr.viewCount ) {
   1649 			return;	// already added a reference here
   1650 		}
   1651 		area->viewCount = tr.viewCount;
   1652 
   1653 		if ( def != NULL ) {
   1654 			AddEntityRefToArea( def, area );
   1655 		}
   1656 		if ( light != NULL ) {
   1657 			AddLightRefToArea( light, area );
   1658 		}
   1659 
   1660 		return;
   1661 	}
   1662 
   1663 	areaNode_t * node = areaNodes + nodeNum;
   1664 
   1665 	// if we know that all possible children nodes only touch an area
   1666 	// we have already marked, we can early out
   1667 	if ( node->commonChildrenArea != CHILDREN_HAVE_MULTIPLE_AREAS && r_useNodeCommonChildren.GetBool() ) {
   1668 		// note that we do NOT try to set a reference in this area
   1669 		// yet, because the test volume may yet wind up being in the
   1670 		// solid part, which would cause bounds slightly poked into
   1671 		// a wall to show up in the next room
   1672 		if ( portalAreas[ node->commonChildrenArea ].viewCount == tr.viewCount ) {
   1673 			return;
   1674 		}
   1675 	}
   1676 
   1677 	// exact check all the corners against the node plane
   1678 	frustumCull_t cull = idRenderMatrix::CullFrustumCornersToPlane( corners, node->plane );
   1679 
   1680 	if ( cull != FRUSTUM_CULL_BACK ) {
   1681 		nodeNum = node->children[0];
   1682 		if ( nodeNum != 0 ) {	// 0 = solid
   1683 			PushFrustumIntoTree_r( def, light, corners, nodeNum );
   1684 		}
   1685 	}
   1686 
   1687 	if ( cull != FRUSTUM_CULL_FRONT ) {
   1688 		nodeNum = node->children[1];
   1689 		if ( nodeNum != 0 ) {	// 0 = solid
   1690 			PushFrustumIntoTree_r( def, light, corners, nodeNum );
   1691 		}
   1692 	}
   1693 }
   1694 
   1695 /*
   1696 ==============
   1697 idRenderWorldLocal::PushFrustumIntoTree
   1698 ==============
   1699 */
   1700 void idRenderWorldLocal::PushFrustumIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light, const idRenderMatrix & frustumTransform, const idBounds & frustumBounds ) {
   1701 	if ( areaNodes == NULL ) {
   1702 		return;
   1703 	}
   1704 
   1705 	// calculate the corners of the frustum in word space
   1706 	ALIGNTYPE16 frustumCorners_t corners;
   1707 	idRenderMatrix::GetFrustumCorners( corners, frustumTransform, frustumBounds );
   1708 
   1709 	PushFrustumIntoTree_r( def, light, corners, 0 );
   1710 }
   1711 
   1712 //===================================================================
   1713 
   1714 /*
   1715 ====================
   1716 idRenderWorldLocal::DebugClearLines
   1717 ====================
   1718 */
   1719 void idRenderWorldLocal::DebugClearLines( int time ) {
   1720 	RB_ClearDebugLines( time );
   1721 	RB_ClearDebugText( time );
   1722 }
   1723 
   1724 /*
   1725 ====================
   1726 idRenderWorldLocal::DebugLine
   1727 ====================
   1728 */
   1729 void idRenderWorldLocal::DebugLine( const idVec4 &color, const idVec3 &start, const idVec3 &end, const int lifetime, const bool depthTest ) {
   1730 	RB_AddDebugLine( color, start, end, lifetime, depthTest );
   1731 }
   1732 
   1733 /*
   1734 ================
   1735 idRenderWorldLocal::DebugArrow
   1736 ================
   1737 */
   1738 void idRenderWorldLocal::DebugArrow( const idVec4 &color, const idVec3 &start, const idVec3 &end, int size, const int lifetime ) {
   1739 	idVec3 forward, right, up, v1, v2;
   1740 	float a, s;
   1741 	int i;
   1742 	static float arrowCos[40];
   1743 	static float arrowSin[40];
   1744 	static int arrowStep;
   1745 
   1746 	DebugLine( color, start, end, lifetime );
   1747 
   1748 	if ( r_debugArrowStep.GetInteger() <= 10 ) {
   1749 		return;
   1750 	}
   1751 	// calculate sine and cosine when step size changes
   1752 	if ( arrowStep != r_debugArrowStep.GetInteger() ) {
   1753 		arrowStep = r_debugArrowStep.GetInteger();
   1754 		for ( i = 0, a = 0; a < 360.0f; a += arrowStep, i++ ) {
   1755 			arrowCos[i] = idMath::Cos16( DEG2RAD( a ) );
   1756 			arrowSin[i] = idMath::Sin16( DEG2RAD( a ) );
   1757 		}
   1758 		arrowCos[i] = arrowCos[0];
   1759 		arrowSin[i] = arrowSin[0];
   1760 	}
   1761 	// draw a nice arrow
   1762 	forward = end - start;
   1763 	forward.Normalize();
   1764 	forward.NormalVectors( right, up);
   1765 	for ( i = 0, a = 0; a < 360.0f; a += arrowStep, i++ ) {
   1766 		s = 0.5f * size * arrowCos[i];
   1767 		v1 = end - size * forward;
   1768 		v1 = v1 + s * right;
   1769 		s = 0.5f * size * arrowSin[i];
   1770 		v1 = v1 + s * up;
   1771 
   1772 		s = 0.5f * size * arrowCos[i+1];
   1773 		v2 = end - size * forward;
   1774 		v2 = v2 + s * right;
   1775 		s = 0.5f * size * arrowSin[i+1];
   1776 		v2 = v2 + s * up;
   1777 
   1778 		DebugLine( color, v1, end, lifetime );
   1779 		DebugLine( color, v1, v2, lifetime );
   1780 	}
   1781 }
   1782 
   1783 /*
   1784 ====================
   1785 idRenderWorldLocal::DebugWinding
   1786 ====================
   1787 */
   1788 void idRenderWorldLocal::DebugWinding( const idVec4 &color, const idWinding &w, const idVec3 &origin, const idMat3 &axis, const int lifetime, const bool depthTest ) {
   1789 	int i;
   1790 	idVec3 point, lastPoint;
   1791 
   1792 	if ( w.GetNumPoints() < 2 ) {
   1793 		return;
   1794 	}
   1795 
   1796 	lastPoint = origin + w[w.GetNumPoints()-1].ToVec3() * axis;
   1797 	for ( i = 0; i < w.GetNumPoints(); i++ ) {
   1798 		point = origin + w[i].ToVec3() * axis;
   1799 		DebugLine( color, lastPoint, point, lifetime, depthTest );
   1800 		lastPoint = point;
   1801 	}
   1802 }
   1803 
   1804 /*
   1805 ====================
   1806 idRenderWorldLocal::DebugCircle
   1807 ====================
   1808 */
   1809 void idRenderWorldLocal::DebugCircle( const idVec4 &color, const idVec3 &origin, const idVec3 &dir, const float radius, const int numSteps, const int lifetime, const bool depthTest ) {
   1810 	int i;
   1811 	float a;
   1812 	idVec3 left, up, point, lastPoint;
   1813 
   1814 	dir.OrthogonalBasis( left, up );
   1815 	left *= radius;
   1816 	up *= radius;
   1817 	lastPoint = origin + up;
   1818 	for ( i = 1; i <= numSteps; i++ ) {
   1819 		a = idMath::TWO_PI * i / numSteps;
   1820 		point = origin + idMath::Sin16( a ) * left + idMath::Cos16( a ) * up;
   1821 		DebugLine( color, lastPoint, point, lifetime, depthTest );
   1822 		lastPoint = point;
   1823 	}
   1824 }
   1825 
   1826 /*
   1827 ============
   1828 idRenderWorldLocal::DebugSphere
   1829 ============
   1830 */
   1831 void idRenderWorldLocal::DebugSphere( const idVec4 &color, const idSphere &sphere, const int lifetime, const bool depthTest /*_D3XP*/ ) {
   1832 	int i, j, n, num;
   1833 	float s, c;
   1834 	idVec3 p, lastp, *lastArray;
   1835 
   1836 	num = 360 / 15;
   1837 	lastArray = (idVec3 *) _alloca16( num * sizeof( idVec3 ) );
   1838 	lastArray[0] = sphere.GetOrigin() + idVec3( 0, 0, sphere.GetRadius() );
   1839 	for ( n = 1; n < num; n++ ) {
   1840 		lastArray[n] = lastArray[0];
   1841 	}
   1842 
   1843 	for ( i = 15; i <= 360; i += 15 ) {
   1844 		s = idMath::Sin16( DEG2RAD(i) );
   1845 		c = idMath::Cos16( DEG2RAD(i) );
   1846 		lastp[0] = sphere.GetOrigin()[0];
   1847 		lastp[1] = sphere.GetOrigin()[1] + sphere.GetRadius() * s;
   1848 		lastp[2] = sphere.GetOrigin()[2] + sphere.GetRadius() * c;
   1849 		for ( n = 0, j = 15; j <= 360; j += 15, n++ ) {
   1850 			p[0] = sphere.GetOrigin()[0] + idMath::Sin16( DEG2RAD(j) ) * sphere.GetRadius() * s;
   1851 			p[1] = sphere.GetOrigin()[1] + idMath::Cos16( DEG2RAD(j) ) * sphere.GetRadius() * s;
   1852 			p[2] = lastp[2];
   1853 
   1854 			DebugLine( color, lastp, p, lifetime,depthTest );
   1855 			DebugLine( color, lastp, lastArray[n], lifetime, depthTest );
   1856 
   1857 			lastArray[n] = lastp;
   1858 			lastp = p;
   1859 		}
   1860 	}
   1861 }
   1862 
   1863 /*
   1864 ====================
   1865 idRenderWorldLocal::DebugBounds
   1866 ====================
   1867 */
   1868 void idRenderWorldLocal::DebugBounds( const idVec4 &color, const idBounds &bounds, const idVec3 &org, const int lifetime ) {
   1869 	int i;
   1870 	idVec3 v[8];
   1871 
   1872 	if ( bounds.IsCleared() ) {
   1873 		return;
   1874 	}
   1875 
   1876 	for ( i = 0; i < 8; i++ ) {
   1877 		v[i][0] = org[0] + bounds[(i^(i>>1))&1][0];
   1878 		v[i][1] = org[1] + bounds[(i>>1)&1][1];
   1879 		v[i][2] = org[2] + bounds[(i>>2)&1][2];
   1880 	}
   1881 	for ( i = 0; i < 4; i++ ) {
   1882 		DebugLine( color, v[i], v[(i+1)&3], lifetime );
   1883 		DebugLine( color, v[4+i], v[4+((i+1)&3)], lifetime );
   1884 		DebugLine( color, v[i], v[4+i], lifetime );
   1885 	}
   1886 }
   1887 
   1888 /*
   1889 ====================
   1890 idRenderWorldLocal::DebugBox
   1891 ====================
   1892 */
   1893 void idRenderWorldLocal::DebugBox( const idVec4 &color, const idBox &box, const int lifetime ) {
   1894 	int i;
   1895 	idVec3 v[8];
   1896 
   1897 	box.ToPoints( v );
   1898 	for ( i = 0; i < 4; i++ ) {
   1899 		DebugLine( color, v[i], v[(i+1)&3], lifetime );
   1900 		DebugLine( color, v[4+i], v[4+((i+1)&3)], lifetime );
   1901 		DebugLine( color, v[i], v[4+i], lifetime );
   1902 	}
   1903 }
   1904 
   1905 /*
   1906 ============
   1907 idRenderWorldLocal::DebugCone
   1908 
   1909   dir is the cone axis
   1910   radius1 is the radius at the apex
   1911   radius2 is the radius at apex+dir
   1912 ============
   1913 */
   1914 void idRenderWorldLocal::DebugCone( const idVec4 &color, const idVec3 &apex, const idVec3 &dir, float radius1, float radius2, const int lifetime ) {
   1915 	int i;
   1916 	idMat3 axis;
   1917 	idVec3 top, p1, p2, lastp1, lastp2, d;
   1918 
   1919 	axis[2] = dir;
   1920 	axis[2].Normalize();
   1921 	axis[2].NormalVectors( axis[0], axis[1] );
   1922 	axis[1] = -axis[1];
   1923 
   1924 	top = apex + dir;
   1925 	lastp2 = top + radius2 * axis[1];
   1926 
   1927 	if ( radius1 == 0.0f ) {
   1928 		for ( i = 20; i <= 360; i += 20 ) {
   1929 			d = idMath::Sin16( DEG2RAD(i) ) * axis[0] + idMath::Cos16( DEG2RAD(i) ) * axis[1];
   1930 			p2 = top + d * radius2;
   1931 			DebugLine( color, lastp2, p2, lifetime );
   1932 			DebugLine( color, p2, apex, lifetime );
   1933 			lastp2 = p2;
   1934 		}
   1935 	} else {
   1936 		lastp1 = apex + radius1 * axis[1];
   1937 		for ( i = 20; i <= 360; i += 20 ) {
   1938 			d = idMath::Sin16( DEG2RAD(i) ) * axis[0] + idMath::Cos16( DEG2RAD(i) ) * axis[1];
   1939 			p1 = apex + d * radius1;
   1940 			p2 = top + d * radius2;
   1941 			DebugLine( color, lastp1, p1, lifetime );
   1942 			DebugLine( color, lastp2, p2, lifetime );
   1943 			DebugLine( color, p1, p2, lifetime );
   1944 			lastp1 = p1;
   1945 			lastp2 = p2;
   1946 		}
   1947 	}
   1948 }
   1949 
   1950 /*
   1951 ================
   1952 idRenderWorldLocal::DebugAxis
   1953 ================
   1954 */
   1955 void idRenderWorldLocal::DebugAxis( const idVec3 &origin, const idMat3 &axis ) {
   1956 	idVec3 start = origin;
   1957 	idVec3 end = start + axis[0] * 20.0f;
   1958 	DebugArrow( colorWhite, start, end, 2 );
   1959 	end = start + axis[0] * -20.0f;
   1960 	DebugArrow( colorWhite, start, end, 2 );
   1961 	end = start + axis[1] * +20.0f;
   1962 	DebugArrow( colorGreen, start, end, 2 );
   1963 	end = start + axis[1] * -20.0f;
   1964 	DebugArrow( colorGreen, start, end, 2 );
   1965 	end = start + axis[2] * +20.0f;
   1966 	DebugArrow( colorBlue, start, end, 2 );
   1967 	end = start + axis[2] * -20.0f;
   1968 	DebugArrow( colorBlue, start, end, 2 );
   1969 }
   1970 
   1971 /*
   1972 ====================
   1973 idRenderWorldLocal::DebugClearPolygons
   1974 ====================
   1975 */
   1976 void idRenderWorldLocal::DebugClearPolygons( int time ) {
   1977 	RB_ClearDebugPolygons( time );
   1978 }
   1979 
   1980 /*
   1981 ====================
   1982 idRenderWorldLocal::DebugPolygon
   1983 ====================
   1984 */
   1985 void idRenderWorldLocal::DebugPolygon( const idVec4 &color, const idWinding &winding, const int lifeTime, const bool depthTest ) {
   1986 	RB_AddDebugPolygon( color, winding, lifeTime, depthTest );
   1987 }
   1988 
   1989 /*
   1990 ================
   1991 idRenderWorldLocal::DebugScreenRect
   1992 ================
   1993 */
   1994 void idRenderWorldLocal::DebugScreenRect( const idVec4 &color, const idScreenRect &rect, const viewDef_t *viewDef, const int lifetime ) {
   1995 	int i;
   1996 	float centerx, centery, dScale, hScale, vScale;
   1997 	idBounds bounds;
   1998 	idVec3 p[4];
   1999 
   2000 	centerx = ( viewDef->viewport.x2 - viewDef->viewport.x1 ) * 0.5f;
   2001 	centery = ( viewDef->viewport.y2 - viewDef->viewport.y1 ) * 0.5f;
   2002 
   2003 	dScale = r_znear.GetFloat() + 1.0f;
   2004 	hScale = dScale * idMath::Tan16( DEG2RAD( viewDef->renderView.fov_x * 0.5f ) );
   2005 	vScale = dScale * idMath::Tan16( DEG2RAD( viewDef->renderView.fov_y * 0.5f ) );
   2006 
   2007 	bounds[0][0] = bounds[1][0] = dScale;
   2008 	bounds[0][1] = -( rect.x1 - centerx ) / centerx * hScale;
   2009 	bounds[1][1] = -( rect.x2 - centerx ) / centerx * hScale;
   2010 	bounds[0][2] = ( rect.y1 - centery ) / centery * vScale;
   2011 	bounds[1][2] = ( rect.y2 - centery ) / centery * vScale;
   2012 
   2013 	for ( i = 0; i < 4; i++ ) {
   2014 		p[i].x = bounds[0][0];
   2015 		p[i].y = bounds[(i^(i>>1))&1].y;
   2016 		p[i].z = bounds[(i>>1)&1].z;
   2017 		p[i] = viewDef->renderView.vieworg + p[i] * viewDef->renderView.viewaxis;
   2018 	}
   2019 	for ( i = 0; i < 4; i++ ) {
   2020 		DebugLine( color, p[i], p[(i+1)&3], false );
   2021 	}
   2022 }
   2023 
   2024 /*
   2025 ================
   2026 idRenderWorldLocal::DrawTextLength
   2027 
   2028   returns the length of the given text
   2029 ================
   2030 */
   2031 float idRenderWorldLocal::DrawTextLength( const char *text, float scale, int len ) {
   2032 	return RB_DrawTextLength( text, scale, len );
   2033 }
   2034 
   2035 /*
   2036 ================
   2037 idRenderWorldLocal::DrawText
   2038 
   2039   oriented on the viewaxis
   2040   align can be 0-left, 1-center (default), 2-right
   2041 ================
   2042 */
   2043 void idRenderWorldLocal::DrawText( const char *text, const idVec3 &origin, float scale, const idVec4 &color, const idMat3 &viewAxis, const int align, const int lifetime, const bool depthTest ) {
   2044 	RB_AddDebugText( text, origin, scale, color, viewAxis, align, lifetime, depthTest );
   2045 }
   2046 
   2047 /*
   2048 ===============
   2049 idRenderWorldLocal::RegenerateWorld
   2050 ===============
   2051 */
   2052 void idRenderWorldLocal::RegenerateWorld() {
   2053 	R_FreeDerivedData();
   2054 	R_ReCreateWorldReferences();
   2055 }
   2056 
   2057 /*
   2058 ===============
   2059 R_GlobalShaderOverride
   2060 ===============
   2061 */
   2062 bool R_GlobalShaderOverride( const idMaterial **shader ) {
   2063 
   2064 	if ( !(*shader)->IsDrawn() ) {
   2065 		return false;
   2066 	}
   2067 
   2068 	if ( tr.primaryRenderView.globalMaterial ) {
   2069 		*shader = tr.primaryRenderView.globalMaterial;
   2070 		return true;
   2071 	}
   2072 
   2073 	if ( r_materialOverride.GetString()[0] != '\0' ) {
   2074 		*shader = declManager->FindMaterial( r_materialOverride.GetString() );
   2075 		return true;
   2076 	}
   2077 	
   2078 	return false;
   2079 }
   2080 
   2081 /*
   2082 ===============
   2083 R_RemapShaderBySkin
   2084 ===============
   2085 */
   2086 const idMaterial *R_RemapShaderBySkin( const idMaterial *shader, const idDeclSkin *skin, const idMaterial *customShader ) {
   2087 
   2088 	if ( !shader ) {
   2089 		return NULL;
   2090 	}
   2091 
   2092 	// never remap surfaces that were originally nodraw, like collision hulls
   2093 	if ( !shader->IsDrawn() ) {
   2094 		return shader;
   2095 	}
   2096 
   2097 	if ( customShader ) {
   2098 		// this is sort of a hack, but cause deformed surfaces to map to empty surfaces,
   2099 		// so the item highlight overlay doesn't highlight the autosprite surface
   2100 		if ( shader->Deform() ) {
   2101 			return NULL;
   2102 		}
   2103 		return const_cast<idMaterial *>(customShader);
   2104 	}
   2105 
   2106 	if ( !skin ) {
   2107 		return const_cast<idMaterial *>(shader);
   2108 	}
   2109 
   2110 	return skin->RemapShaderBySkin( shader );
   2111 }