dhewm3/neo/renderer/tr_rendertools.cpp
dhewg 736ec20d4d Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2011-12-19 23:21:47 +01:00

2388 lines
55 KiB
C++

/*
===========================================================================
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 <http://www.gnu.org/licenses/>.
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<drawSurf_t *>(surf)->material = material;
}
newDrawSurfs[i] = const_cast<drawSurf_t *>(surf);
}
for ( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next ) {
for ( surf = vLight->localInteractions; surf; surf = surf->nextOnLight ) {
const_cast<drawSurf_t *>(surf)->material = material;
newDrawSurfs[i++] = const_cast<drawSurf_t *>(surf);
}
for ( surf = vLight->globalInteractions; surf; surf = surf->nextOnLight ) {
const_cast<drawSurf_t *>(surf)->material = material;
newDrawSurfs[i++] = const_cast<drawSurf_t *>(surf);
}
vLight->localInteractions = NULL;
vLight->globalInteractions = NULL;
}
switch( r_showOverDraw.GetInteger() ) {
case 1: // geometry overdraw
const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = newDrawSurfs;
const_cast<viewDef_t *>(backEnd.viewDef)->numDrawSurfs = numDrawSurfs;
break;
case 2: // light interaction overdraw
const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = &newDrawSurfs[numDrawSurfs];
const_cast<viewDef_t *>(backEnd.viewDef)->numDrawSurfs = interactions;
break;
case 3: // geometry + light interaction overdraw
const_cast<viewDef_t *>(backEnd.viewDef)->drawSurfs = newDrawSurfs;
const_cast<viewDef_t *>(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 ) {
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 );
qglColor3f( 1, 1, 1 );
globalImages->BindNull();
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
for ( i = 0 ; i < glConfig.vidWidth * glConfig.vidHeight ; 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( glConfig.vidWidth, glConfig.vidHeight, 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
=================
*/
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;
}
RB_LogComment( "---------- RB_RenderDebugTools ----------\n" );
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();
}
}