doom3-bfg/neo/renderer/tr_backend_rendertools.cpp
Robert Beckebans 277964f074 Because I can :)
- Implemented soft shadows using PCF hardware shadow mapping

  The implementation uses sampler2DArrayShadow and PCF which usually
  requires Direct3D 10.1 however it is in the OpenGL 3.2 core so it should
  be widely supported.
  All 3 light types are supported which means parallel lights (sun) use
  scene independent cascaded shadow mapping.
  The implementation is very fast with single taps (400 fps average per
  scene on a GTX 660 ti OC) however I defaulted it to 16 taps so the shadows look
  really good which should you give stable 100 fps on todays hardware.

  The shadow filtering algorithm is based on Carmack's research which was
  released in the original Doom 3 GPL release draw_exp.cpp.

- Changed interaction shaders to use Half-Lambert lighting like in HL2 to
  make the game less dark

- Fixed some of the renderer debugging/development tools like r_showTris
2014-05-10 14:40:01 +02:00

3161 lines
69 KiB
C++

/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2013-2014 Robert Beckebans
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;
}
glBegin( GL_LINE_LOOP );
glVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] );
glVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] );
glVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] );
glVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] );
glEnd();
glBegin( GL_LINE_LOOP );
glVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] );
glVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] );
glVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] );
glVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] );
glEnd();
glBegin( GL_LINES );
glVertex3f( bounds[0][0], bounds[0][1], bounds[0][2] );
glVertex3f( bounds[0][0], bounds[0][1], bounds[1][2] );
glVertex3f( bounds[0][0], bounds[1][1], bounds[0][2] );
glVertex3f( bounds[0][0], bounds[1][1], bounds[1][2] );
glVertex3f( bounds[1][0], bounds[0][1], bounds[0][2] );
glVertex3f( bounds[1][0], bounds[0][1], bounds[1][2] );
glVertex3f( bounds[1][0], bounds[1][1], bounds[0][2] );
glVertex3f( bounds[1][0], bounds[1][1], bounds[1][2] );
glEnd();
}
/*
================
RB_SimpleSurfaceSetup
================
*/
static void RB_SimpleSurfaceSetup( const drawSurf_t* drawSurf )
{
// change the matrix if needed
if( drawSurf->space != backEnd.currentSpace )
{
// RB begin
RB_SetMVP( drawSurf->space->mvp );
//qglLoadMatrixf( drawSurf->space->modelViewMatrix );
// RB end
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;
// RB begin
//qglLoadMatrixf( backEnd.viewDef->worldSpace.modelViewMatrix );
RB_SetMVP( backEnd.viewDef->worldSpace.mvp );
// RB end
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()
{
glPushMatrix();
glPushAttrib( GL_ALL_ATTRIB_BITS );
glLoadIdentity();
glDisable( GL_TEXTURE_2D );
glDisable( GL_DEPTH_TEST );
glDisable( GL_CULL_FACE );
glDisable( GL_SCISSOR_TEST );
glBegin( GL_POLYGON );
glVertex3f( -20, -20, -10 );
glVertex3f( 20, -20, -10 );
glVertex3f( 20, 20, -10 );
glVertex3f( -20, 20, -10 );
glEnd();
glPopAttrib();
glPopMatrix();
}
/*
====================
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 );
glReadPixels( 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 );
glReadPixels( 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 idVec3 colors[8] =
{
idVec3( 0, 0, 0 ),
idVec3( 1, 0, 0 ),
idVec3( 0, 1, 0 ),
idVec3( 0, 0, 1 ),
idVec3( 0, 1, 1 ),
idVec3( 1, 0, 1 ),
idVec3( 1, 1, 0 ),
idVec3( 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
glStencilOp( GL_KEEP, GL_KEEP, GL_KEEP );
for( i = 0; i < 6; i++ )
{
GL_Color( colors[i] );
renderProgManager.BindShader_Color();
glStencilFunc( GL_EQUAL, i, 255 );
RB_PolygonClear();
}
glStencilFunc( 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 );
glReadPixels( 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
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
GL_State( GLS_DEPTHFUNC_ALWAYS );
glPushMatrix();
glLoadIdentity();
glOrtho( 0, 1, 0, 1, -1, 1 );
glRasterPos2f( 0, 0 );
glPopMatrix();
GL_Color( 1, 1, 1 );
globalImages->BindNull();
glMatrixMode( GL_MODELVIEW );
glDrawPixels( 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;
}
glPushMatrix();
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
glPushMatrix();
glLoadIdentity();
glOrtho( 0, 1, 0, 1, -1, 1 );
glRasterPos2f( 0, 0 );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
glPopMatrix();
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 );
glReadPixels( 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
glDrawPixels( 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;
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( matrix );
glMatrixMode( GL_MODELVIEW );
}
/*
===============
RB_EnterModelDepthHack
===============
*/
static void RB_EnterModelDepthHack( float depth )
{
float matrix[16];
memcpy( matrix, backEnd.viewDef->projectionMatrix, sizeof( matrix ) );
matrix[14] -= depth;
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( matrix );
glMatrixMode( GL_MODELVIEW );
}
/*
===============
RB_LeaveDepthHack
===============
*/
static void RB_LeaveDepthHack()
{
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( backEnd.viewDef->projectionMatrix );
glMatrixMode( 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 );
// RB begin
#if 1
if( drawSurf->space != backEnd.currentSpace )
{
backEnd.currentSpace = drawSurf->space;
RB_SetMVP( drawSurf->space->mvp );
}
#else
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 );
}
}
#endif
if( drawSurf->jointCache )
{
renderProgManager.BindShader_ColorSkinned();
}
else
{
renderProgManager.BindShader_Color();
}
// RB end
// 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 );
// RB begin
/*if( drawSurf->space != NULL && ( drawSurf->space->weaponDepthHack || drawSurf->space->modelDepthHack != 0.0f ) )
{
RB_LeaveDepthHack();
}*/
// RB end
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();
// RB begin
renderProgManager.BindShader_Color();
// RB end
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
glBindBufferARB( GL_ARRAY_BUFFER_ARB, ( GLintptrARB )vertexBuffer.GetAPIObject() );
GLintptrARB vertOffset = vertexBuffer.GetOffset();
// RB end
glVertexPointer( 3, GL_FLOAT, sizeof( idShadowVert ), ( void* )vertOffset );
glBegin( 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 )
{
glArrayElement( i1 );
glArrayElement( i2 );
}
else if( ( i1 & 1 ) + ( i3 & 1 ) == 0 )
{
glArrayElement( i1 );
glArrayElement( i3 );
}
}
}
glEnd();
}
}
}
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;
}
idVec4 color( 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 );
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();
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;
//glLoadMatrixf( 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 == NULL || tri->verts == NULL )
{
continue;
}
RB_SimpleSurfaceSetup( drawSurf );
glBegin( 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 );
}
glVertex3fv( a->xyz.ToFloatPtr() );
glVertex3fv( b->xyz.ToFloatPtr() );
glVertex3fv( c->xyz.ToFloatPtr() );
}
glEnd();
}
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;
if( tri == NULL || tri->verts == NULL )
{
continue;
}
glBegin( 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];
glVertex3fv( a->xyz.ToFloatPtr() );
glVertex3fv( b->xyz.ToFloatPtr() );
glVertex3fv( c->xyz.ToFloatPtr() );
}
glEnd();
}
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 == NULL || tri->verts == NULL )
{
continue;
}
glBegin( 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 );
}
glVertex3fv( v->xyz.ToFloatPtr() );
}
glEnd();
}
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();
// RB begin
renderProgManager.BindShader_VertexColor();
GL_State( GLS_DEPTHFUNC_LESS );
for( i = 0; i < numDrawSurfs; i++ )
{
drawSurf = drawSurfs[i];
RB_SimpleSurfaceSetup( drawSurf );
tri = drawSurf->frontEndGeo;
if( tri == NULL || tri->verts == NULL )
{
continue;
}
renderProgManager.CommitUniforms();
glBegin( GL_TRIANGLES );
for( j = 0; j < tri->numIndexes; j++ )
{
const idDrawVert* v;
v = &tri->verts[tri->indexes[j]];
glColor4ubv( v->color );
glVertex3fv( v->xyz.ToFloatPtr() );
}
glEnd();
}
// RB end
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 == NULL || tri->verts == NULL )
{
continue;
}
// RB begin
renderProgManager.BindShader_VertexColor();
glBegin( 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();
glColor3f( 0, 0, 1 );
glVertex3fv( tri->verts[j].xyz.ToFloatPtr() );
VectorMA( tri->verts[j].xyz, size, normal, end );
glVertex3fv( end.ToFloatPtr() );
glColor3f( 1, 0, 0 );
glVertex3fv( tri->verts[j].xyz.ToFloatPtr() );
VectorMA( tri->verts[j].xyz, size, tangent, end );
glVertex3fv( end.ToFloatPtr() );
glColor3f( 0, 1, 0 );
glVertex3fv( tri->verts[j].xyz.ToFloatPtr() );
VectorMA( tri->verts[j].xyz, size, bitangent, end );
glVertex3fv( end.ToFloatPtr() );
}
glEnd();
// RB end
}
if( showNumbers )
{
RB_SimpleWorldSetup();
for( i = 0; i < numDrawSurfs; i++ )
{
drawSurf = drawSurfs[i];
tri = drawSurf->frontEndGeo;
if( tri == NULL || tri->verts == NULL )
{
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;
glBegin( 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 );
glVertex3fv( pos.ToFloatPtr() );
VectorMA( pos, r_showNormals.GetFloat(), v[k]->normal, end );
glVertex3fv( end.ToFloatPtr() );
GL_Color( 1, 0, 0 );
glVertex3fv( pos.ToFloatPtr() );
VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[0], end );
glVertex3fv( end.ToFloatPtr() );
GL_Color( 0, 1, 0 );
glVertex3fv( pos.ToFloatPtr() );
VectorMA( pos, r_showNormals.GetFloat(), v[k]->tangents[1], end );
glVertex3fv( end.ToFloatPtr() );
GL_Color( 1, 1, 1 );
glVertex3fv( pos.ToFloatPtr() );
glVertex3fv( v[k]->xyz.ToFloatPtr() );
}
}
glEnd();
}
}
#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 == NULL || tri->verts == NULL )
{
continue;
}
RB_SimpleSurfaceSetup( drawSurf );
// draw non-shared edges in yellow
glBegin( 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 );
glVertex3fv( mid.ToFloatPtr() );
glVertex3fv( tangents[0].ToFloatPtr() );
GL_Color( 0, 1, 0 );
glVertex3fv( mid.ToFloatPtr() );
glVertex3fv( tangents[1].ToFloatPtr() );
}
glEnd();
}
}
/*
=====================
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 );
glEnable( GL_POLYGON_OFFSET_LINE );
globalImages->BindNull();
for( i = 0; i < numDrawSurfs; i++ )
{
drawSurf = drawSurfs[i];
tri = drawSurf->frontEndGeo;
if( tri == NULL || tri->verts == NULL )
{
continue;
}
if( !tri->dominantTris )
{
continue;
}
RB_SimpleSurfaceSetup( drawSurf );
GL_Color( 1, 1, 0 );
glBegin( 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 );
glVertex3fv( mid.ToFloatPtr() );
glVertex3fv( a->xyz.ToFloatPtr() );
}
glEnd();
}
glDisable( 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 );
glBegin( 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 )
{
glVertex3fv( ac[ i1 ].xyz.ToFloatPtr() );
glVertex3fv( ac[ i2 ].xyz.ToFloatPtr() );
}
}
}
glEnd();
// 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 );
glBegin( GL_LINES );
for( j = 0; j < tri->numSilEdges; j++ )
{
edge = tri->silEdges + j;
if( edge->p1 != danglePlane && edge->p2 != danglePlane )
{
continue;
}
glVertex3fv( ac[ edge->v1 ].xyz.ToFloatPtr() );
glVertex3fv( ac[ edge->v2 ].xyz.ToFloatPtr() );
}
glEnd();
}
}
/*
==============
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'
// glMatrixMode( GL_PROJECTION );
// glLoadIdentity();
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 );
// RB: show different light types
if( vLight->parallel )
{
GL_Color( 1.0f, 0.0f, 0.0f, 0.25f );
}
else if( vLight->pointLight )
{
GL_Color( 0.0f, 0.0f, 1.0f, 0.25f );
}
else
{
GL_Color( 0.0f, 1.0f, 0.0f, 0.25f );
}
// RB end
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
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( backEnd.viewDef->projectionMatrix );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
}
/*
=====================
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();
// RB begin
GL_Color( color[0], color[1], color[2], 1 /*color[3]*/ );
renderProgManager.CommitUniforms();
// RB end
int i, j, len, num, index, charIndex, line;
float textLen = 1.0f, spacing = 1.0f;
idVec3 org, p1, p2;
if( text && *text )
{
glBegin( GL_LINES );
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];
glVertex3fv( p1.ToFloatPtr() );
glVertex3fv( p2.ToFloatPtr() );
}
org -= viewAxis[1] * ( spacing * scale );
}
glEnd();
}
}
/*
================
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
glLineWidth( 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 );
}
}
glLineWidth( 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();
// RB begin
renderProgManager.BindShader_VertexColor();
renderProgManager.CommitUniforms();
// RB end
globalImages->BindNull();
width = r_debugLineWidth.GetInteger();
if( width < 1 )
{
width = 1;
}
else if( width > 10 )
{
width = 10;
}
// draw lines
glLineWidth( width );
if( !r_debugLineDepthTest.GetBool() )
{
GL_State( GLS_POLYMODE_LINE | GLS_DEPTHFUNC_ALWAYS );
}
else
{
GL_State( GLS_POLYMODE_LINE );
}
glBegin( GL_LINES );
line = rb_debugLines;
for( i = 0; i < rb_numDebugLines; i++, line++ )
{
if( !line->depthTest )
{
glColor3fv( line->rgb.ToFloatPtr() );
glVertex3fv( line->start.ToFloatPtr() );
glVertex3fv( line->end.ToFloatPtr() );
}
}
glEnd();
if( !r_debugLineDepthTest.GetBool() )
{
GL_State( GLS_POLYMODE_LINE );
}
glBegin( GL_LINES );
line = rb_debugLines;
for( i = 0; i < rb_numDebugLines; i++, line++ )
{
if( line->depthTest )
{
glColor4fv( line->rgb.ToFloatPtr() );
glVertex3fv( line->start.ToFloatPtr() );
glVertex3fv( line->end.ToFloatPtr() );
}
}
glEnd();
glLineWidth( 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();
// RB begin
renderProgManager.BindShader_VertexColor();
renderProgManager.CommitUniforms();
// RB end
globalImages->BindNull();
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 ) {
glColor4fv( poly->rgb.ToFloatPtr() );
glBegin( GL_POLYGON );
for( j = 0; j < poly->winding.GetNumPoints(); j++ )
{
glVertex3fv( poly->winding[j].ToFloatPtr() );
}
glEnd();
// }
}
GL_State( GLS_DEFAULT );
if( r_debugPolygonFilled.GetBool() )
{
glDisable( GL_POLYGON_OFFSET_FILL );
}
else
{
glDisable( 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();
glClearColor( 1, 0, 0, 1 );
for( float f = 0.0f ; f <= 1.0f ; f += 0.125f )
{
glScissor( w * f - 1 , 0, 3, h );
glClear( GL_COLOR_BUFFER_BIT );
glScissor( 0, h * f - 1 , w, 3 );
glClear( GL_COLOR_BUFFER_BIT );
}
glClearColor( 0, 1, 0, 1 );
float f = 0.5f;
glScissor( w * f - 1 , 0, 3, h );
glClear( GL_COLOR_BUFFER_BIT );
glScissor( 0, h * f - 1 , w, 3 );
glClear( GL_COLOR_BUFFER_BIT );
glScissor( 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;
}
}
}
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
GL_State( GLS_DEPTHFUNC_ALWAYS );
GL_Color( 1, 1, 1 );
glPushMatrix();
glLoadIdentity();
glDisable( GL_TEXTURE_2D );
glOrtho( 0, 1, 0, 1, -1, 1 );
glRasterPos2f( 0.01f, 0.01f );
glDrawPixels( G_WIDTH, G_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, image );
glPopMatrix();
glEnable( GL_TEXTURE_2D );
glMatrixMode( 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;
}
}
}
}
glLoadIdentity();
glMatrixMode( GL_PROJECTION );
GL_State( GLS_DEPTHFUNC_ALWAYS );
GL_Color( 1, 1, 1 );
glPushMatrix();
glLoadIdentity();
glDisable( GL_TEXTURE_2D );
glOrtho( 0, 1, 0, 1, -1, 1 );
glRasterPos2f( 0.01f, 0.01f );
glDrawPixels( G_WIDTH, G_HEIGHT, GL_RGBA, GL_UNSIGNED_BYTE, image );
glPopMatrix();
glEnable( GL_TEXTURE_2D );
glMatrixMode( 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 );
// glMatrixMode( GL_PROJECTION );
// glLoadMatrixf( finalOrtho );
// glMatrixMode( GL_MODELVIEW );
// glLoadIdentity();
// 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 begin
void RB_ShowShadowMaps()
{
idImage* image = NULL;
int max;
float w, h;
if( !r_showShadowMaps.GetBool() )
return;
image = globalImages->shadowImage;
if( !image )
{
return;
}
// 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 );
for( int i = 0; i < ( r_shadowMapSplits.GetInteger() + 1 ); i++ )
{
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();
// 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 * w * 2.1f * i ); // 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 );
float screenCorrectionParm[4];
screenCorrectionParm[0] = i;
screenCorrectionParm[1] = 0.0f;
screenCorrectionParm[2] = 0.0f;
screenCorrectionParm[3] = 1.0f;
renderProgManager.SetRenderParm( RENDERPARM_SCREENCORRECTIONFACTOR, screenCorrectionParm ); // rpScreenCorrectionFactor
// glMatrixMode( GL_PROJECTION );
// glLoadMatrixf( finalOrtho );
// glMatrixMode( GL_MODELVIEW );
// glLoadIdentity();
// Set Color
GL_Color( 1, 1, 1, 1 );
GL_SelectTexture( 0 );
image->Bind();
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_NONE );
renderProgManager.BindShader_DebugShadowMap();
RB_DrawElementsWithCounters( &backEnd.testImageSurface );
}
glTexParameteri( GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE );
}
// RB end
/*
=================
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();
glBegin( 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;
glVertex3f( point[0], point[1], point[2] );
point = p[k] + dir[3] * radius;
glVertex3f( point[0], point[1], point[2] );
point = p[k] + dir[4] * radius;
glVertex3f( point[0], point[1], point[2] );
point = p[k] + dir[5] * radius;
glVertex3f( point[0], point[1], point[2] );
point = p[k] + dir[k] * radius;
glVertex3f( point[0], point[1], point[2] );
}
glEnd();
}
}
/*
================
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;
}
glLoadMatrixf( 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 begin
RB_ShowShadowMaps();
// RB end
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();
}
}