/* =========================================================================== 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 "../idlib/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; } qglBindBufferARB( GL_ARRAY_BUFFER_ARB, (GLuint)vertexBuffer.GetAPIObject() ); int vertOffset = vertexBuffer.GetOffset(); 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(); } }