/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 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. =========================================================================== */ #pragma hdrstop #include "precompiled.h" #include "tr_local.h" #include "simplex.h" // line font definition idCVar r_showCenterOfProjection( "r_showCenterOfProjection", "0", CVAR_RENDERER | CVAR_BOOL, "Draw a cross to show the center of projection" ); idCVar r_showLines( "r_showLines", "0", CVAR_RENDERER | CVAR_INTEGER, "1 = draw alternate horizontal lines, 2 = draw alternate vertical lines" ); #define MAX_DEBUG_LINES 16384 typedef struct debugLine_s { idVec4 rgb; idVec3 start; idVec3 end; bool depthTest; int lifeTime; } debugLine_t; debugLine_t rb_debugLines[ MAX_DEBUG_LINES ]; int rb_numDebugLines = 0; int rb_debugLineTime = 0; #define MAX_DEBUG_TEXT 512 typedef struct debugText_s { idStr text; idVec3 origin; float scale; idVec4 color; idMat3 viewAxis; int align; int lifeTime; bool depthTest; } debugText_t; debugText_t rb_debugText[ MAX_DEBUG_TEXT ]; int rb_numDebugText = 0; int rb_debugTextTime = 0; #define MAX_DEBUG_POLYGONS 8192 typedef struct debugPolygon_s { idVec4 rgb; idWinding winding; bool depthTest; int lifeTime; } debugPolygon_t; debugPolygon_t rb_debugPolygons[ MAX_DEBUG_POLYGONS ]; int rb_numDebugPolygons = 0; int rb_debugPolygonTime = 0; static void RB_DrawText( const char* text, const idVec3& origin, float scale, const idVec4& color, const idMat3& viewAxis, const int align ); void RB_SetMVP( const idRenderMatrix& mvp ); /* ================ RB_DrawBounds ================ */ void RB_DrawBounds( const idBounds& bounds ) { if( bounds.IsCleared() ) { return; } qglBegin( GL_LINE_LOOP ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] ); qglEnd(); qglBegin( GL_LINE_LOOP ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] ); qglEnd(); qglBegin( GL_LINES ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] ); qglVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] ); qglEnd(); } /* ================ RB_SimpleSurfaceSetup ================ */ static void RB_SimpleSurfaceSetup( const drawSurf_t* drawSurf ) { // change the matrix if needed if( drawSurf->space != backEnd.currentSpace ) { qglLoadMatrixf( drawSurf->space->modelViewMatrix ); backEnd.currentSpace = drawSurf->space; } // change the scissor if needed if( !backEnd.currentScissor.Equals( drawSurf->scissorRect ) && r_useScissor.GetBool() ) { GL_Scissor( backEnd.viewDef->viewport.x1 + drawSurf->scissorRect.x1, backEnd.viewDef->viewport.y1 + drawSurf->scissorRect.y1, drawSurf->scissorRect.x2 + 1 - drawSurf->scissorRect.x1, drawSurf->scissorRect.y2 + 1 - drawSurf->scissorRect.y1 ); backEnd.currentScissor = drawSurf->scissorRect; } } /* ================ RB_SimpleWorldSetup ================ */ static void RB_SimpleWorldSetup() { backEnd.currentSpace = &backEnd.viewDef->worldSpace; qglLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix ); GL_Scissor( backEnd.viewDef->viewport.x1 + backEnd.viewDef->scissor.x1, backEnd.viewDef->viewport.y1 + backEnd.viewDef->scissor.y1, backEnd.viewDef->scissor.x2 + 1 - backEnd.viewDef->scissor.x1, backEnd.viewDef->scissor.y2 + 1 - backEnd.viewDef->scissor.y1 ); backEnd.currentScissor = backEnd.viewDef->scissor; } /* ================= RB_PolygonClear This will cover the entire screen with normal rasterization. Texturing is disabled, but the existing glColor, glDepthMask, glColorMask, and the enabled state of depth buffering and stenciling will matter. ================= */ void RB_PolygonClear() { qglPushMatrix(); qglPushAttrib( GL_ALL_ATTRIB_BITS ); qglLoadIdentity(); qglDisable( GL_TEXTURE_2D ); qglDisable( GL_DEPTH_TEST ); qglDisable( GL_CULL_FACE ); qglDisable( GL_SCISSOR_TEST ); qglBegin( GL_POLYGON ); qglVertex3f( -20, -20, -10 ); qglVertex3f( 20, -20, -10 ); qglVertex3f( 20, 20, -10 ); qglVertex3f( -20, 20, -10 ); qglEnd(); qglPopAttrib(); qglPopMatrix(); } /* ==================== RB_ShowDestinationAlpha ==================== */ void RB_ShowDestinationAlpha() { GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1 ); RB_PolygonClear(); } /* =================== RB_ScanStencilBuffer Debugging tool to see what values are in the stencil buffer =================== */ void RB_ScanStencilBuffer() { int counts[256]; int i; byte* stencilReadback; memset( counts, 0, sizeof( counts ) ); stencilReadback = ( byte* )R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight(), TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); for( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { counts[ stencilReadback[i] ]++; } R_StaticFree( stencilReadback ); // print some stats (not supposed to do from back end in SMP...) common->Printf( "stencil values:\n" ); for( i = 0; i < 255; i++ ) { if( counts[i] ) { common->Printf( "%i: %i\n", i, counts[i] ); } } } /* =================== RB_CountStencilBuffer Print an overdraw count based on stencil index values =================== */ static void RB_CountStencilBuffer() { int count; int i; byte* stencilReadback; stencilReadback = ( byte* )R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight(), TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); count = 0; for( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { count += stencilReadback[i]; } R_StaticFree( stencilReadback ); // print some stats (not supposed to do from back end in SMP...) common->Printf( "overdraw: %5.1f\n", ( float )count / ( renderSystem->GetWidth() * renderSystem->GetHeight() ) ); } /* =================== R_ColorByStencilBuffer Sets the screen colors based on the contents of the stencil buffer. Stencil of 0 = black, 1 = red, 2 = green, 3 = blue, ..., 7+ = white =================== */ static void R_ColorByStencilBuffer() { int i; static float colors[8][3] = { {0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}, {0, 1, 1}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}, }; // clear color buffer to white (>6 passes) GL_Clear( true, false, false, 0, 1.0f, 1.0f, 1.0f, 1.0f ); // now draw color for each stencil value qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); for( i = 0; i < 6; i++ ) { GL_Color( colors[i] ); renderProgManager.BindShader_Color(); qglStencilFunc( GL_EQUAL, i, 255 ); RB_PolygonClear(); } qglStencilFunc( GL_ALWAYS, 0, 255 ); } //====================================================================== /* ================== RB_ShowOverdraw ================== */ void RB_ShowOverdraw() { const idMaterial* material; int i; drawSurf_t** drawSurfs; const drawSurf_t* surf; int numDrawSurfs; viewLight_t* vLight; if( r_showOverDraw.GetInteger() == 0 ) { return; } material = declManager->FindMaterial( "textures/common/overdrawtest", false ); if( material == NULL ) { return; } drawSurfs = backEnd.viewDef->drawSurfs; numDrawSurfs = backEnd.viewDef->numDrawSurfs; int interactions = 0; for( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) { for( surf = vLight->localInteractions; surf; surf = surf->nextOnLight ) { interactions++; } for( surf = vLight->globalInteractions; surf; surf = surf->nextOnLight ) { interactions++; } } // FIXME: can't frame alloc from the renderer back-end drawSurf_t** newDrawSurfs = ( drawSurf_t** )R_FrameAlloc( numDrawSurfs + interactions * sizeof( newDrawSurfs[0] ), FRAME_ALLOC_DRAW_SURFACE_POINTER ); for( i = 0; i < numDrawSurfs; i++ ) { surf = drawSurfs[i]; if( surf->material ) { const_cast( surf )->material = material; } newDrawSurfs[i] = const_cast( surf ); } for( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) { for( surf = vLight->localInteractions; surf; surf = surf->nextOnLight ) { const_cast( surf )->material = material; newDrawSurfs[i++] = const_cast( surf ); } for( surf = vLight->globalInteractions; surf; surf = surf->nextOnLight ) { const_cast( surf )->material = material; newDrawSurfs[i++] = const_cast( surf ); } vLight->localInteractions = NULL; vLight->globalInteractions = NULL; } switch( r_showOverDraw.GetInteger() ) { case 1: // geometry overdraw const_cast( backEnd.viewDef )->drawSurfs = newDrawSurfs; const_cast( backEnd.viewDef )->numDrawSurfs = numDrawSurfs; break; case 2: // light interaction overdraw const_cast( backEnd.viewDef )->drawSurfs = &newDrawSurfs[numDrawSurfs]; const_cast( backEnd.viewDef )->numDrawSurfs = interactions; break; case 3: // geometry + light interaction overdraw const_cast( backEnd.viewDef )->drawSurfs = newDrawSurfs; const_cast( backEnd.viewDef )->numDrawSurfs += interactions; break; } } /* =================== RB_ShowIntensity Debugging tool to see how much dynamic range a scene is using. The greatest of the rgb values at each pixel will be used, with the resulting color shading from red at 0 to green at 128 to blue at 255 =================== */ static void RB_ShowIntensity() { byte* colorReadback; int i, j, c; if( !r_showIntensity.GetBool() ) { return; } colorReadback = ( byte* )R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight() * 4, TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, colorReadback ); c = renderSystem->GetWidth() * renderSystem->GetHeight() * 4; for( i = 0; i < c; i += 4 ) { j = colorReadback[i]; if( colorReadback[i + 1] > j ) { j = colorReadback[i + 1]; } if( colorReadback[i + 2] > j ) { j = colorReadback[i + 2]; } if( j < 128 ) { colorReadback[i + 0] = 2 * ( 128 - j ); colorReadback[i + 1] = 2 * j; colorReadback[i + 2] = 0; } else { colorReadback[i + 0] = 0; colorReadback[i + 1] = 2 * ( 255 - j ); colorReadback[i + 2] = 2 * ( j - 128 ); } } // draw it back to the screen qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0, 0 ); qglPopMatrix(); GL_Color( 1, 1, 1 ); globalImages->BindNull(); qglMatrixMode( GL_MODELVIEW ); qglDrawPixels( renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA , GL_UNSIGNED_BYTE, colorReadback ); R_StaticFree( colorReadback ); } /* =================== RB_ShowDepthBuffer Draw the depth buffer as colors =================== */ static void RB_ShowDepthBuffer() { void* depthReadback; if( !r_showDepth.GetBool() ) { return; } qglPushMatrix(); qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0, 0 ); qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); qglPopMatrix(); GL_State( GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1 ); globalImages->BindNull(); depthReadback = R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight() * 4, TAG_RENDER_TOOLS ); memset( depthReadback, 0, renderSystem->GetWidth() * renderSystem->GetHeight() * 4 ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_DEPTH_COMPONENT , GL_FLOAT, depthReadback ); #if 0 for( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { ( ( byte* )depthReadback )[i * 4] = ( ( byte* )depthReadback )[i * 4 + 1] = ( ( byte* )depthReadback )[i * 4 + 2] = 255 * ( ( float* )depthReadback )[i]; ( ( byte* )depthReadback )[i * 4 + 3] = 1; } #endif qglDrawPixels( renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA , GL_UNSIGNED_BYTE, depthReadback ); R_StaticFree( depthReadback ); } /* ================= RB_ShowLightCount This is a debugging tool that will draw each surface with a color based on how many lights are effecting it ================= */ static void RB_ShowLightCount() { int i; const drawSurf_t* surf; const viewLight_t* vLight; if( !r_showLightCount.GetBool() ) { return; } RB_SimpleWorldSetup(); GL_Clear( false, false, true, 0, 0.0f, 0.0f, 0.0f, 0.0f ); // optionally count everything through walls if( r_showLightCount.GetInteger() >= 2 ) { GL_State( GLS_DEPTHFUNC_EQUAL | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_INCR | GLS_STENCIL_OP_PASS_INCR ); } else { GL_State( GLS_DEPTHFUNC_EQUAL | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_INCR ); } globalImages->defaultImage->Bind(); for( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) { for( i = 0; i < 2; i++ ) { for( surf = i ? vLight->localInteractions : vLight->globalInteractions; surf; surf = ( drawSurf_t* )surf->nextOnLight ) { RB_SimpleSurfaceSetup( surf ); RB_DrawElementsWithCounters( surf ); } } } // display the results R_ColorByStencilBuffer(); if( r_showLightCount.GetInteger() > 2 ) { RB_CountStencilBuffer(); } } /* =============== RB_SetWeaponDepthHack =============== */ static void RB_SetWeaponDepthHack() { } /* =============== RB_SetModelDepthHack =============== */ static void RB_SetModelDepthHack( float depth ) { } /* =============== RB_EnterWeaponDepthHack =============== */ static void RB_EnterWeaponDepthHack() { float matrix[16]; memcpy( matrix, backEnd.viewDef->projectionMatrix, sizeof( matrix ) ); const float modelDepthHack = 0.25f; matrix[2] *= modelDepthHack; matrix[6] *= modelDepthHack; matrix[10] *= modelDepthHack; matrix[14] *= modelDepthHack; qglMatrixMode( GL_PROJECTION ); qglLoadMatrixf( matrix ); qglMatrixMode( GL_MODELVIEW ); } /* =============== RB_EnterModelDepthHack =============== */ static void RB_EnterModelDepthHack( float depth ) { float matrix[16]; memcpy( matrix, backEnd.viewDef->projectionMatrix, sizeof( matrix ) ); matrix[14] -= depth; qglMatrixMode( GL_PROJECTION ); qglLoadMatrixf( matrix ); qglMatrixMode( GL_MODELVIEW ); } /* =============== RB_LeaveDepthHack =============== */ static void RB_LeaveDepthHack() { qglMatrixMode( GL_PROJECTION ); qglLoadMatrixf( backEnd.viewDef->projectionMatrix ); qglMatrixMode( GL_MODELVIEW ); } /* ============= RB_LoadMatrixWithBypass does a glLoadMatrixf after optionally applying the low-latency bypass matrix ============= */ static void RB_LoadMatrixWithBypass( const float m[16] ) { glLoadMatrixf( m ); } /* ==================== RB_RenderDrawSurfListWithFunction The triangle functions can check backEnd.currentSpace != surf->space to see if they need to perform any new matrix setup. The modelview matrix will already have been loaded, and backEnd.currentSpace will be updated after the triangle function completes. ==================== */ static void RB_RenderDrawSurfListWithFunction( drawSurf_t** drawSurfs, int numDrawSurfs, void ( *triFunc_ )( const drawSurf_t* ) ) { backEnd.currentSpace = NULL; for( int i = 0 ; i < numDrawSurfs ; i++ ) { const drawSurf_t* drawSurf = drawSurfs[i]; if( drawSurf == NULL ) { continue; } assert( drawSurf->space != NULL ); if( drawSurf->space != NULL ) // is it ever NULL? Do we need to check? { // Set these values ahead of time so we don't have to reconstruct the matrices on the consoles if( drawSurf->space->weaponDepthHack ) { RB_SetWeaponDepthHack(); } if( drawSurf->space->modelDepthHack != 0.0f ) { RB_SetModelDepthHack( drawSurf->space->modelDepthHack ); } // change the matrix if needed if( drawSurf->space != backEnd.currentSpace ) { RB_LoadMatrixWithBypass( drawSurf->space->modelViewMatrix ); } if( drawSurf->space->weaponDepthHack ) { RB_EnterWeaponDepthHack(); } if( drawSurf->space->modelDepthHack != 0.0f ) { RB_EnterModelDepthHack( drawSurf->space->modelDepthHack ); } } // change the scissor if needed if( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( drawSurf->scissorRect ) ) { backEnd.currentScissor = drawSurf->scissorRect; GL_Scissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1, backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1, backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1, backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 ); } // render it triFunc_( drawSurf ); if( drawSurf->space != NULL && ( drawSurf->space->weaponDepthHack || drawSurf->space->modelDepthHack != 0.0f ) ) { RB_LeaveDepthHack(); } backEnd.currentSpace = drawSurf->space; } } /* ================= RB_ShowSilhouette Blacks out all edges, then adds color for each edge that a shadow plane extends from, allowing you to see doubled edges FIXME: not thread safe! ================= */ static void RB_ShowSilhouette() { int i; const drawSurf_t* surf; const viewLight_t* vLight; if( !r_showSilhouette.GetBool() ) { return; } // // clear all triangle edges to black // globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); GL_Color( 0, 0, 0 ); GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_POLYMODE_LINE ); GL_Cull( CT_TWO_SIDED ); RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs, RB_DrawElementsWithCounters ); // // now blend in edges that cast silhouettes // RB_SimpleWorldSetup(); GL_Color( 0.5, 0, 0 ); GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); for( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) { for( i = 0; i < 2; i++ ) { for( surf = i ? vLight->localShadows : vLight->globalShadows ; surf; surf = ( drawSurf_t* )surf->nextOnLight ) { RB_SimpleSurfaceSetup( surf ); const srfTriangles_t* tri = surf->frontEndGeo; idVertexBuffer vertexBuffer; if( !vertexCache.GetVertexBuffer( tri->shadowCache, &vertexBuffer ) ) { continue; } // RB: 64 bit fixes, changed GLuint to GLintptrARB qglBindBufferARB( GL_ARRAY_BUFFER_ARB, ( GLintptrARB )vertexBuffer.GetAPIObject() ); GLintptrARB vertOffset = vertexBuffer.GetOffset(); // RB end qglVertexPointer( 3, GL_FLOAT, sizeof( idShadowVert ), ( void* )vertOffset ); qglBegin( GL_LINES ); for( int j = 0; j < tri->numIndexes; j += 3 ) { int i1 = tri->indexes[j + 0]; int i2 = tri->indexes[j + 1]; int i3 = tri->indexes[j + 2]; if( ( i1 & 1 ) + ( i2 & 1 ) + ( i3 & 1 ) == 1 ) { if( ( i1 & 1 ) + ( i2 & 1 ) == 0 ) { qglArrayElement( i1 ); qglArrayElement( i2 ); } else if( ( i1 & 1 ) + ( i3 & 1 ) == 0 ) { qglArrayElement( i1 ); qglArrayElement( i3 ); } } } qglEnd(); } } } GL_State( GLS_DEFAULT ); GL_Color( 1, 1, 1 ); GL_Cull( CT_FRONT_SIDED ); } /* ===================== RB_ShowTris Debugging tool ===================== */ static void RB_ShowTris( drawSurf_t** drawSurfs, int numDrawSurfs ) { modelTrace_t mt; idVec3 end; if( r_showTris.GetInteger() == 0 ) { return; } float color[4] = { 1, 1, 1, 1 }; GL_PolygonOffset( -1.0f, -2.0f ); switch( r_showTris.GetInteger() ) { case 1: // only draw visible ones GL_State( GLS_DEPTHMASK | GLS_ALPHAMASK | GLS_POLYMODE_LINE | GLS_POLYGON_OFFSET ); break; case 2: // draw all front facing case 3: // draw all GL_State( GLS_DEPTHMASK | GLS_ALPHAMASK | GLS_POLYMODE_LINE | GLS_POLYGON_OFFSET | GLS_DEPTHFUNC_ALWAYS ); break; case 4: // only draw visible ones with blended lines GL_State( GLS_DEPTHMASK | GLS_ALPHAMASK | GLS_POLYMODE_LINE | GLS_POLYGON_OFFSET | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); color[3] = 0.4f; break; } if( r_showTris.GetInteger() == 3 ) { GL_Cull( CT_TWO_SIDED ); } GL_Color( color ); renderProgManager.BindShader_Color(); RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_DrawElementsWithCounters ); if( r_showTris.GetInteger() == 3 ) { GL_Cull( CT_FRONT_SIDED ); } } /* ===================== RB_ShowSurfaceInfo Debugging tool ===================== */ static void RB_ShowSurfaceInfo( drawSurf_t** drawSurfs, int numDrawSurfs ) { modelTrace_t mt; idVec3 start, end; if( !r_showSurfaceInfo.GetBool() ) { return; } // start far enough away that we don't hit the player model start = tr.primaryView->renderView.vieworg + tr.primaryView->renderView.viewaxis[0] * 16; end = start + tr.primaryView->renderView.viewaxis[0] * 1000.0f; if( !tr.primaryWorld->Trace( mt, start, end, 0.0f, false ) ) { return; } globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); GL_Color( 1, 1, 1 ); static float scale = -1; static float bias = -2; GL_PolygonOffset( scale, bias ); GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_POLYMODE_LINE | GLS_POLYGON_OFFSET ); idVec3 trans[3]; float matrix[16]; // transform the object verts into global space R_AxisToModelMatrix( mt.entity->axis, mt.entity->origin, matrix ); tr.primaryWorld->DrawText( mt.entity->hModel->Name(), mt.point + tr.primaryView->renderView.viewaxis[2] * 12, 0.35f, colorRed, tr.primaryView->renderView.viewaxis ); tr.primaryWorld->DrawText( mt.material->GetName(), mt.point, 0.35f, colorBlue, tr.primaryView->renderView.viewaxis ); } /* ===================== RB_ShowViewEntitys Debugging tool ===================== */ static void RB_ShowViewEntitys( viewEntity_t* vModels ) { if( !r_showViewEntitys.GetBool() ) { return; } if( r_showViewEntitys.GetInteger() >= 2 ) { common->Printf( "view entities: " ); for( const viewEntity_t* vModel = vModels; vModel; vModel = vModel->next ) { if( vModel->entityDef->IsDirectlyVisible() ) { common->Printf( "<%i> ", vModel->entityDef->index ); } else { common->Printf( "%i ", vModel->entityDef->index ); } } common->Printf( "\n" ); } globalImages->BindNull(); renderProgManager.BindShader_Color(); GL_Color( 1, 1, 1 ); GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_POLYMODE_LINE ); GL_Cull( CT_TWO_SIDED ); for( const viewEntity_t* vModel = vModels; vModel; vModel = vModel->next ) { idBounds b; qglLoadMatrixf( vModel->modelViewMatrix ); const idRenderEntityLocal* edef = vModel->entityDef; if( !edef ) { continue; } // draw the model bounds in white if directly visible, // or, blue if it is only-for-sahdow idVec4 color; if( edef->IsDirectlyVisible() ) { color.Set( 1, 1, 1, 1 ); } else { color.Set( 0, 0, 1, 1 ); } GL_Color( color[0], color[1], color[2] ); RB_DrawBounds( edef->localReferenceBounds ); // transform the upper bounds corner into global space if( r_showViewEntitys.GetInteger() >= 2 ) { idVec3 corner; R_LocalPointToGlobal( vModel->modelMatrix, edef->localReferenceBounds[1], corner ); tr.primaryWorld->DrawText( va( "%i:%s", edef->index, edef->parms.hModel->Name() ), corner, 0.25f, color, tr.primaryView->renderView.viewaxis ); } // draw the actual bounds in yellow if different if( r_showViewEntitys.GetInteger() >= 3 ) { GL_Color( 1, 1, 0 ); // FIXME: cannot instantiate a dynamic model from the renderer back-end idRenderModel* model = R_EntityDefDynamicModel( vModel->entityDef ); if( !model ) { continue; // particles won't instantiate without a current view } b = model->Bounds( &vModel->entityDef->parms ); if( b != vModel->entityDef->localReferenceBounds ) { RB_DrawBounds( b ); } } } } /* ===================== RB_ShowTexturePolarity Shade triangle red if they have a positive texture area green if they have a negative texture area, or blue if degenerate area ===================== */ static void RB_ShowTexturePolarity( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; const srfTriangles_t* tri; if( !r_showTexturePolarity.GetBool() ) { return; } globalImages->BindNull(); GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); GL_Color( 1, 1, 1 ); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); qglBegin( GL_TRIANGLES ); for( j = 0; j < tri->numIndexes; j += 3 ) { idDrawVert* a, *b, *c; float d0[5], d1[5]; float area; a = tri->verts + tri->indexes[j]; b = tri->verts + tri->indexes[j + 1]; c = tri->verts + tri->indexes[j + 2]; const idVec2 aST = a->GetTexCoord(); const idVec2 bST = b->GetTexCoord(); const idVec2 cST = c->GetTexCoord(); d0[3] = bST[0] - aST[0]; d0[4] = bST[1] - aST[1]; d1[3] = cST[0] - aST[0]; d1[4] = cST[1] - aST[1]; area = d0[3] * d1[4] - d0[4] * d1[3]; if( idMath::Fabs( area ) < 0.0001 ) { GL_Color( 0, 0, 1, 0.5 ); } else if( area < 0 ) { GL_Color( 1, 0, 0, 0.5 ); } else { GL_Color( 0, 1, 0, 0.5 ); } qglVertex3fv( a->xyz.ToFloatPtr() ); qglVertex3fv( b->xyz.ToFloatPtr() ); qglVertex3fv( c->xyz.ToFloatPtr() ); } qglEnd(); } GL_State( GLS_DEFAULT ); } /* ===================== RB_ShowUnsmoothedTangents Shade materials that are using unsmoothed tangents ===================== */ static void RB_ShowUnsmoothedTangents( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; const srfTriangles_t* tri; if( !r_showUnsmoothedTangents.GetBool() ) { return; } globalImages->BindNull(); GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); GL_Color( 0, 1, 0, 0.5 ); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; if( !drawSurf->material->UseUnsmoothedTangents() ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->frontEndGeo; qglBegin( GL_TRIANGLES ); for( j = 0; j < tri->numIndexes; j += 3 ) { idDrawVert* a, *b, *c; a = tri->verts + tri->indexes[j]; b = tri->verts + tri->indexes[j + 1]; c = tri->verts + tri->indexes[j + 2]; qglVertex3fv( a->xyz.ToFloatPtr() ); qglVertex3fv( b->xyz.ToFloatPtr() ); qglVertex3fv( c->xyz.ToFloatPtr() ); } qglEnd(); } GL_State( GLS_DEFAULT ); } /* ===================== RB_ShowTangentSpace Shade a triangle by the RGB colors of its tangent space 1 = tangents[0] 2 = tangents[1] 3 = normal ===================== */ static void RB_ShowTangentSpace( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; const srfTriangles_t* tri; if( !r_showTangentSpace.GetInteger() ) { return; } globalImages->BindNull(); GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } qglBegin( GL_TRIANGLES ); for( j = 0; j < tri->numIndexes; j++ ) { const idDrawVert* v; v = &tri->verts[tri->indexes[j]]; if( r_showTangentSpace.GetInteger() == 1 ) { const idVec3 vertexTangent = v->GetTangent(); GL_Color( 0.5 + 0.5 * vertexTangent[0], 0.5 + 0.5 * vertexTangent[1], 0.5 + 0.5 * vertexTangent[2], 0.5 ); } else if( r_showTangentSpace.GetInteger() == 2 ) { const idVec3 vertexBiTangent = v->GetBiTangent(); GL_Color( 0.5 + 0.5 * vertexBiTangent[0], 0.5 + 0.5 * vertexBiTangent[1], 0.5 + 0.5 * vertexBiTangent[2], 0.5 ); } else { const idVec3 vertexNormal = v->GetNormal(); GL_Color( 0.5 + 0.5 * vertexNormal[0], 0.5 + 0.5 * vertexNormal[1], 0.5 + 0.5 * vertexNormal[2], 0.5 ); } qglVertex3fv( v->xyz.ToFloatPtr() ); } qglEnd(); } GL_State( GLS_DEFAULT ); } /* ===================== RB_ShowVertexColor Draw each triangle with the solid vertex colors ===================== */ static void RB_ShowVertexColor( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; const srfTriangles_t* tri; if( !r_showVertexColor.GetBool() ) { return; } globalImages->BindNull(); GL_State( GLS_DEPTHFUNC_LESS ); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } qglBegin( GL_TRIANGLES ); for( j = 0; j < tri->numIndexes; j++ ) { const idDrawVert* v; v = &tri->verts[tri->indexes[j]]; qglColor4ubv( v->color ); qglVertex3fv( v->xyz.ToFloatPtr() ); } qglEnd(); } GL_State( GLS_DEFAULT ); } /* ===================== RB_ShowNormals Debugging tool ===================== */ static void RB_ShowNormals( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; idVec3 end; const srfTriangles_t* tri; float size; bool showNumbers; idVec3 pos; if( r_showNormals.GetFloat() == 0.0f ) { return; } globalImages->BindNull(); if( !r_debugLineDepthTest.GetBool() ) { GL_State( GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS ); } else { GL_State( GLS_POLYMODE_LINE ); } size = r_showNormals.GetFloat(); if( size < 0.0f ) { size = -size; showNumbers = true; } else { showNumbers = false; } for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } qglBegin( GL_LINES ); for( j = 0; j < tri->numVerts; j++ ) { const idVec3 normal = tri->verts[j].GetNormal(); const idVec3 tangent = tri->verts[j].GetTangent(); const idVec3 bitangent = tri->verts[j].GetBiTangent(); GL_Color( 0, 0, 1 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, normal, end ); qglVertex3fv( end.ToFloatPtr() ); GL_Color( 1, 0, 0 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, tangent, end ); qglVertex3fv( end.ToFloatPtr() ); GL_Color( 0, 1, 0 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, bitangent, end ); qglVertex3fv( end.ToFloatPtr() ); } qglEnd(); } if( showNumbers ) { RB_SimpleWorldSetup(); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } for( j = 0; j < tri->numVerts; j++ ) { const idVec3 normal = tri->verts[j].GetNormal(); const idVec3 tangent = tri->verts[j].GetTangent(); R_LocalPointToGlobal( drawSurf->space->modelMatrix, tri->verts[j].xyz + tangent + normal * 0.2f, pos ); RB_DrawText( va( "%d", j ), pos, 0.01f, colorWhite, backEnd.viewDef->renderView.viewaxis, 1 ); } for( j = 0; j < tri->numIndexes; j += 3 ) { const idVec3 normal = tri->verts[ tri->indexes[ j + 0 ] ].GetNormal(); R_LocalPointToGlobal( drawSurf->space->modelMatrix, ( tri->verts[ tri->indexes[ j + 0 ] ].xyz + tri->verts[ tri->indexes[ j + 1 ] ].xyz + tri->verts[ tri->indexes[ j + 2 ] ].xyz ) * ( 1.0f / 3.0f ) + normal * 0.2f, pos ); RB_DrawText( va( "%d", j / 3 ), pos, 0.01f, colorCyan, backEnd.viewDef->renderView.viewaxis, 1 ); } } } } #if 0 // compiler warning /* ===================== RB_ShowNormals Debugging tool ===================== */ static void RB_AltShowNormals( drawSurf_t** drawSurfs, int numDrawSurfs ) { if( r_showNormals.GetFloat() == 0.0f ) { return; } globalImages->BindNull(); GL_State( GLS_DEPTHFUNC_ALWAYS ); for( int i = 0; i < numDrawSurfs; i++ ) { drawSurf_t* drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); const srfTriangles_t* tri = drawSurf->geo; qglBegin( GL_LINES ); for( int j = 0; j < tri->numIndexes; j += 3 ) { const idDrawVert* v[3] = { &tri->verts[tri->indexes[j + 0]], &tri->verts[tri->indexes[j + 1]], &tri->verts[tri->indexes[j + 2]] } const idPlane plane( v[0]->xyz, v[1]->xyz, v[2]->xyz ); // make the midpoint slightly above the triangle const idVec3 mid = ( v[0]->xyz + v[1]->xyz + v[2]->xyz ) * ( 1.0f / 3.0f ) + 0.1f * plane.Normal(); for( int k = 0; k < 3; k++ ) { const idVec3 pos = ( mid + v[k]->xyz * 3.0f ) * 0.25f; idVec3 end; GL_Color( 0, 0, 1 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->normal, end ); qglVertex3fv( end.ToFloatPtr() ); GL_Color( 1, 0, 0 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[0], end ); qglVertex3fv( end.ToFloatPtr() ); GL_Color( 0, 1, 0 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[1], end ); qglVertex3fv( end.ToFloatPtr() ); GL_Color( 1, 1, 1 ); qglVertex3fv( pos.ToFloatPtr() ); qglVertex3fv( v[k]->xyz.ToFloatPtr() ); } } qglEnd(); } } #endif /* ===================== RB_ShowTextureVectors Draw texture vectors in the center of each triangle ===================== */ static void RB_ShowTextureVectors( drawSurf_t** drawSurfs, int numDrawSurfs ) { if( r_showTextureVectors.GetFloat() == 0.0f ) { return; } GL_State( GLS_DEPTHFUNC_LESS ); globalImages->BindNull(); for( int i = 0; i < numDrawSurfs; i++ ) { drawSurf_t* drawSurf = drawSurfs[i]; const srfTriangles_t* tri = drawSurf->frontEndGeo; if( tri->verts == NULL ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); // draw non-shared edges in yellow qglBegin( GL_LINES ); for( int j = 0; j < tri->numIndexes; j += 3 ) { float d0[5], d1[5]; idVec3 temp; idVec3 tangents[2]; const idDrawVert* a = &tri->verts[tri->indexes[j + 0]]; const idDrawVert* b = &tri->verts[tri->indexes[j + 1]]; const idDrawVert* c = &tri->verts[tri->indexes[j + 2]]; const idPlane plane( a->xyz, b->xyz, c->xyz ); // make the midpoint slightly above the triangle const idVec3 mid = ( a->xyz + b->xyz + c->xyz ) * ( 1.0f / 3.0f ) + 0.1f * plane.Normal(); // calculate the texture vectors const idVec2 aST = a->GetTexCoord(); const idVec2 bST = b->GetTexCoord(); const idVec2 cST = c->GetTexCoord(); d0[0] = b->xyz[0] - a->xyz[0]; d0[1] = b->xyz[1] - a->xyz[1]; d0[2] = b->xyz[2] - a->xyz[2]; d0[3] = bST[0] - aST[0]; d0[4] = bST[1] - aST[1]; d1[0] = c->xyz[0] - a->xyz[0]; d1[1] = c->xyz[1] - a->xyz[1]; d1[2] = c->xyz[2] - a->xyz[2]; d1[3] = cST[0] - aST[0]; d1[4] = cST[1] - aST[1]; const float area = d0[3] * d1[4] - d0[4] * d1[3]; if( area == 0 ) { continue; } const float inva = 1.0f / area; temp[0] = ( d0[0] * d1[4] - d0[4] * d1[0] ) * inva; temp[1] = ( d0[1] * d1[4] - d0[4] * d1[1] ) * inva; temp[2] = ( d0[2] * d1[4] - d0[4] * d1[2] ) * inva; temp.Normalize(); tangents[0] = temp; temp[0] = ( d0[3] * d1[0] - d0[0] * d1[3] ) * inva; temp[1] = ( d0[3] * d1[1] - d0[1] * d1[3] ) * inva; temp[2] = ( d0[3] * d1[2] - d0[2] * d1[3] ) * inva; temp.Normalize(); tangents[1] = temp; // draw the tangents tangents[0] = mid + tangents[0] * r_showTextureVectors.GetFloat(); tangents[1] = mid + tangents[1] * r_showTextureVectors.GetFloat(); GL_Color( 1, 0, 0 ); qglVertex3fv( mid.ToFloatPtr() ); qglVertex3fv( tangents[0].ToFloatPtr() ); GL_Color( 0, 1, 0 ); qglVertex3fv( mid.ToFloatPtr() ); qglVertex3fv( tangents[1].ToFloatPtr() ); } qglEnd(); } } /* ===================== RB_ShowDominantTris Draw lines from each vertex to the dominant triangle center ===================== */ static void RB_ShowDominantTris( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t* drawSurf; const srfTriangles_t* tri; if( !r_showDominantTri.GetBool() ) { return; } GL_State( GLS_DEPTHFUNC_LESS ); GL_PolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_LINE ); globalImages->BindNull(); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->frontEndGeo; if( !tri->verts ) { continue; } if( !tri->dominantTris ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); GL_Color( 1, 1, 0 ); qglBegin( GL_LINES ); for( j = 0; j < tri->numVerts; j++ ) { const idDrawVert* a, *b, *c; idVec3 mid; // find the midpoint of the dominant tri a = &tri->verts[j]; b = &tri->verts[tri->dominantTris[j].v2]; c = &tri->verts[tri->dominantTris[j].v3]; mid = ( a->xyz + b->xyz + c->xyz ) * ( 1.0f / 3.0f ); qglVertex3fv( mid.ToFloatPtr() ); qglVertex3fv( a->xyz.ToFloatPtr() ); } qglEnd(); } qglDisable( GL_POLYGON_OFFSET_LINE ); } /* ===================== RB_ShowEdges Debugging tool ===================== */ static void RB_ShowEdges( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i, j, k, m, n, o; drawSurf_t* drawSurf; const srfTriangles_t* tri; const silEdge_t* edge; int danglePlane; if( !r_showEdges.GetBool() ) { return; } globalImages->BindNull(); GL_State( GLS_DEPTHFUNC_ALWAYS ); for( i = 0; i < numDrawSurfs; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->frontEndGeo; idDrawVert* ac = ( idDrawVert* )tri->verts; if( !ac ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); // draw non-shared edges in yellow GL_Color( 1, 1, 0 ); qglBegin( GL_LINES ); for( j = 0; j < tri->numIndexes; j += 3 ) { for( k = 0; k < 3; k++ ) { int l, i1, i2; l = ( k == 2 ) ? 0 : k + 1; i1 = tri->indexes[j + k]; i2 = tri->indexes[j + l]; // if these are used backwards, the edge is shared for( m = 0; m < tri->numIndexes; m += 3 ) { for( n = 0; n < 3; n++ ) { o = ( n == 2 ) ? 0 : n + 1; if( tri->indexes[m + n] == i2 && tri->indexes[m + o] == i1 ) { break; } } if( n != 3 ) { break; } } // if we didn't find a backwards listing, draw it in yellow if( m == tri->numIndexes ) { qglVertex3fv( ac[ i1 ].xyz.ToFloatPtr() ); qglVertex3fv( ac[ i2 ].xyz.ToFloatPtr() ); } } } qglEnd(); // draw dangling sil edges in red if( !tri->silEdges ) { continue; } // the plane number after all real planes // is the dangling edge danglePlane = tri->numIndexes / 3; GL_Color( 1, 0, 0 ); qglBegin( GL_LINES ); for( j = 0; j < tri->numSilEdges; j++ ) { edge = tri->silEdges + j; if( edge->p1 != danglePlane && edge->p2 != danglePlane ) { continue; } qglVertex3fv( ac[ edge->v1 ].xyz.ToFloatPtr() ); qglVertex3fv( ac[ edge->v2 ].xyz.ToFloatPtr() ); } qglEnd(); } } /* ============== RB_ShowLights Visualize all light volumes used in the current scene r_showLights 1 : just print volumes numbers, highlighting ones covering the view r_showLights 2 : also draw planes of each volume r_showLights 3 : also draw edges of each volume ============== */ static void RB_ShowLights() { if( !r_showLights.GetInteger() ) { return; } GL_State( GLS_DEFAULT ); // we use the 'vLight->invProjectMVPMatrix' qglMatrixMode( GL_PROJECTION ); qglLoadIdentity(); globalImages->BindNull(); renderProgManager.BindShader_Color(); GL_Cull( CT_TWO_SIDED ); common->Printf( "volumes: " ); // FIXME: not in back end! int count = 0; for( viewLight_t* vLight = backEnd.viewDef->viewLights; vLight != NULL; vLight = vLight->next ) { count++; // depth buffered planes if( r_showLights.GetInteger() >= 2 ) { GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHMASK ); GL_Color( 0.0f, 0.0f, 1.0f, 0.25f ); idRenderMatrix invProjectMVPMatrix; idRenderMatrix::Multiply( backEnd.viewDef->worldSpace.mvp, vLight->inverseBaseLightProject, invProjectMVPMatrix ); RB_SetMVP( invProjectMVPMatrix ); RB_DrawElementsWithCounters( &backEnd.zeroOneCubeSurface ); } // non-hidden lines if( r_showLights.GetInteger() >= 3 ) { GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_POLYMODE_LINE | GLS_DEPTHMASK ); GL_Color( 1.0f, 1.0f, 1.0f ); idRenderMatrix invProjectMVPMatrix; idRenderMatrix::Multiply( backEnd.viewDef->worldSpace.mvp, vLight->inverseBaseLightProject, invProjectMVPMatrix ); RB_SetMVP( invProjectMVPMatrix ); RB_DrawElementsWithCounters( &backEnd.zeroOneCubeSurface ); } common->Printf( "%i ", vLight->lightDef->index ); } common->Printf( " = %i total\n", count ); // set back the default projection matrix qglMatrixMode( GL_PROJECTION ); qglLoadMatrixf( backEnd.viewDef->projectionMatrix ); qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); } /* ===================== RB_ShowPortals Debugging tool, won't work correctly with SMP or when mirrors are present ===================== */ static void RB_ShowPortals() { if( !r_showPortals.GetBool() ) { return; } // all portals are expressed in world coordinates RB_SimpleWorldSetup(); globalImages->BindNull(); renderProgManager.BindShader_Color(); GL_State( GLS_DEPTHFUNC_ALWAYS ); ( ( idRenderWorldLocal* )backEnd.viewDef->renderWorld )->ShowPortals(); } /* ================ RB_ClearDebugText ================ */ void RB_ClearDebugText( int time ) { int i; int num; debugText_t* text; rb_debugTextTime = time; if( !time ) { // free up our strings text = rb_debugText; for( i = 0; i < MAX_DEBUG_TEXT; i++, text++ ) { text->text.Clear(); } rb_numDebugText = 0; return; } // copy any text that still needs to be drawn num = 0; text = rb_debugText; for( i = 0; i < rb_numDebugText; i++, text++ ) { if( text->lifeTime > time ) { if( num != i ) { rb_debugText[ num ] = *text; } num++; } } rb_numDebugText = num; } /* ================ RB_AddDebugText ================ */ void RB_AddDebugText( const char* text, const idVec3& origin, float scale, const idVec4& color, const idMat3& viewAxis, const int align, const int lifetime, const bool depthTest ) { debugText_t* debugText; if( rb_numDebugText < MAX_DEBUG_TEXT ) { debugText = &rb_debugText[ rb_numDebugText++ ]; debugText->text = text; debugText->origin = origin; debugText->scale = scale; debugText->color = color; debugText->viewAxis = viewAxis; debugText->align = align; debugText->lifeTime = rb_debugTextTime + lifetime; debugText->depthTest = depthTest; } } /* ================ RB_DrawTextLength returns the length of the given text ================ */ float RB_DrawTextLength( const char* text, float scale, int len ) { int i, num, index, charIndex; float spacing, textLen = 0.0f; if( text && *text ) { if( !len ) { len = strlen( text ); } for( i = 0; i < len; i++ ) { charIndex = text[i] - 32; if( charIndex < 0 || charIndex > NUM_SIMPLEX_CHARS ) { continue; } num = simplex[charIndex][0] * 2; spacing = simplex[charIndex][1]; index = 2; while( index - 2 < num ) { if( simplex[charIndex][index] < 0 ) { index++; continue; } index += 2; if( simplex[charIndex][index] < 0 ) { index++; continue; } } textLen += spacing * scale; } } return textLen; } /* ================ RB_DrawText oriented on the viewaxis align can be 0-left, 1-center (default), 2-right ================ */ static void RB_DrawText( const char* text, const idVec3& origin, float scale, const idVec4& color, const idMat3& viewAxis, const int align ) { renderProgManager.BindShader_Color(); int i, j, len, num, index, charIndex, line; float textLen = 1.0f, spacing = 1.0f; idVec3 org, p1, p2; if( text && *text ) { qglBegin( GL_LINES ); qglColor3fv( color.ToFloatPtr() ); if( text[0] == '\n' ) { line = 1; } else { line = 0; } len = strlen( text ); for( i = 0; i < len; i++ ) { if( i == 0 || text[i] == '\n' ) { org = origin - viewAxis[2] * ( line * 36.0f * scale ); if( align != 0 ) { for( j = 1; i + j <= len; j++ ) { if( i + j == len || text[i + j] == '\n' ) { textLen = RB_DrawTextLength( text + i, scale, j ); break; } } if( align == 2 ) { // right org += viewAxis[1] * textLen; } else { // center org += viewAxis[1] * ( textLen * 0.5f ); } } line++; } charIndex = text[i] - 32; if( charIndex < 0 || charIndex > NUM_SIMPLEX_CHARS ) { continue; } num = simplex[charIndex][0] * 2; spacing = simplex[charIndex][1]; index = 2; while( index - 2 < num ) { if( simplex[charIndex][index] < 0 ) { index++; continue; } p1 = org + scale * simplex[charIndex][index] * -viewAxis[1] + scale * simplex[charIndex][index + 1] * viewAxis[2]; index += 2; if( simplex[charIndex][index] < 0 ) { index++; continue; } p2 = org + scale * simplex[charIndex][index] * -viewAxis[1] + scale * simplex[charIndex][index + 1] * viewAxis[2]; qglVertex3fv( p1.ToFloatPtr() ); qglVertex3fv( p2.ToFloatPtr() ); } org -= viewAxis[1] * ( spacing * scale ); } qglEnd(); } } /* ================ RB_ShowDebugText ================ */ void RB_ShowDebugText() { int i; int width; debugText_t* text; if( !rb_numDebugText ) { return; } // all lines are expressed in world coordinates RB_SimpleWorldSetup(); globalImages->BindNull(); width = r_debugLineWidth.GetInteger(); if( width < 1 ) { width = 1; } else if( width > 10 ) { width = 10; } // draw lines qglLineWidth( width ); if( !r_debugLineDepthTest.GetBool() ) { GL_State( GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS ); } else { GL_State( GLS_POLYMODE_LINE ); } text = rb_debugText; for( i = 0; i < rb_numDebugText; i++, text++ ) { if( !text->depthTest ) { RB_DrawText( text->text, text->origin, text->scale, text->color, text->viewAxis, text->align ); } } if( !r_debugLineDepthTest.GetBool() ) { GL_State( GLS_POLYMODE_LINE ); } text = rb_debugText; for( i = 0; i < rb_numDebugText; i++, text++ ) { if( text->depthTest ) { RB_DrawText( text->text, text->origin, text->scale, text->color, text->viewAxis, text->align ); } } qglLineWidth( 1 ); } /* ================ RB_ClearDebugLines ================ */ void RB_ClearDebugLines( int time ) { int i; int num; debugLine_t* line; rb_debugLineTime = time; if( !time ) { rb_numDebugLines = 0; return; } // copy any lines that still need to be drawn num = 0; line = rb_debugLines; for( i = 0; i < rb_numDebugLines; i++, line++ ) { if( line->lifeTime > time ) { if( num != i ) { rb_debugLines[ num ] = *line; } num++; } } rb_numDebugLines = num; } /* ================ RB_AddDebugLine ================ */ void RB_AddDebugLine( const idVec4& color, const idVec3& start, const idVec3& end, const int lifeTime, const bool depthTest ) { debugLine_t* line; if( rb_numDebugLines < MAX_DEBUG_LINES ) { line = &rb_debugLines[ rb_numDebugLines++ ]; line->rgb = color; line->start = start; line->end = end; line->depthTest = depthTest; line->lifeTime = rb_debugLineTime + lifeTime; } } /* ================ RB_ShowDebugLines ================ */ void RB_ShowDebugLines() { int i; int width; debugLine_t* line; if( !rb_numDebugLines ) { return; } // all lines are expressed in world coordinates RB_SimpleWorldSetup(); globalImages->BindNull(); width = r_debugLineWidth.GetInteger(); if( width < 1 ) { width = 1; } else if( width > 10 ) { width = 10; } // draw lines qglLineWidth( width ); if( !r_debugLineDepthTest.GetBool() ) { GL_State( GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS ); } else { GL_State( GLS_POLYMODE_LINE ); } qglBegin( GL_LINES ); line = rb_debugLines; for( i = 0; i < rb_numDebugLines; i++, line++ ) { if( !line->depthTest ) { qglColor3fv( line->rgb.ToFloatPtr() ); qglVertex3fv( line->start.ToFloatPtr() ); qglVertex3fv( line->end.ToFloatPtr() ); } } qglEnd(); if( !r_debugLineDepthTest.GetBool() ) { GL_State( GLS_POLYMODE_LINE ); } qglBegin( GL_LINES ); line = rb_debugLines; for( i = 0; i < rb_numDebugLines; i++, line++ ) { if( line->depthTest ) { qglColor4fv( line->rgb.ToFloatPtr() ); qglVertex3fv( line->start.ToFloatPtr() ); qglVertex3fv( line->end.ToFloatPtr() ); } } qglEnd(); qglLineWidth( 1 ); GL_State( GLS_DEFAULT ); } /* ================ RB_ClearDebugPolygons ================ */ void RB_ClearDebugPolygons( int time ) { int i; int num; debugPolygon_t* poly; rb_debugPolygonTime = time; if( !time ) { rb_numDebugPolygons = 0; return; } // copy any polygons that still need to be drawn num = 0; poly = rb_debugPolygons; for( i = 0; i < rb_numDebugPolygons; i++, poly++ ) { if( poly->lifeTime > time ) { if( num != i ) { rb_debugPolygons[ num ] = *poly; } num++; } } rb_numDebugPolygons = num; } /* ================ RB_AddDebugPolygon ================ */ void RB_AddDebugPolygon( const idVec4& color, const idWinding& winding, const int lifeTime, const bool depthTest ) { debugPolygon_t* poly; if( rb_numDebugPolygons < MAX_DEBUG_POLYGONS ) { poly = &rb_debugPolygons[ rb_numDebugPolygons++ ]; poly->rgb = color; poly->winding = winding; poly->depthTest = depthTest; poly->lifeTime = rb_debugPolygonTime + lifeTime; } } /* ================ RB_ShowDebugPolygons ================ */ void RB_ShowDebugPolygons() { int i, j; debugPolygon_t* poly; if( !rb_numDebugPolygons ) { return; } // all lines are expressed in world coordinates RB_SimpleWorldSetup(); globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); if( r_debugPolygonFilled.GetBool() ) { GL_State( GLS_POLYGON_OFFSET | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHMASK ); GL_PolygonOffset( -1, -2 ); } else { GL_State( GLS_POLYGON_OFFSET | GLS_POLYMODE_LINE ); GL_PolygonOffset( -1, -2 ); } poly = rb_debugPolygons; for( i = 0; i < rb_numDebugPolygons; i++, poly++ ) { // if ( !poly->depthTest ) { qglColor4fv( poly->rgb.ToFloatPtr() ); qglBegin( GL_POLYGON ); for( j = 0; j < poly->winding.GetNumPoints(); j++ ) { qglVertex3fv( poly->winding[j].ToFloatPtr() ); } qglEnd(); // } } GL_State( GLS_DEFAULT ); if( r_debugPolygonFilled.GetBool() ) { qglDisable( GL_POLYGON_OFFSET_FILL ); } else { qglDisable( GL_POLYGON_OFFSET_LINE ); } GL_State( GLS_DEFAULT ); } /* ================ RB_ShowCenterOfProjection ================ */ void RB_ShowCenterOfProjection() { if( !r_showCenterOfProjection.GetBool() ) { return; } const int w = backEnd.viewDef->scissor.GetWidth(); const int h = backEnd.viewDef->scissor.GetHeight(); qglClearColor( 1, 0, 0, 1 ); for( float f = 0.0f ; f <= 1.0f ; f += 0.125f ) { qglScissor( w * f - 1 , 0, 3, h ); qglClear( GL_COLOR_BUFFER_BIT ); qglScissor( 0, h * f - 1 , w, 3 ); qglClear( GL_COLOR_BUFFER_BIT ); } qglClearColor( 0, 1, 0, 1 ); float f = 0.5f; qglScissor( w * f - 1 , 0, 3, h ); qglClear( GL_COLOR_BUFFER_BIT ); qglScissor( 0, h * f - 1 , w, 3 ); qglClear( GL_COLOR_BUFFER_BIT ); qglScissor( 0, 0, w, h ); } /* ================ RB_ShowLines Draw exact pixel lines to check pixel center sampling ================ */ void RB_ShowLines() { if( !r_showLines.GetBool() ) { return; } glEnable( GL_SCISSOR_TEST ); if( backEnd.viewDef->renderView.viewEyeBuffer == 0 ) { glClearColor( 1, 0, 0, 1 ); } else if( backEnd.viewDef->renderView.viewEyeBuffer == 1 ) { glClearColor( 0, 1, 0, 1 ); } else { glClearColor( 0, 0, 1, 1 ); } const int start = ( r_showLines.GetInteger() > 2 ); // 1,3 = horizontal, 2,4 = vertical if( r_showLines.GetInteger() == 1 || r_showLines.GetInteger() == 3 ) { for( int i = start ; i < tr.GetHeight() ; i += 2 ) { glScissor( 0, i, tr.GetWidth(), 1 ); glClear( GL_COLOR_BUFFER_BIT ); } } else { for( int i = start ; i < tr.GetWidth() ; i += 2 ) { glScissor( i, 0, 1, tr.GetHeight() ); glClear( GL_COLOR_BUFFER_BIT ); } } } /* ================ RB_TestGamma ================ */ #define G_WIDTH 512 #define G_HEIGHT 512 #define BAR_HEIGHT 64 void RB_TestGamma() { byte image[G_HEIGHT][G_WIDTH][4]; int i, j; int c, comp; int v, dither; int mask, y; if( r_testGamma.GetInteger() <= 0 ) { return; } v = r_testGamma.GetInteger(); if( v <= 1 || v >= 196 ) { v = 128; } memset( image, 0, sizeof( image ) ); for( mask = 0; mask < 8; mask++ ) { y = mask * BAR_HEIGHT; for( c = 0; c < 4; c++ ) { v = c * 64 + 32; // solid color for( i = 0; i < BAR_HEIGHT / 2; i++ ) { for( j = 0; j < G_WIDTH / 4; j++ ) { for( comp = 0; comp < 3; comp++ ) { if( mask & ( 1 << comp ) ) { image[y + i][c * G_WIDTH / 4 + j][comp] = v; } } } // dithered color for( j = 0; j < G_WIDTH / 4; j++ ) { if( ( i ^ j ) & 1 ) { dither = c * 64; } else { dither = c * 64 + 63; } for( comp = 0; comp < 3; comp++ ) { if( mask & ( 1 << comp ) ) { image[y + BAR_HEIGHT / 2 + i][c * G_WIDTH / 4 + j][comp] = dither; } } } } } } // draw geometrically increasing steps in the bottom row y = 0 * BAR_HEIGHT; float scale = 1; for( c = 0; c < 4; c++ ) { v = ( int )( 64 * scale ); if( v < 0 ) { v = 0; } else if( v > 255 ) { v = 255; } scale = scale * 1.5; for( i = 0; i < BAR_HEIGHT; i++ ) { for( j = 0; j < G_WIDTH / 4; j++ ) { image[y + i][c * G_WIDTH / 4 + j][0] = v; image[y + i][c * G_WIDTH / 4 + j][1] = v; image[y + i][c * G_WIDTH / 4 + j][2] = v; } } } qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1 ); qglPushMatrix(); qglLoadIdentity(); qglDisable( GL_TEXTURE_2D ); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0.01f, 0.01f ); qglDrawPixels( G_WIDTH, G_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, image ); qglPopMatrix(); qglEnable( GL_TEXTURE_2D ); qglMatrixMode( GL_MODELVIEW ); } /* ================== RB_TestGammaBias ================== */ static void RB_TestGammaBias() { byte image[G_HEIGHT][G_WIDTH][4]; if( r_testGammaBias.GetInteger() <= 0 ) { return; } int y = 0; for( int bias = -40; bias < 40; bias += 10, y += BAR_HEIGHT ) { float scale = 1; for( int c = 0; c < 4; c++ ) { int v = ( int )( 64 * scale + bias ); scale = scale * 1.5; if( v < 0 ) { v = 0; } else if( v > 255 ) { v = 255; } for( int i = 0; i < BAR_HEIGHT; i++ ) { for( int j = 0; j < G_WIDTH / 4; j++ ) { image[y + i][c * G_WIDTH / 4 + j][0] = v; image[y + i][c * G_WIDTH / 4 + j][1] = v; image[y + i][c * G_WIDTH / 4 + j][2] = v; } } } } qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1 ); qglPushMatrix(); qglLoadIdentity(); qglDisable( GL_TEXTURE_2D ); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0.01f, 0.01f ); qglDrawPixels( G_WIDTH, G_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, image ); qglPopMatrix(); qglEnable( GL_TEXTURE_2D ); qglMatrixMode( GL_MODELVIEW ); } /* ================ RB_TestImage Display a single image over most of the screen ================ */ void RB_TestImage() { idImage* image = NULL; idImage* imageCr = NULL; idImage* imageCb = NULL; int max; float w, h; image = tr.testImage; if( !image ) { return; } if( tr.testVideo ) { cinData_t cin; cin = tr.testVideo->ImageForTime( backEnd.viewDef->renderView.time[1] - tr.testVideoStartTime ); if( cin.imageY != NULL ) { image = cin.imageY; imageCr = cin.imageCr; imageCb = cin.imageCb; } else { tr.testImage = NULL; return; } w = 0.25; h = 0.25; } else { max = image->GetUploadWidth() > image->GetUploadHeight() ? image->GetUploadWidth() : image->GetUploadHeight(); w = 0.25 * image->GetUploadWidth() / max; h = 0.25 * image->GetUploadHeight() / max; w *= ( float )renderSystem->GetHeight() / renderSystem->GetWidth(); } // Set State GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); // Set Parms float texS[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; float texT[4] = { 0.0f, 1.0f, 0.0f, 0.0f }; renderProgManager.SetRenderParm( RENDERPARM_TEXTUREMATRIX_S, texS ); renderProgManager.SetRenderParm( RENDERPARM_TEXTUREMATRIX_T, texT ); float texGenEnabled[4] = { 0, 0, 0, 0 }; renderProgManager.SetRenderParm( RENDERPARM_TEXGEN_0_ENABLED, texGenEnabled ); // not really necessary but just for clarity const float screenWidth = 1.0f; const float screenHeight = 1.0f; const float halfScreenWidth = screenWidth * 0.5f; const float halfScreenHeight = screenHeight * 0.5f; float scale[16] = { 0 }; scale[0] = w; // scale scale[5] = h; // scale scale[12] = halfScreenWidth - ( halfScreenWidth * w ); // translate scale[13] = halfScreenHeight - ( halfScreenHeight * h ); // translate scale[10] = 1.0f; scale[15] = 1.0f; float ortho[16] = { 0 }; ortho[0] = 2.0f / screenWidth; ortho[5] = -2.0f / screenHeight; ortho[10] = -2.0f; ortho[12] = -1.0f; ortho[13] = 1.0f; ortho[14] = -1.0f; ortho[15] = 1.0f; float finalOrtho[16]; R_MatrixMultiply( scale, ortho, finalOrtho ); float projMatrixTranspose[16]; R_MatrixTranspose( finalOrtho, projMatrixTranspose ); renderProgManager.SetRenderParms( RENDERPARM_MVPMATRIX_X, projMatrixTranspose, 4 ); qglMatrixMode( GL_PROJECTION ); qglLoadMatrixf( finalOrtho ); qglMatrixMode( GL_MODELVIEW ); qglLoadIdentity(); // Set Color GL_Color( 1, 1, 1, 1 ); // Bind the Texture if( ( imageCr != NULL ) && ( imageCb != NULL ) ) { GL_SelectTexture( 0 ); image->Bind(); GL_SelectTexture( 1 ); imageCr->Bind(); GL_SelectTexture( 2 ); imageCb->Bind(); renderProgManager.BindShader_Bink(); } else { GL_SelectTexture( 0 ); image->Bind(); // Set Shader renderProgManager.BindShader_Texture(); } // Draw! RB_DrawElementsWithCounters( &backEnd.testImageSurface ); } /* ================= RB_DrawExpandedTriangles ================= */ void RB_DrawExpandedTriangles( const srfTriangles_t* tri, const float radius, const idVec3& vieworg ) { int i, j, k; idVec3 dir[6], normal, point; for( i = 0; i < tri->numIndexes; i += 3 ) { idVec3 p[3] = { tri->verts[ tri->indexes[ i + 0 ] ].xyz, tri->verts[ tri->indexes[ i + 1 ] ].xyz, tri->verts[ tri->indexes[ i + 2 ] ].xyz }; dir[0] = p[0] - p[1]; dir[1] = p[1] - p[2]; dir[2] = p[2] - p[0]; normal = dir[0].Cross( dir[1] ); if( normal * p[0] < normal * vieworg ) { continue; } dir[0] = normal.Cross( dir[0] ); dir[1] = normal.Cross( dir[1] ); dir[2] = normal.Cross( dir[2] ); dir[0].Normalize(); dir[1].Normalize(); dir[2].Normalize(); qglBegin( GL_LINE_LOOP ); for( j = 0; j < 3; j++ ) { k = ( j + 1 ) % 3; dir[4] = ( dir[j] + dir[k] ) * 0.5f; dir[4].Normalize(); dir[3] = ( dir[j] + dir[4] ) * 0.5f; dir[3].Normalize(); dir[5] = ( dir[4] + dir[k] ) * 0.5f; dir[5].Normalize(); point = p[k] + dir[j] * radius; qglVertex3f( point[0], point[1], point[2] ); point = p[k] + dir[3] * radius; qglVertex3f( point[0], point[1], point[2] ); point = p[k] + dir[4] * radius; qglVertex3f( point[0], point[1], point[2] ); point = p[k] + dir[5] * radius; qglVertex3f( point[0], point[1], point[2] ); point = p[k] + dir[k] * radius; qglVertex3f( point[0], point[1], point[2] ); } qglEnd(); } } /* ================ RB_ShowTrace Debug visualization FIXME: not thread safe! ================ */ void RB_ShowTrace( drawSurf_t** drawSurfs, int numDrawSurfs ) { int i; const srfTriangles_t* tri; const drawSurf_t* surf; idVec3 start, end; idVec3 localStart, localEnd; localTrace_t hit; float radius; if( r_showTrace.GetInteger() == 0 ) { return; } if( r_showTrace.GetInteger() == 2 ) { radius = 5.0f; } else { radius = 0.0f; } // determine the points of the trace start = backEnd.viewDef->renderView.vieworg; end = start + 4000 * backEnd.viewDef->renderView.viewaxis[0]; // check and draw the surfaces globalImages->whiteImage->Bind(); // find how many are ambient for( i = 0; i < numDrawSurfs; i++ ) { surf = drawSurfs[i]; tri = surf->frontEndGeo; if( tri == NULL || tri->verts == NULL ) { continue; } // transform the points into local space R_GlobalPointToLocal( surf->space->modelMatrix, start, localStart ); R_GlobalPointToLocal( surf->space->modelMatrix, end, localEnd ); // check the bounding box if( !tri->bounds.Expand( radius ).LineIntersection( localStart, localEnd ) ) { continue; } qglLoadMatrixf( surf->space->modelViewMatrix ); // highlight the surface GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); GL_Color( 1, 0, 0, 0.25 ); RB_DrawElementsWithCounters( surf ); // draw the bounding box GL_State( GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1, 1 ); RB_DrawBounds( tri->bounds ); if( radius != 0.0f ) { // draw the expanded triangles GL_Color( 0.5f, 0.5f, 1.0f, 1.0f ); RB_DrawExpandedTriangles( tri, radius, localStart ); } // check the exact surfaces hit = R_LocalTrace( localStart, localEnd, radius, tri ); if( hit.fraction < 1.0 ) { GL_Color( 1, 1, 1, 1 ); RB_DrawBounds( idBounds( hit.point ).Expand( 1 ) ); } } } /* ================= RB_RenderDebugTools ================= */ void RB_RenderDebugTools( drawSurf_t** drawSurfs, int numDrawSurfs ) { // don't do much if this was a 2D rendering if( !backEnd.viewDef->viewEntitys ) { RB_TestImage(); RB_ShowLines(); return; } renderLog.OpenMainBlock( MRB_DRAW_DEBUG_TOOLS ); RENDERLOG_PRINTF( "---------- RB_RenderDebugTools ----------\n" ); GL_State( GLS_DEFAULT ); GL_Scissor( backEnd.viewDef->viewport.x1 + backEnd.viewDef->scissor.x1, backEnd.viewDef->viewport.y1 + backEnd.viewDef->scissor.y1, backEnd.viewDef->scissor.x2 + 1 - backEnd.viewDef->scissor.x1, backEnd.viewDef->scissor.y2 + 1 - backEnd.viewDef->scissor.y1 ); backEnd.currentScissor = backEnd.viewDef->scissor; RB_ShowLightCount(); RB_ShowTexturePolarity( drawSurfs, numDrawSurfs ); RB_ShowTangentSpace( drawSurfs, numDrawSurfs ); RB_ShowVertexColor( drawSurfs, numDrawSurfs ); RB_ShowTris( drawSurfs, numDrawSurfs ); RB_ShowUnsmoothedTangents( drawSurfs, numDrawSurfs ); RB_ShowSurfaceInfo( drawSurfs, numDrawSurfs ); RB_ShowEdges( drawSurfs, numDrawSurfs ); RB_ShowNormals( drawSurfs, numDrawSurfs ); RB_ShowViewEntitys( backEnd.viewDef->viewEntitys ); RB_ShowLights(); RB_ShowTextureVectors( drawSurfs, numDrawSurfs ); RB_ShowDominantTris( drawSurfs, numDrawSurfs ); if( r_testGamma.GetInteger() > 0 ) // test here so stack check isn't so damn slow on debug builds { RB_TestGamma(); } if( r_testGammaBias.GetInteger() > 0 ) { RB_TestGammaBias(); } RB_TestImage(); RB_ShowPortals(); RB_ShowSilhouette(); RB_ShowDepthBuffer(); RB_ShowIntensity(); RB_ShowCenterOfProjection(); RB_ShowLines(); RB_ShowDebugLines(); RB_ShowDebugText(); RB_ShowDebugPolygons(); RB_ShowTrace( drawSurfs, numDrawSurfs ); renderLog.CloseMainBlock(); } /* ================= RB_ShutdownDebugTools ================= */ void RB_ShutdownDebugTools() { for( int i = 0; i < MAX_DEBUG_POLYGONS; i++ ) { rb_debugPolygons[i].winding.Clear(); } }