2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
36 R_ListRenderLightDefs_f
39 void R_ListRenderLightDefs_f( const idCmdArgs &args ) {
41 idRenderLightLocal *ldef;
43 if ( !tr.primaryWorld ) {
50 for ( i = 0 ; i < tr.primaryWorld->lightDefs.Num() ; i++ ) {
51 ldef = tr.primaryWorld->lightDefs[i];
53 common->Printf( "%4i: FREED\n", i );
57 // count up the interactions
59 for ( idInteraction *inter = ldef->firstInteraction; inter != NULL; inter = inter->lightNext ) {
64 // count up the references
66 for ( areaReference_t *ref = ldef->references ; ref ; ref = ref->ownerNext ) {
71 common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, ldef->lightShader->GetName());
75 common->Printf( "%i lightDefs, %i interactions, %i areaRefs\n", active, totalIntr, totalRef );
80 R_ListRenderEntityDefs_f
83 void R_ListRenderEntityDefs_f( const idCmdArgs &args ) {
85 idRenderEntityLocal *mdef;
87 if ( !tr.primaryWorld ) {
94 for ( i = 0 ; i < tr.primaryWorld->entityDefs.Num() ; i++ ) {
95 mdef = tr.primaryWorld->entityDefs[i];
97 common->Printf( "%4i: FREED\n", i );
101 // count up the interactions
103 for ( idInteraction *inter = mdef->firstInteraction; inter != NULL; inter = inter->entityNext ) {
108 // count up the references
110 for ( areaReference_t *ref = mdef->entityRefs ; ref ; ref = ref->ownerNext ) {
115 common->Printf( "%4i: %3i intr %2i refs %s\n", i, iCount, rCount, mdef->parms.hModel->Name());
119 common->Printf( "total active: %i\n", active );
124 idRenderWorldLocal::idRenderWorldLocal
127 idRenderWorldLocal::idRenderWorldLocal() {
129 mapTimeStamp = FILE_NOT_FOUND_TIMESTAMP;
131 generateAllInteractionsCalled = false;
139 doublePortals = NULL;
140 numInterAreaPortals = 0;
142 interactionTable = 0;
143 interactionTableWidth = 0;
144 interactionTableHeight = 0;
149 idRenderWorldLocal::~idRenderWorldLocal
152 idRenderWorldLocal::~idRenderWorldLocal() {
153 // free all the entityDefs, lightDefs, portals, etc
156 // free up the debug lines, polys, and text
157 RB_ClearDebugPolygons( 0 );
158 RB_ClearDebugLines( 0 );
159 RB_ClearDebugText( 0 );
164 ResizeInteractionTable
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;
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();
190 UpdateEntityDef( entityHandle, re );
199 Does not write to the demo file, which will only be updated for
203 int c_callbackUpdate;
205 void idRenderWorldLocal::UpdateEntityDef( qhandle_t entityHandle, const renderEntity_t *re ) {
206 if ( r_skipUpdates.GetBool() ) {
210 tr.pc.c_entityUpdates++;
212 if ( !re->hModel && !re->callback ) {
213 common->Error( "idRenderWorld::UpdateEntityDef: NULL hModel" );
216 // create new slots if needed
217 if ( entityHandle < 0 || entityHandle > LUDICROUS_INDEX ) {
218 common->Error( "idRenderWorld::UpdateEntityDef: index = %i", entityHandle );
220 while ( entityHandle >= entityDefs.Num() ) {
221 entityDefs.Append( NULL );
224 idRenderEntityLocal *def = entityDefs[entityHandle];
227 if ( !re->forceUpdate ) {
229 // check for exact match (OPTIMIZE: check through pointers more)
230 if ( !re->joints && !re->callbackData && !def->dynamicModel && !memcmp( re, &def->parms, sizeof( *re ) ) ) {
234 // if the only thing that changed was shaderparms, we can just leave things as they are
235 // after updating parms
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 ) {
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 );
246 if ( boundsMatch && originMatch && axisMatch && modelMatch ) {
247 // only clear the dynamic model and interaction surfaces if they exist
249 R_ClearEntityDefDynamicModel( def );
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 );
260 R_FreeEntityDefDerivedData( def, false, false );
263 // creating a new one
264 def = new idRenderEntityLocal;
265 entityDefs[entityHandle] = def;
268 def->index = entityHandle;
273 R_AxisToModelMatrix( def->parms.axis, def->parms.origin, def->modelMatrix );
275 def->lastModifiedFrameNum = tr.frameCount;
276 if ( session->writeDemo && def->archived ) {
277 WriteFreeEntity( entityHandle );
278 def->archived = false;
281 // optionally immediately issue any callbacks
282 if ( !r_useEntityCallbacks.GetBool() && def->parms.callback ) {
283 R_IssueEntityDefCallback( def );
286 // based on the model bounds, add references in each area
287 // that may contain the updated surface
288 R_CreateEntityRefs( def );
295 Frees all references and lit surfaces from the model, and
296 NULL's out it's entry in the world list
299 void idRenderWorldLocal::FreeEntityDef( qhandle_t entityHandle ) {
300 idRenderEntityLocal *def;
302 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
303 common->Printf( "idRenderWorld::FreeEntityDef: handle %i > %i\n", entityHandle, entityDefs.Num() );
307 def = entityDefs[entityHandle];
309 common->Printf( "idRenderWorld::FreeEntityDef: handle %i is NULL\n", entityHandle );
313 R_FreeEntityDefDerivedData( def, false, false );
315 if ( session->writeDemo && def->archived ) {
316 WriteFreeEntity( entityHandle );
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
323 def->parms.gui[ 0 ] = NULL;
324 def->parms.gui[ 1 ] = NULL;
325 def->parms.gui[ 2 ] = NULL;
328 entityDefs[ entityHandle ] = NULL;
336 const renderEntity_t *idRenderWorldLocal::GetRenderEntity( qhandle_t entityHandle ) const {
337 idRenderEntityLocal *def;
339 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
340 common->Printf( "idRenderWorld::GetRenderEntity: invalid handle %i [0, %i]\n", entityHandle, entityDefs.Num() );
344 def = entityDefs[entityHandle];
346 common->Printf( "idRenderWorld::GetRenderEntity: handle %i is NULL\n", entityHandle );
358 qhandle_t idRenderWorldLocal::AddLightDef( const renderLight_t *rlight ) {
359 // try and reuse a free spot
360 int lightHandle = lightDefs.FindNull();
362 if ( lightHandle == -1 ) {
363 lightHandle = lightDefs.Append( NULL );
364 if ( interactionTable && lightDefs.Num() > interactionTableHeight ) {
365 ResizeInteractionTable();
368 UpdateLightDef( lightHandle, rlight );
377 The generation of all the derived interaction data will
378 usually be deferred until it is visible in a scene
380 Does not write to the demo file, which will only be done for visible lights
383 void idRenderWorldLocal::UpdateLightDef( qhandle_t lightHandle, const renderLight_t *rlight ) {
384 if ( r_skipUpdates.GetBool() ) {
388 tr.pc.c_lightUpdates++;
390 // create new slots if needed
391 if ( lightHandle < 0 || lightHandle > LUDICROUS_INDEX ) {
392 common->Error( "idRenderWorld::UpdateLightDef: index = %i", lightHandle );
394 while ( lightHandle >= lightDefs.Num() ) {
395 lightDefs.Append( NULL );
398 bool justUpdate = false;
399 idRenderLightLocal *light = lightDefs[lightHandle];
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 ) {
412 // if we are updating shadows, the prelight model is no longer valid
413 light->lightHasMoved = true;
414 R_FreeLightDefDerivedData( light );
418 light = new idRenderLightLocal;
419 lightDefs[lightHandle] = light;
422 light->index = lightHandle;
425 light->parms = *rlight;
426 light->lastModifiedFrameNum = tr.frameCount;
427 if ( session->writeDemo && light->archived ) {
428 WriteFreeLight( lightHandle );
429 light->archived = false;
432 if ( light->lightHasMoved ) {
433 light->parms.prelightModel = NULL;
437 R_DeriveLightData( light );
438 R_CreateLightRefs( light );
439 R_CreateLightDefFogPortals( light );
447 Frees all references and lit surfaces from the light, and
448 NULL's out it's entry in the world list
451 void idRenderWorldLocal::FreeLightDef( qhandle_t lightHandle ) {
452 idRenderLightLocal *light;
454 if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) {
455 common->Printf( "idRenderWorld::FreeLightDef: invalid handle %i [0, %i]\n", lightHandle, lightDefs.Num() );
459 light = lightDefs[lightHandle];
461 common->Printf( "idRenderWorld::FreeLightDef: handle %i is NULL\n", lightHandle );
465 R_FreeLightDefDerivedData( light );
467 if ( session->writeDemo && light->archived ) {
468 WriteFreeLight( lightHandle );
472 lightDefs[lightHandle] = NULL;
480 const renderLight_t *idRenderWorldLocal::GetRenderLight( qhandle_t lightHandle ) const {
481 idRenderLightLocal *def;
483 if ( lightHandle < 0 || lightHandle >= lightDefs.Num() ) {
484 common->Printf( "idRenderWorld::GetRenderLight: handle %i > %i\n", lightHandle, lightDefs.Num() );
488 def = lightDefs[lightHandle];
490 common->Printf( "idRenderWorld::GetRenderLight: handle %i is NULL\n", lightHandle );
499 idRenderWorldLocal::ProjectDecalOntoWorld
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;
510 if ( !idRenderModelDecal::CreateProjectionInfo( info, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) {
514 // get the world areas touched by the projection volume
515 numAreas = BoundsInAreas( info.projectionBounds, areas, 10 );
517 // check all areas for models
518 for ( i = 0; i < numAreas; i++ ) {
520 area = &portalAreas[ areas[i] ];
522 // check all models in this area
523 for ( ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) {
526 // completely ignore any dynamic or callback models
527 model = def->parms.hModel;
528 if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback ) {
532 if ( def->parms.customShader != NULL && !def->parms.customShader->AllowOverlays() ) {
537 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
539 // if the model bounds do not overlap with the projection bounds
540 if ( !info.projectionBounds.IntersectsBounds( bounds ) ) {
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 );
548 if ( !def->decals ) {
549 def->decals = idRenderModelDecal::Alloc();
551 def->decals->CreateDecal( model, localInfo );
558 idRenderWorldLocal::ProjectDecal
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;
564 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
565 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
569 idRenderEntityLocal *def = entityDefs[ entityHandle ];
574 const idRenderModel *model = def->parms.hModel;
576 if ( model == NULL || model->IsDynamicModel() != DM_STATIC || def->parms.callback ) {
580 if ( !idRenderModelDecal::CreateProjectionInfo( info, winding, projectionOrigin, parallel, fadeDepth, material, startTime ) ) {
585 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
587 // if the model bounds do not overlap with the projection bounds
588 if ( !info.projectionBounds.IntersectsBounds( bounds ) ) {
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 );
596 if ( def->decals == NULL ) {
597 def->decals = idRenderModelDecal::Alloc();
599 def->decals->CreateDecal( model, localInfo );
604 idRenderWorldLocal::ProjectOverlay
607 void idRenderWorldLocal::ProjectOverlay( qhandle_t entityHandle, const idPlane localTextureAxis[2], const idMaterial *material ) {
609 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
610 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
614 idRenderEntityLocal *def = entityDefs[ entityHandle ];
619 const renderEntity_t *refEnt = &def->parms;
621 idRenderModel *model = refEnt->hModel;
622 if ( model->IsDynamicModel() != DM_CACHED ) { // FIXME: probably should be MD5 only
625 model = R_EntityDefDynamicModel( def );
627 if ( def->overlay == NULL ) {
628 def->overlay = idRenderModelOverlay::Alloc();
630 def->overlay->CreateOverlay( model, localTextureAxis, material );
635 idRenderWorldLocal::RemoveDecals
638 void idRenderWorldLocal::RemoveDecals( qhandle_t entityHandle ) {
639 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
640 common->Error( "idRenderWorld::ProjectOverlay: index = %i", entityHandle );
644 idRenderEntityLocal *def = entityDefs[ entityHandle ];
649 R_FreeEntityDefDecals( def );
650 R_FreeEntityDefOverlay( def );
657 Sets the current view so any calls to the render world will use the correct parms.
660 void idRenderWorldLocal::SetRenderView( const renderView_t *renderView ) {
661 tr.primaryRenderView = *renderView;
668 Draw a 3D view into a part of the window, then return
671 Rendering a scene may require multiple views to be rendered
675 void idRenderWorldLocal::RenderScene( const renderView_t *renderView ) {
679 if ( !glConfig.isInitialized ) {
685 // skip front end rendering work, which will result
686 // in only gui drawing
687 if ( r_skipFrontEnd.GetBool() ) {
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 );
695 // close any gui drawing
696 tr.guiModel->EmitFullScreen();
697 tr.guiModel->Clear();
699 int startTime = Sys_Milliseconds();
701 // setup view parms for the initial view
703 viewDef_t *parms = (viewDef_t *)R_ClearedFrameAlloc( sizeof( *parms ) );
704 parms->renderView = *renderView;
706 if ( tr.takingScreenshot ) {
707 parms->renderView.forceUpdate = true;
710 // set up viewport, adjusted for resolution and OpenGL style 0 at the bottom
711 tr.RenderViewToViewport( &parms->renderView, &parms->viewport );
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;
722 parms->isSubview = false;
723 parms->initialViewAreaOrigin = renderView->vieworg;
724 parms->floatTime = parms->renderView.time * 0.001f;
725 parms->renderWorld = this;
727 // use this time for any subsequent 2D rendering, so damage blobs/etc
728 // can use level time
729 tr.frameShaderTime = parms->floatTime;
731 // see if the view needs to reverse the culling sense in mirrors
732 // or environment cube sides
734 cross = parms->renderView.viewaxis[1].Cross( parms->renderView.viewaxis[2] );
735 if ( cross * parms->renderView.viewaxis[0] > 0 ) {
736 parms->isMirror = false;
738 parms->isMirror = true;
741 if ( r_lockSurfaces.GetBool() ) {
742 R_LockSurfaceScene( parms );
746 // save this world for use by some console commands
747 tr.primaryWorld = this;
748 tr.primaryRenderView = *renderView;
749 tr.primaryView = parms;
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 );
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 );
764 for ( int i = 0 ; i < entityDefs.Num() ; i++ ) {
765 idRenderEntityLocal *def = entityDefs[i];
769 if ( def->parms.callback ) {
772 if ( def->parms.hModel->IsDynamicModel() == DM_CONTINUOUS ) {
777 int endTime = Sys_Milliseconds();
779 tr.pc.frontEndMsec += endTime - startTime;
781 // prepare for any 2D drawing after this
782 tr.guiModel->Clear();
791 int idRenderWorldLocal::NumAreas( void ) const {
792 return numPortalAreas;
800 int idRenderWorldLocal::NumPortalsInArea( int areaNum ) {
805 if ( areaNum >= numPortalAreas || areaNum < 0 ) {
806 common->Error( "idRenderWorld::NumPortalsInArea: bad areanum %i", areaNum );
808 area = &portalAreas[areaNum];
811 for ( portal = area->portals ; portal ; portal = portal->next ) {
822 exitPortal_t idRenderWorldLocal::GetPortal( int areaNum, int portalNum ) {
828 if ( areaNum > numPortalAreas ) {
829 common->Error( "idRenderWorld::GetPortal: areaNum > numAreas" );
831 area = &portalAreas[areaNum];
834 for ( portal = area->portals ; portal ; portal = portal->next ) {
835 if ( count == portalNum ) {
836 ret.areas[0] = areaNum;
837 ret.areas[1] = portal->intoArea;
839 ret.blockingBits = portal->doublePortal->blockingBits;
840 ret.portalHandle = portal->doublePortal - doublePortals + 1;
846 common->Error( "idRenderWorld::GetPortal: portalNum > numPortals" );
848 memset( &ret, 0, sizeof( ret ) );
856 Will return -1 if the point is not in an area, otherwise
857 it will return 0 <= value < tr.world->numPortalAreas
860 int idRenderWorldLocal::PointInArea( const idVec3 &point ) const {
870 d = point * node->plane.Normal() + node->plane[3];
872 nodeNum = node->children[0];
874 nodeNum = node->children[1];
876 if ( nodeNum == 0 ) {
877 return -1; // in solid
880 nodeNum = -1 - nodeNum;
881 if ( nodeNum >= numPortalAreas ) {
882 common->Error( "idRenderWorld::PointInArea: area out of range" );
886 node = areaNodes + nodeNum;
897 void idRenderWorldLocal::BoundsInAreas_r( int nodeNum, const idBounds &bounds, int *areas, int *numAreas, int maxAreas ) const {
903 nodeNum = -1 - nodeNum;
905 for ( i = 0; i < (*numAreas); i++ ) {
906 if ( areas[i] == nodeNum ) {
910 if ( i >= (*numAreas) && (*numAreas) < maxAreas ) {
911 areas[(*numAreas)++] = nodeNum;
916 node = areaNodes + nodeNum;
918 side = bounds.PlaneSide( node->plane );
919 if ( side == PLANESIDE_FRONT ) {
920 nodeNum = node->children[0];
922 else if ( side == PLANESIDE_BACK ) {
923 nodeNum = node->children[1];
926 if ( node->children[1] != 0 ) {
927 BoundsInAreas_r( node->children[1], bounds, areas, numAreas, maxAreas );
928 if ( (*numAreas) >= maxAreas ) {
932 nodeNum = node->children[0];
934 } while( nodeNum != 0 );
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
947 int idRenderWorldLocal::BoundsInAreas( const idBounds &bounds, int *areas, int maxAreas ) const {
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 );
957 BoundsInAreas_r( 0, bounds, areas, &numAreas, maxAreas );
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.
971 guiPoint_t idRenderWorldLocal::GuiTrace( qhandle_t entityHandle, const idVec3 start, const idVec3 end ) const {
973 idVec3 localStart, localEnd, bestPoint;
975 idRenderModel *model;
977 const idMaterial *shader;
983 if ( ( entityHandle < 0 ) || ( entityHandle >= entityDefs.Num() ) ) {
984 common->Printf( "idRenderWorld::GuiTrace: invalid handle %i\n", entityHandle );
988 idRenderEntityLocal *def = entityDefs[entityHandle];
990 common->Printf( "idRenderWorld::GuiTrace: handle %i is NULL\n", entityHandle );
994 model = def->parms.hModel;
995 if ( def->parms.callback || !def->parms.hModel || def->parms.hModel->IsDynamicModel() != DM_STATIC ) {
999 // transform the points into local space
1000 R_GlobalPointToLocal( def->modelMatrix, start, localStart );
1001 R_GlobalPointToLocal( def->modelMatrix, end, localEnd );
1004 float best = 99999.0;
1005 const modelSurface_t *bestSurf = NULL;
1007 for ( j = 0 ; j < model->NumSurfaces() ; j++ ) {
1008 const modelSurface_t *surf = model->Surface( j );
1010 tri = surf->geometry;
1015 shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
1019 // only trace against gui surfaces
1020 if (!shader->HasGui()) {
1024 local = R_LocalTrace( localStart, localEnd, 0.0f, tri );
1025 if ( local.fraction < 1.0 ) {
1026 idVec3 origin, axis[3];
1030 R_SurfaceToTextureAxis( tri, origin, axis );
1031 cursor = local.point - origin;
1033 axisLen[0] = axis[0].Length();
1034 axisLen[1] = axis[1].Length();
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();
1049 idRenderWorldLocal::ModelTrace
1052 bool idRenderWorldLocal::ModelTrace( modelTrace_t &trace, qhandle_t entityHandle, const idVec3 &start, const idVec3 &end, const float radius ) const {
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;
1062 trace.fraction = 1.0f;
1064 if ( entityHandle < 0 || entityHandle >= entityDefs.Num() ) {
1065 // common->Error( "idRenderWorld::ModelTrace: index = %i", entityHandle );
1069 idRenderEntityLocal *def = entityDefs[entityHandle];
1074 renderEntity_t *refEnt = &def->parms;
1076 model = R_EntityDefDynamicModel( def );
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 );
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 );
1092 shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
1094 if ( shader->GetSurfaceFlags() & SURF_COLLISION ) {
1095 collisionSurface = true;
1100 // only use baseSurfaces, not any overlays
1101 for ( i = 0; i < model->NumBaseSurfaces(); i++ ) {
1102 surf = model->Surface( i );
1104 shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
1106 if ( !surf->geometry || !shader ) {
1110 if ( collisionSurface ) {
1111 // only trace vs collision surfaces
1112 if ( !( shader->GetSurfaceFlags() & SURF_COLLISION ) ) {
1116 // skip if not drawn or translucent
1117 if ( !shader->IsDrawn() || ( shader->Coverage() != MC_OPAQUE && shader->Coverage() != MC_PERFORATED ) ) {
1122 localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry );
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] );
1134 return ( trace.fraction < 1.0f );
1139 idRenderWorldLocal::Trace
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",
1150 const char* playerMaterialExcludeList[] = {
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;
1168 trace.fraction = 1.0f;
1171 // bounds for the whole trace
1172 traceBounds.Clear();
1173 traceBounds.AddPoint( start );
1174 traceBounds.AddPoint( end );
1176 // get the world areas the trace is in
1177 numAreas = BoundsInAreas( traceBounds, areas, 128 );
1181 // check all areas for models
1182 for ( i = 0; i < numAreas; i++ ) {
1184 area = &portalAreas[ areas[i] ];
1186 // check all models in this area
1187 for ( ref = area->entityRefs.areaNext; ref != &area->entityRefs; ref = ref->areaNext ) {
1190 model = def->parms.hModel;
1195 if ( model->IsDynamicModel() != DM_STATIC ) {
1196 if ( skipDynamic ) {
1200 #if 1 /* _D3XP addition. could use a cleaner approach */
1202 idStr name = model->Name();
1203 const char *exclude;
1206 for ( k = 0; playerModelExcludeList[k]; k++ ) {
1207 exclude = playerModelExcludeList[k];
1208 if ( name == exclude ) {
1213 if ( playerModelExcludeList[k] ) {
1219 model = R_EntityDefDynamicModel( def );
1221 continue; // can happen with particle systems, which don't instantiate without a valid view
1225 bounds.FromTransformedBounds( model->Bounds( &def->parms ), def->parms.origin, def->parms.axis );
1227 // if the model bounds do not overlap with the trace bounds
1228 if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) {
1232 // check all model surfaces
1233 for ( j = 0; j < model->NumSurfaces(); j++ ) {
1234 const modelSurface_t *surf = model->Surface( j );
1236 shader = R_RemapShaderBySkin( surf->shader, def->parms.customSkin, def->parms.customShader );
1238 // if no geometry or no shader
1239 if ( !surf->geometry || !shader ) {
1243 #if 1 /* _D3XP addition. could use a cleaner approach */
1245 idStr name = shader->GetName();
1246 const char *exclude;
1249 for ( k = 0; playerMaterialExcludeList[k]; k++ ) {
1250 exclude = playerMaterialExcludeList[k];
1251 if ( name == exclude ) {
1256 if ( playerMaterialExcludeList[k] ) {
1262 tri = surf->geometry;
1264 bounds.FromTransformedBounds( tri->bounds, def->parms.origin, def->parms.axis );
1266 // if triangle bounds do not overlap with the trace bounds
1267 if ( !traceBounds.IntersectsBounds( bounds ) || !bounds.LineIntersection( start, trace.point ) ) {
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 );
1278 localTrace = R_LocalTrace( localStart, localEnd, radius, surf->geometry );
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] );
1288 traceBounds.Clear();
1289 traceBounds.AddPoint( start );
1290 traceBounds.AddPoint( start + trace.fraction * (end - start) );
1295 return ( trace.fraction < 1.0f );
1300 idRenderWorldLocal::RecurseProcBSP
1303 void idRenderWorldLocal::RecurseProcBSP_r( modelTrace_t *results, int parentNodeNum, int nodeNum, float p1f, float p2f, const idVec3 &p1, const idVec3 &p2 ) const {
1311 if ( results->fraction <= p1f) {
1312 return; // already hit something nearer
1315 if ( nodeNum < 0 ) {
1318 // if solid leaf node
1319 if ( nodeNum == 0 ) {
1320 if ( parentNodeNum != -1 ) {
1322 results->fraction = p1f;
1323 results->point = p1;
1324 node = &areaNodes[parentNodeNum];
1325 results->normal = node->plane.Normal();
1329 node = &areaNodes[nodeNum];
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];
1335 if ( t1 >= 0.0f && t2 >= 0.0f ) {
1336 RecurseProcBSP_r( results, nodeNum, node->children[0], p1f, p2f, p1, p2 );
1339 if ( t1 < 0.0f && t2 < 0.0f ) {
1340 RecurseProcBSP_r( results, nodeNum, node->children[1], p1f, p2f, p1, p2 );
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 );
1355 idRenderWorldLocal::FastWorldTrace
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 );
1369 =================================================================================
1373 =================================================================================
1380 This is called by R_PushVolumeIntoTree and also directly
1381 for the world model references that are precalculated.
1384 void idRenderWorldLocal::AddEntityRefToArea( idRenderEntityLocal *def, portalArea_t *area ) {
1385 areaReference_t *ref;
1388 common->Error( "idRenderWorldLocal::AddEntityRefToArea: NULL def" );
1391 ref = areaReferenceAllocator.Alloc();
1393 tr.pc.c_entityReferences++;
1397 // link to entityDef
1398 ref->ownerNext = def->entityRefs;
1399 def->entityRefs = ref;
1401 // link to end of area list
1403 ref->areaNext = &area->entityRefs;
1404 ref->areaPrev = area->entityRefs.areaPrev;
1405 ref->areaNext->areaPrev = ref;
1406 ref->areaPrev->areaNext = ref;
1415 void idRenderWorldLocal::AddLightRefToArea( idRenderLightLocal *light, portalArea_t *area ) {
1416 areaReference_t *lref;
1418 // add a lightref to this area
1419 lref = areaReferenceAllocator.Alloc();
1420 lref->light = light;
1422 lref->ownerNext = light->references;
1423 light->references = lref;
1424 tr.pc.c_lightReferences++;
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;
1435 GenerateAllInteractions
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
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
1445 void idRenderWorldLocal::GenerateAllInteractions() {
1446 if ( !glConfig.isInitialized ) {
1450 int start = Sys_Milliseconds();
1452 generateAllInteractionsCalled = false;
1454 // watch how much memory we allocate
1455 tr.staticAllocCount = 0;
1457 // let idRenderWorldLocal::CreateLightDefInteractions() know that it shouldn't
1458 // try and do any view specific optimizations
1461 for ( int i = 0 ; i < this->lightDefs.Num() ; i++ ) {
1462 idRenderLightLocal *ldef = this->lightDefs[i];
1466 this->CreateLightDefInteractions( ldef );
1469 int end = Sys_Milliseconds();
1470 int msec = end - start;
1472 common->Printf( "idRenderWorld::GenerateAllInteractions, msec = %i, staticAllocCount = %i.\n", msec, tr.staticAllocCount );
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 );
1483 for ( int i = 0 ; i < this->lightDefs.Num() ; i++ ) {
1484 idRenderLightLocal *ldef = this->lightDefs[i];
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;
1493 interactionTable[ index ] = inter;
1498 common->Printf( "interactionTable size: %i bytes\n", size );
1499 common->Printf( "%i interaction take %i bytes\n", count, count * sizeof( idInteraction ) );
1502 // entities flagged as noDynamicInteractions will no longer make any
1503 generateAllInteractionsCalled = true;
1508 idRenderWorldLocal::FreeInteractions
1511 void idRenderWorldLocal::FreeInteractions() {
1513 idRenderEntityLocal *def;
1515 for ( i = 0 ; i < entityDefs.Num(); i++ ) {
1516 def = entityDefs[i];
1520 // free all the interactions
1521 while ( def->firstInteraction != NULL ) {
1522 def->firstInteraction->UnlinkAndFree();
1531 Used for both light volumes and model volumes.
1533 This does not clip the points by the planes, so some slop
1536 tr.viewCount should be bumped before calling, allowing it
1537 to prevent double checking areas.
1539 We might alternatively choose to do this with an area flow.
1542 void idRenderWorldLocal::PushVolumeIntoTree_r( idRenderEntityLocal *def, idRenderLightLocal *light, const idSphere *sphere, int numPoints, const idVec3 (*points),
1548 if ( nodeNum < 0 ) {
1550 int areaNum = -1 - nodeNum;
1552 area = &portalAreas[ areaNum ];
1553 if ( area->viewCount == tr.viewCount ) {
1554 return; // already added a reference here
1556 area->viewCount = tr.viewCount;
1559 AddEntityRefToArea( def, area );
1562 AddLightRefToArea( light, area );
1568 node = areaNodes + nodeNum;
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 ) {
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 );
1593 if ( sd <= -sphere->GetRadius() ) {
1594 nodeNum = node->children[1];
1595 if ( nodeNum ) { // 0 = solid
1596 PushVolumeIntoTree_r( def, light, sphere, numPoints, points, nodeNum );
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;
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 ) {
1613 } else if ( !back && D0 <= 0.0f ) {
1616 D2 = points[i+1] * norm + plane3;
1617 if ( !front && D1 >= 0.0f ) {
1619 } else if ( !back && D1 <= 0.0f ) {
1622 D3 = points[i+1] * norm + plane3;
1623 if ( !front && D2 >= 0.0f ) {
1625 } else if ( !back && D2 <= 0.0f ) {
1629 if ( !front && D3 >= 0.0f ) {
1631 } else if ( !back && D3 <= 0.0f ) {
1634 if ( back && front ) {
1638 if(!(back && front)) {
1639 for (; i < numPoints ; i++ ) {
1641 d = points[i] * node->plane.Normal() + node->plane[3];
1644 } else if ( d <= 0.0f ) {
1647 if ( back && front ) {
1653 for ( i = 0 ; i < numPoints ; i++ ) {
1656 d = points[i] * node->plane.Normal() + node->plane[3];
1659 } else if ( d <= 0.0f ) {
1662 if ( back && front ) {
1668 nodeNum = node->children[0];
1669 if ( nodeNum ) { // 0 = solid
1670 PushVolumeIntoTree_r( def, light, sphere, numPoints, points, nodeNum );
1674 nodeNum = node->children[1];
1675 if ( nodeNum ) { // 0 = solid
1676 PushVolumeIntoTree_r( def, light, sphere, numPoints, points, nodeNum );
1686 void idRenderWorldLocal::PushVolumeIntoTree( idRenderEntityLocal *def, idRenderLightLocal *light, int numPoints, const idVec3 (*points) ) {
1688 float radSquared, lr;
1691 if ( areaNodes == NULL ) {
1695 // calculate a bounding sphere for the points
1697 for ( i = 0; i < numPoints; i++ ) {
1700 mid *= ( 1.0f / numPoints );
1704 for ( i = 0; i < numPoints; i++ ) {
1705 dir = points[i] - mid;
1707 if ( lr > radSquared ) {
1712 idSphere sphere( mid, sqrt( radSquared ) );
1714 PushVolumeIntoTree_r( def, light, &sphere, numPoints, points, 0 );
1717 //===================================================================
1720 ====================
1721 idRenderWorldLocal::DebugClearLines
1722 ====================
1724 void idRenderWorldLocal::DebugClearLines( int time ) {
1725 RB_ClearDebugLines( time );
1726 RB_ClearDebugText( time );
1730 ====================
1731 idRenderWorldLocal::DebugLine
1732 ====================
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 );
1740 idRenderWorldLocal::DebugArrow
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;
1747 static float arrowCos[40];
1748 static float arrowSin[40];
1749 static int arrowStep;
1751 DebugLine( color, start, end, lifetime );
1753 if ( r_debugArrowStep.GetInteger() <= 10 ) {
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 ) );
1763 arrowCos[i] = arrowCos[0];
1764 arrowSin[i] = arrowSin[0];
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];
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];
1783 DebugLine( color, v1, end, lifetime );
1784 DebugLine( color, v1, v2, lifetime );
1789 ====================
1790 idRenderWorldLocal::DebugWinding
1791 ====================
1793 void idRenderWorldLocal::DebugWinding( const idVec4 &color, const idWinding &w, const idVec3 &origin, const idMat3 &axis, const int lifetime, const bool depthTest ) {
1795 idVec3 point, lastPoint;
1797 if ( w.GetNumPoints() < 2 ) {
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 );
1810 ====================
1811 idRenderWorldLocal::DebugCircle
1812 ====================
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 ) {
1817 idVec3 left, up, point, lastPoint;
1819 dir.OrthogonalBasis( left, up );
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 );
1833 idRenderWorldLocal::DebugSphere
1836 void idRenderWorldLocal::DebugSphere( const idVec4 &color, const idSphere &sphere, const int lifetime, const bool depthTest /*_D3XP*/ ) {
1839 idVec3 p, lastp, *lastArray;
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];
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;
1859 DebugLine( color, lastp, p, lifetime,depthTest );
1860 DebugLine( color, lastp, lastArray[n], lifetime, depthTest );
1862 lastArray[n] = lastp;
1869 ====================
1870 idRenderWorldLocal::DebugBounds
1871 ====================
1873 void idRenderWorldLocal::DebugBounds( const idVec4 &color, const idBounds &bounds, const idVec3 &org, const int lifetime ) {
1877 if ( bounds.IsCleared() ) {
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];
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 );
1894 ====================
1895 idRenderWorldLocal::DebugBox
1896 ====================
1898 void idRenderWorldLocal::DebugBox( const idVec4 &color, const idBox &box, const int lifetime ) {
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 );
1912 idRenderWorldLocal::DebugFrustum
1915 void idRenderWorldLocal::DebugFrustum( const idVec4 &color, const idFrustum &frustum, const bool showFromOrigin, const int lifetime ) {
1919 frustum.ToPoints( v );
1921 if ( frustum.GetNearDistance() > 0.0f ) {
1922 for ( i = 0; i < 4; i++ ) {
1923 DebugLine( color, v[i], v[(i+1)&3], lifetime );
1925 if ( showFromOrigin ) {
1926 for ( i = 0; i < 4; i++ ) {
1927 DebugLine( color, frustum.GetOrigin(), v[i], lifetime );
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 );
1939 idRenderWorldLocal::DebugCone
1941 dir is the cone axis
1942 radius1 is the radius at the apex
1943 radius2 is the radius at apex+dir
1946 void idRenderWorldLocal::DebugCone( const idVec4 &color, const idVec3 &apex, const idVec3 &dir, float radius1, float radius2, const int lifetime ) {
1949 idVec3 top, p1, p2, lastp1, lastp2, d;
1952 axis[2].Normalize();
1953 axis[2].NormalVectors( axis[0], axis[1] );
1957 lastp2 = top + radius2 * axis[1];
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 );
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 );
1984 idRenderWorldLocal::DebugAxis
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 );
2004 ====================
2005 idRenderWorldLocal::DebugClearPolygons
2006 ====================
2008 void idRenderWorldLocal::DebugClearPolygons( int time ) {
2009 RB_ClearDebugPolygons( time );
2013 ====================
2014 idRenderWorldLocal::DebugPolygon
2015 ====================
2017 void idRenderWorldLocal::DebugPolygon( const idVec4 &color, const idWinding &winding, const int lifeTime, const bool depthTest ) {
2018 RB_AddDebugPolygon( color, winding, lifeTime, depthTest );
2023 idRenderWorldLocal::DebugScreenRect
2026 void idRenderWorldLocal::DebugScreenRect( const idVec4 &color, const idScreenRect &rect, const viewDef_t *viewDef, const int lifetime ) {
2028 float centerx, centery, dScale, hScale, vScale;
2032 centerx = ( viewDef->viewport.x2 - viewDef->viewport.x1 ) * 0.5f;
2033 centery = ( viewDef->viewport.y2 - viewDef->viewport.y1 ) * 0.5f;
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 ) );
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;
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;
2051 for ( i = 0; i < 4; i++ ) {
2052 DebugLine( color, p[i], p[(i+1)&3], false );
2058 idRenderWorldLocal::DrawTextLength
2060 returns the length of the given text
2063 float idRenderWorldLocal::DrawTextLength( const char *text, float scale, int len ) {
2064 return RB_DrawTextLength( text, scale, len );
2069 idRenderWorldLocal::DrawText
2071 oriented on the viewaxis
2072 align can be 0-left, 1-center (default), 2-right
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 );
2081 idRenderWorldLocal::RegenerateWorld
2084 void idRenderWorldLocal::RegenerateWorld() {
2085 R_RegenerateWorld_f( idCmdArgs() );
2090 R_GlobalShaderOverride
2093 bool R_GlobalShaderOverride( const idMaterial **shader ) {
2095 if ( !(*shader)->IsDrawn() ) {
2099 if ( tr.primaryRenderView.globalMaterial ) {
2100 *shader = tr.primaryRenderView.globalMaterial;
2104 if ( r_materialOverride.GetString()[0] != '\0' ) {
2105 *shader = declManager->FindMaterial( r_materialOverride.GetString() );
2117 const idMaterial *R_RemapShaderBySkin( const idMaterial *shader, const idDeclSkin *skin, const idMaterial *customShader ) {
2123 // never remap surfaces that were originally nodraw, like collision hulls
2124 if ( !shader->IsDrawn() ) {
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() ) {
2134 return const_cast<idMaterial *>(customShader);
2137 if ( !skin || !shader ) {
2138 return const_cast<idMaterial *>(shader);
2141 return skin->RemapShaderBySkin( shader );