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