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