mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-30 16:11:11 +00:00
736ec20d4d
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.
2388 lines
55 KiB
C++
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();
|
|
}
|
|
}
|