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