/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 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 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 Source Code. If not, see . 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. 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. =========================================================================== */ #include "sys/platform.h" #include "renderer/simplex.h" // line font definition #include "renderer/VertexCache.h" #include "renderer/Cinematic.h" #include "renderer/RenderWorld_local.h" #include "renderer/tr_local.h" #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 ); /* ================ 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 ================ */ 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 ( r_useScissor.GetBool() && !backEnd.currentScissor.Equals( drawSurf->scissorRect ) ) { backEnd.currentScissor = drawSurf->scissorRect; qglScissor( 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 ); } } /* ================ RB_SimpleWorldSetup ================ */ void RB_SimpleWorldSetup( void ) { backEnd.currentSpace = &backEnd.viewDef->worldSpace; qglLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix ); backEnd.currentScissor = backEnd.viewDef->scissor; qglScissor( 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 ); } /* ================= 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( void ) { 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( void ) { GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ZERO | GLS_DEPTHMASK | GLS_DEPTHFUNC_ALWAYS ); qglColor3f( 1, 1, 1 ); RB_PolygonClear(); } /* =================== RB_ScanStencilBuffer Debugging tool to see what values are in the stencil buffer =================== */ void RB_ScanStencilBuffer( void ) { int counts[256]; int i; byte *stencilReadback; memset( counts, 0, sizeof( counts ) ); stencilReadback = (byte *)R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; 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 =================== */ void RB_CountStencilBuffer( void ) { int count; int i; byte *stencilReadback; stencilReadback = (byte *)R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); count = 0; for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; 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/(glConfig.vidWidth * glConfig.vidHeight) ); } /* =================== 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( void ) { 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) qglClearColor( 1, 1, 1, 1 ); qglDisable( GL_SCISSOR_TEST ); qglClear( GL_COLOR_BUFFER_BIT ); // now draw color for each stencil value qglStencilOp( GL_KEEP, GL_KEEP, GL_KEEP ); for ( i = 0 ; i < 6 ; i++ ) { qglColor3fv( colors[i] ); qglStencilFunc( GL_EQUAL, i, 255 ); RB_PolygonClear(); } qglStencilFunc( GL_ALWAYS, 0, 255 ); } //====================================================================== /* ================== RB_ShowOverdraw ================== */ void RB_ShowOverdraw( void ) { 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++; } } drawSurf_t **newDrawSurfs = (drawSurf_t **)R_FrameAlloc( numDrawSurfs + interactions * sizeof( newDrawSurfs[0] ) ); 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 =================== */ void RB_ShowIntensity( void ) { byte *colorReadback; int i, j, c; if ( !r_showIntensity.GetBool() ) { return; } colorReadback = (byte *)R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight * 4 ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGBA, GL_UNSIGNED_BYTE, colorReadback ); c = glConfig.vidWidth * glConfig.vidHeight * 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(); qglColor3f( 1, 1, 1 ); globalImages->BindNull(); qglMatrixMode( GL_MODELVIEW ); qglDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_RGBA , GL_UNSIGNED_BYTE, colorReadback ); R_StaticFree( colorReadback ); } /* =================== RB_ShowDepthBuffer Draw the depth buffer as colors =================== */ void RB_ShowDepthBuffer( void ) { if ( !r_showDepth.GetBool() ) { return; } qglPushMatrix(); qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0, 0 ); GL_State( GLS_DEPTHFUNC_ALWAYS ); qglColor3f( 1, 1, 1 ); bool haveDepthCapture = r_enableDepthCapture.GetInteger() == 1 || (r_enableDepthCapture.GetInteger() == -1 && r_useSoftParticles.GetBool()); if ( haveDepthCapture ) { //GL_SelectTexture( 0 ); //qglEnable(GL_TEXTURE_2D); globalImages->currentDepthImage->Bind(); const float x=0, y=0, w=1, h=1; // debug values: //const float x = 0.1, y = 0.1, w = 0.8, h = 0.8; //qglColor4f( 0.0f, 0.0f, 0.5f, 1.0f ); const float tx=0, ty=0; // the actual texturesize of currentDepthImage is the next bigger power of two (POT), // so the normalized width/height of the part of it we actually wanna show is the following const float tw = float(glConfig.vidWidth) / float(globalImages->currentDepthImage->uploadWidth); const float th = float(glConfig.vidHeight) / float(globalImages->currentDepthImage->uploadHeight); qglBegin( GL_QUADS ); qglTexCoord2f(tx, ty); qglVertex2f( x, y ); // ( 0,0 ); qglTexCoord2f(tx, ty+th); qglVertex2f( x, y+h ); // ( 0,1 ); qglTexCoord2f(tx+tw, ty+th); qglVertex2f( x+w, y+h ); // ( 1,1 ); qglTexCoord2f(tx+tw, ty); qglVertex2f( x+w, y ); // ( 1,0 ); qglEnd(); // TODO: probably a shader transforming this to something viewable qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); qglPopMatrix(); } else { qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); qglPopMatrix(); globalImages->BindNull(); void* depthReadback = R_StaticAlloc( glConfig.vidWidth * glConfig.vidHeight*4 ); memset( depthReadback, 0, glConfig.vidWidth * glConfig.vidHeight*4 ); qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_DEPTH_COMPONENT , GL_FLOAT, depthReadback ); #if 0 // the following looks better, but is different from the !r_skipDepthCapture.GetBool() case above // (which draws the captured depth buffer unaltered, unless we add a shader) for ( int i = 0, n=glConfig.vidWidth * glConfig.vidHeight; i < n ; i++ ) { float& px = ((float *)depthReadback)[i]; float d = px; // the following calculation is based on how the TDM soft particle shader translates the depth value to doom units // 0.9995 is practically infinite distance, clamping to 0.9994 clamps to max 30k doom units, // which is more than enough (and prevents a potential division by 0 below) d = (d < 0.9994f) ? d : 0.9994f; d = d - 0.9995f; // d is now negative, between -0.0001 and -0.9995 d = 1.0f / d; // d *= -3.0f; // this would translate d to distance in doom units, doing it together with the next step d *= (-3.0f / 3000.0f); // now 3000 units is 1.0, i.e. completely white (=> more details for closer distances) px = d; } #endif qglDrawPixels( glConfig.vidWidth, glConfig.vidHeight, GL_LUMINANCE, GL_FLOAT, 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 ================= */ void RB_ShowLightCount( void ) { int i; const drawSurf_t *surf; const viewLight_t *vLight; if ( !r_showLightCount.GetBool() ) { return; } GL_State( GLS_DEPTHFUNC_EQUAL ); RB_SimpleWorldSetup(); qglClearStencil( 0 ); qglClear( GL_STENCIL_BUFFER_BIT ); qglEnable( GL_STENCIL_TEST ); // optionally count everything through walls if ( r_showLightCount.GetInteger() >= 2 ) { qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); } else { qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); } qglStencilFunc( GL_ALWAYS, 1, 255 ); 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 ); if ( !surf->geo->ambientCache ) { continue; } const idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache ); qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), &ac->xyz ); RB_DrawElementsWithCounters( surf->geo ); } } } // display the results R_ColorByStencilBuffer(); if ( r_showLightCount.GetInteger() > 2 ) { RB_CountStencilBuffer(); } } /* ================= RB_ShowSilhouette Blacks out all edges, then adds color for each edge that a shadow plane extends from, allowing you to see doubled edges ================= */ void RB_ShowSilhouette( void ) { int i; const drawSurf_t *surf; const viewLight_t *vLight; if ( !r_showSilhouette.GetBool() ) { return; } // // clear all triangle edges to black // qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); qglDisable( GL_STENCIL_TEST ); qglColor3f( 0, 0, 0 ); GL_State( GLS_POLYMODE_LINE ); GL_Cull( CT_TWO_SIDED ); qglDisable( GL_DEPTH_TEST ); RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs, RB_T_RenderTriangleSurface ); // // now blend in edges that cast silhouettes // RB_SimpleWorldSetup(); qglColor3f( 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->geo; qglVertexPointer( 3, GL_FLOAT, sizeof( shadowCache_t ), vertexCache.Position( tri->shadowCache ) ); 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(); } } } qglEnable( GL_DEPTH_TEST ); GL_State( GLS_DEFAULT ); qglColor3f( 1,1,1 ); GL_Cull( CT_FRONT_SIDED ); } /* ================= RB_ShowShadowCount This is a debugging tool that will draw only the shadow volumes and count up the total fill usage ================= */ static void RB_ShowShadowCount( void ) { int i; const drawSurf_t *surf; const viewLight_t *vLight; if ( !r_showShadowCount.GetBool() ) { return; } GL_State( GLS_DEFAULT ); qglClearStencil( 0 ); qglClear( GL_STENCIL_BUFFER_BIT ); qglEnable( GL_STENCIL_TEST ); qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); qglStencilFunc( GL_ALWAYS, 1, 255 ); globalImages->defaultImage->Bind(); // draw both sides GL_Cull( CT_TWO_SIDED ); 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->geo; if ( !tri->shadowCache ) { continue; } if ( r_showShadowCount.GetInteger() == 3 ) { // only show turboshadows if ( tri->numShadowIndexesNoCaps != tri->numIndexes ) { continue; } } if ( r_showShadowCount.GetInteger() == 4 ) { // only show static shadows if ( tri->numShadowIndexesNoCaps == tri->numIndexes ) { continue; } } shadowCache_t *cache = (shadowCache_t *)vertexCache.Position( tri->shadowCache ); qglVertexPointer( 4, GL_FLOAT, sizeof( *cache ), &cache->xyz ); RB_DrawElementsWithCounters( tri ); } } } // display the results R_ColorByStencilBuffer(); if ( r_showShadowCount.GetInteger() == 2 ) { common->Printf( "all shadows " ); } else if ( r_showShadowCount.GetInteger() == 3 ) { common->Printf( "turboShadows " ); } else if ( r_showShadowCount.GetInteger() == 4 ) { common->Printf( "static shadows " ); } if ( r_showShadowCount.GetInteger() >= 2 ) { RB_CountStencilBuffer(); } GL_Cull( CT_FRONT_SIDED ); } /* =============== RB_T_RenderTriangleSurfaceAsLines =============== */ void RB_T_RenderTriangleSurfaceAsLines( const drawSurf_t *surf ) { const srfTriangles_t *tri = surf->geo; if ( !tri->verts ) { return; } qglBegin( GL_LINES ); for ( int i = 0 ; i < tri->numIndexes ; i+= 3 ) { for ( int j = 0 ; j < 3 ; j++ ) { int k = ( j + 1 ) % 3; qglVertex3fv( tri->verts[ tri->silIndexes[i+j] ].xyz.ToFloatPtr() ); qglVertex3fv( tri->verts[ tri->silIndexes[i+k] ].xyz.ToFloatPtr() ); } } qglEnd(); } /* ===================== RB_ShowTris Debugging tool ===================== */ static void RB_ShowTris( drawSurf_t **drawSurfs, int numDrawSurfs ) { idVec3 end; if ( !r_showTris.GetInteger() ) { return; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); qglDisable( GL_STENCIL_TEST ); qglColor3f( 1, 1, 1 ); GL_State( GLS_POLYMODE_LINE ); switch ( r_showTris.GetInteger() ) { case 1: // only draw visible ones qglPolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_LINE ); break; default: case 2: // draw all front facing GL_Cull( CT_FRONT_SIDED ); qglDisable( GL_DEPTH_TEST ); break; case 3: // draw all GL_Cull( CT_TWO_SIDED ); qglDisable( GL_DEPTH_TEST ); break; } RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_T_RenderTriangleSurface ); qglEnable( GL_DEPTH_TEST ); qglDisable( GL_POLYGON_OFFSET_LINE ); qglDepthRange( 0, 1 ); GL_State( GLS_DEFAULT ); 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; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); qglDisable( GL_STENCIL_TEST ); qglColor3f( 1, 1, 1 ); GL_State( GLS_POLYMODE_LINE ); qglPolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_LINE ); 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 ); qglEnable( GL_DEPTH_TEST ); qglDisable( GL_POLYGON_OFFSET_LINE ); qglDepthRange( 0, 1 ); GL_State( GLS_DEFAULT ); GL_Cull( CT_FRONT_SIDED ); } /* ===================== 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 ( ; vModels ; vModels = vModels->next ) { common->Printf( "%i ", vModels->entityDef->index ); } common->Printf( "\n" ); return; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_TEXTURE_2D ); qglDisable( GL_STENCIL_TEST ); qglColor3f( 1, 1, 1 ); GL_State( GLS_POLYMODE_LINE ); GL_Cull( CT_TWO_SIDED ); qglDisable( GL_DEPTH_TEST ); qglDisable( GL_SCISSOR_TEST ); for ( ; vModels ; vModels = vModels->next ) { idBounds b; qglLoadMatrixf( vModels->modelViewMatrix ); if ( !vModels->entityDef ) { continue; } // draw the reference bounds in yellow qglColor3f( 1, 1, 0 ); RB_DrawBounds( vModels->entityDef->referenceBounds ); // draw the model bounds in white qglColor3f( 1, 1, 1 ); idRenderModel *model = R_EntityDefDynamicModel( vModels->entityDef ); if ( !model ) { continue; // particles won't instantiate without a current view } b = model->Bounds( &vModels->entityDef->parms ); RB_DrawBounds( b ); } qglEnable( GL_DEPTH_TEST ); qglDisable( GL_POLYGON_OFFSET_LINE ); qglDepthRange( 0, 1 ); GL_State( GLS_DEFAULT ); GL_Cull( CT_FRONT_SIDED ); } /* ===================== 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; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); qglColor3f( 1, 1, 1 ); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->geo; 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]; // VectorSubtract( b->xyz, a->xyz, d0 ); d0[3] = b->st[0] - a->st[0]; d0[4] = b->st[1] - a->st[1]; // VectorSubtract( c->xyz, a->xyz, d1 ); d1[3] = c->st[0] - a->st[0]; d1[4] = c->st[1] - a->st[1]; area = d0[3] * d1[4] - d0[4] * d1[3]; if ( idMath::Fabs( area ) < 0.0001 ) { qglColor4f( 0, 0, 1, 0.5 ); } else if ( area < 0 ) { qglColor4f( 1, 0, 0, 0.5 ); } else { qglColor4f( 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; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); qglColor4f( 0, 1, 0, 0.5 ); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; if ( !drawSurf->material->UseUnsmoothedTangents() ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->geo; 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; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); 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->geo; 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 ) { qglColor4f( 0.5 + 0.5 * v->tangents[0][0], 0.5 + 0.5 * v->tangents[0][1], 0.5 + 0.5 * v->tangents[0][2], 0.5 ); } else if ( r_showTangentSpace.GetInteger() == 2 ) { qglColor4f( 0.5 + 0.5 * v->tangents[1][0], 0.5 + 0.5 * v->tangents[1][1], 0.5 + 0.5 * v->tangents[1][2], 0.5 ); } else { qglColor4f( 0.5 + 0.5 * v->normal[0], 0.5 + 0.5 * v->normal[1], 0.5 + 0.5 * v->normal[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; } qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); GL_State( GLS_DEPTHFUNC_LESS ); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->geo; 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; } GL_State( GLS_POLYMODE_LINE ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); if ( !r_debugLineDepthTest.GetBool() ) { qglDisable( GL_DEPTH_TEST ); } else { qglEnable( GL_DEPTH_TEST ); } 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->geo; if ( !tri->verts ) { continue; } qglBegin( GL_LINES ); for ( j = 0 ; j < tri->numVerts ; j++ ) { qglColor3f( 0, 0, 1 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, tri->verts[j].normal, end ); qglVertex3fv( end.ToFloatPtr() ); qglColor3f( 1, 0, 0 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, tri->verts[j].tangents[0], end ); qglVertex3fv( end.ToFloatPtr() ); qglColor3f( 0, 1, 0 ); qglVertex3fv( tri->verts[j].xyz.ToFloatPtr() ); VectorMA( tri->verts[j].xyz, size, tri->verts[j].tangents[1], end ); qglVertex3fv( end.ToFloatPtr() ); } qglEnd(); } if ( showNumbers ) { RB_SimpleWorldSetup(); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->geo; if ( !tri->verts ) { continue; } for ( j = 0 ; j < tri->numVerts ; j++ ) { R_LocalPointToGlobal( drawSurf->space->modelMatrix, tri->verts[j].xyz + tri->verts[j].tangents[0] + tri->verts[j].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 ) { 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 ) + tri->verts[ tri->indexes[ j + 0 ] ].normal * 0.2f, pos ); RB_DrawText( va( "%d", j / 3 ), pos, 0.01f, colorCyan, backEnd.viewDef->renderView.viewaxis, 1 ); } } } qglEnable( GL_STENCIL_TEST ); } /* ===================== RB_ShowNormals Debugging tool ===================== */ #if 0 static void RB_AltShowNormals( drawSurf_t **drawSurfs, int numDrawSurfs ) { int i, j, k; drawSurf_t *drawSurf; idVec3 end; const srfTriangles_t *tri; if ( r_showNormals.GetFloat() == 0.0f ) { return; } GL_State( GLS_DEFAULT ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); qglDisable( GL_DEPTH_TEST ); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; RB_SimpleSurfaceSetup( drawSurf ); tri = drawSurf->geo; qglBegin( GL_LINES ); for ( j = 0 ; j < tri->numIndexes ; j += 3 ) { const idDrawVert *v[3]; idVec3 mid; v[0] = &tri->verts[tri->indexes[j+0]]; v[1] = &tri->verts[tri->indexes[j+1]]; v[2] = &tri->verts[tri->indexes[j+2]]; // make the midpoint slightly above the triangle mid = ( v[0]->xyz + v[1]->xyz + v[2]->xyz ) * ( 1.0f / 3.0f ); mid += 0.1f * tri->facePlanes[ j / 3 ].Normal(); for ( k = 0 ; k < 3 ; k++ ) { idVec3 pos; pos = ( mid + v[k]->xyz * 3.0f ) * 0.25f; qglColor3f( 0, 0, 1 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->normal, end ); qglVertex3fv( end.ToFloatPtr() ); qglColor3f( 1, 0, 0 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[0], end ); qglVertex3fv( end.ToFloatPtr() ); qglColor3f( 0, 1, 0 ); qglVertex3fv( pos.ToFloatPtr() ); VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[1], end ); qglVertex3fv( end.ToFloatPtr() ); qglColor3f( 1, 1, 1 ); qglVertex3fv( pos.ToFloatPtr() ); qglVertex3fv( v[k]->xyz.ToFloatPtr() ); } } qglEnd(); } qglEnable( GL_DEPTH_TEST ); qglEnable( GL_STENCIL_TEST ); } #endif /* ===================== RB_ShowTextureVectors Draw texture vectors in the center of each triangle ===================== */ static void RB_ShowTextureVectors( drawSurf_t **drawSurfs, int numDrawSurfs ) { int i, j; drawSurf_t *drawSurf; const srfTriangles_t *tri; if ( r_showTextureVectors.GetFloat() == 0.0f ) { return; } GL_State( GLS_DEPTHFUNC_LESS ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->geo; if ( !tri->verts ) { continue; } if ( !tri->facePlanes ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); // draw non-shared edges in yellow qglBegin( GL_LINES ); for ( j = 0 ; j < tri->numIndexes ; j+= 3 ) { const idDrawVert *a, *b, *c; float area, inva; idVec3 temp; float d0[5], d1[5]; idVec3 mid; idVec3 tangents[2]; a = &tri->verts[tri->indexes[j+0]]; b = &tri->verts[tri->indexes[j+1]]; c = &tri->verts[tri->indexes[j+2]]; // make the midpoint slightly above the triangle mid = ( a->xyz + b->xyz + c->xyz ) * ( 1.0f / 3.0f ); mid += 0.1f * tri->facePlanes[ j / 3 ].Normal(); // calculate the texture vectors VectorSubtract( b->xyz, a->xyz, d0 ); d0[3] = b->st[0] - a->st[0]; d0[4] = b->st[1] - a->st[1]; VectorSubtract( c->xyz, a->xyz, d1 ); d1[3] = c->st[0] - a->st[0]; d1[4] = c->st[1] - a->st[1]; area = d0[3] * d1[4] - d0[4] * d1[3]; if ( area == 0 ) { continue; } inva = 1.0 / 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(); qglColor3f( 1, 0, 0 ); qglVertex3fv( mid.ToFloatPtr() ); qglVertex3fv( tangents[0].ToFloatPtr() ); qglColor3f( 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 ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); qglPolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_LINE ); globalImages->BindNull(); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->geo; if ( !tri->verts ) { continue; } if ( !tri->dominantTris ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); qglColor3f( 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; } GL_State( GLS_DEFAULT ); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_DEPTH_TEST ); for ( i = 0 ; i < numDrawSurfs ; i++ ) { drawSurf = drawSurfs[i]; tri = drawSurf->geo; idDrawVert *ac = (idDrawVert *)tri->verts; if ( !ac ) { continue; } RB_SimpleSurfaceSetup( drawSurf ); // draw non-shared edges in yellow qglColor3f( 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; qglColor3f( 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(); } qglEnable( GL_DEPTH_TEST ); } /* ============== 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 ============== */ void RB_ShowLights( void ) { const idRenderLightLocal *light; int count; srfTriangles_t *tri; viewLight_t *vLight; if ( !r_showLights.GetInteger() ) { return; } // all volumes are expressed in world coordinates RB_SimpleWorldSetup(); qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); globalImages->BindNull(); qglDisable( GL_STENCIL_TEST ); GL_Cull( CT_TWO_SIDED ); qglDisable( GL_DEPTH_TEST ); common->Printf( "volumes: " ); // FIXME: not in back end! count = 0; for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) { light = vLight->lightDef; count++; tri = light->frustumTris; // depth buffered planes if ( r_showLights.GetInteger() >= 2 ) { GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHMASK ); qglColor4f( 0, 0, 1, 0.25 ); qglEnable( GL_DEPTH_TEST ); RB_RenderTriangleSurface( tri ); } // non-hidden lines if ( r_showLights.GetInteger() >= 3 ) { GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK ); qglDisable( GL_DEPTH_TEST ); qglColor3f( 1, 1, 1 ); RB_RenderTriangleSurface( tri ); } int index; index = backEnd.viewDef->renderWorld->lightDefs.FindIndex( vLight->lightDef ); if ( vLight->viewInsideLight ) { // view is in this volume common->Printf( "[%i] ", index ); } else { common->Printf( "%i ", index ); } } qglEnable( GL_DEPTH_TEST ); qglDisable( GL_POLYGON_OFFSET_LINE ); qglDepthRange( 0, 1 ); GL_State( GLS_DEFAULT ); GL_Cull( CT_FRONT_SIDED ); common->Printf( " = %i total\n", count ); } /* ===================== RB_ShowPortals Debugging tool, won't work correctly with SMP or when mirrors are present ===================== */ void RB_ShowPortals( void ) { if ( !r_showPortals.GetBool() ) { return; } // all portals are expressed in world coordinates RB_SimpleWorldSetup(); globalImages->BindNull(); qglDisable( GL_DEPTH_TEST ); GL_State( GLS_DEFAULT ); ((idRenderWorldLocal *)backEnd.viewDef->renderWorld)->ShowPortals(); qglEnable( GL_DEPTH_TEST ); } /* ================ 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 ) { int i, j, len, num, index, charIndex, line; float textLen = 0.0f, spacing; idVec3 org, p1, p2; if ( text && *text ) { qglBegin( GL_LINES ); qglColor3fv( color.ToFloatPtr() ); if ( text[0] == '\n' ) { line = 1; } else { line = 0; } org.Zero(); 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( void ) { 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 GL_State( GLS_POLYMODE_LINE ); qglLineWidth( width ); if ( !r_debugLineDepthTest.GetBool() ) { qglDisable( GL_DEPTH_TEST ); } 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() ) { qglEnable( GL_DEPTH_TEST ); } 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 ); GL_State( GLS_DEFAULT ); } /* ================ 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( void ) { 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 GL_State( GLS_POLYMODE_LINE );//| GLS_DEPTHMASK ); //| GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); qglLineWidth( width ); if ( !r_debugLineDepthTest.GetBool() ) { qglDisable( GL_DEPTH_TEST ); } 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() ) { qglEnable( GL_DEPTH_TEST ); } 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( void ) { 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 ); qglDisable( GL_STENCIL_TEST ); qglEnable( GL_DEPTH_TEST ); if ( r_debugPolygonFilled.GetBool() ) { GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHMASK ); qglPolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_FILL ); } else { GL_State( GLS_POLYMODE_LINE ); qglPolygonOffset( -1, -2 ); qglEnable( GL_POLYGON_OFFSET_LINE ); } 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 ); } qglDepthRange( 0, 1 ); GL_State( GLS_DEFAULT ); } /* ================ RB_TestGamma ================ */ #define G_WIDTH 512 #define G_HEIGHT 512 #define BAR_HEIGHT 64 void RB_TestGamma( void ) { 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 ); qglColor3f( 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( void ) { 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 ); qglColor3f( 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( void ) { idImage *image; int max; float w, h; image = tr.testImage; if ( !image ) { return; } if ( tr.testVideo ) { cinData_t cin; cin = tr.testVideo->ImageForTime( (int)(1000 * ( backEnd.viewDef->floatTime - tr.testVideoStartTime ) ) ); if ( cin.image ) { image->UploadScratch( cin.image, cin.imageWidth, cin.imageHeight ); } else { tr.testImage = NULL; return; } w = 0.25; h = 0.25; } else { max = image->uploadWidth > image->uploadHeight ? image->uploadWidth : image->uploadHeight; w = 0.25 * image->uploadWidth / max; h = 0.25 * image->uploadHeight / max; w *= (float)glConfig.vidHeight / glConfig.vidWidth; } qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS | GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); qglColor3f( 1, 1, 1 ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); tr.testImage->Bind(); qglBegin( GL_QUADS ); qglTexCoord2f( 0, 1 ); qglVertex2f( 0.5 - w, 0 ); qglTexCoord2f( 0, 0 ); qglVertex2f( 0.5 - w, h*2 ); qglTexCoord2f( 1, 0 ); qglVertex2f( 0.5 + w, h*2 ); qglTexCoord2f( 1, 1 ); qglVertex2f( 0.5 + w, 0 ); qglEnd(); qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); } /* ================= RB_RenderDebugTools ================= */ void RB_RenderDebugTools( drawSurf_t **drawSurfs, int numDrawSurfs ) { // don't do anything if this was a 2D rendering if ( !backEnd.viewDef->viewEntitys ) { return; } GL_State( GLS_DEFAULT ); backEnd.currentScissor = backEnd.viewDef->scissor; qglScissor( 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 ); RB_ShowLightCount(); RB_ShowShadowCount(); 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_ShowDebugLines(); RB_ShowDebugText(); RB_ShowDebugPolygons(); RB_ShowTrace( drawSurfs, numDrawSurfs ); } /* ================= RB_ShutdownDebugTools ================= */ void RB_ShutdownDebugTools( void ) { for ( int i = 0; i < MAX_DEBUG_POLYGONS; i++ ) { rb_debugPolygons[i].winding.Clear(); } }