Code: Select all
/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "../idlib/precompiled.h"
#include "tr_local.h"
/*
===========================================================================
DEFAULT GLSL SHADER
===========================================================================
*/
#define GLSL_VERSION_ATTRIBS \
"#version 130\n"
#define GLSL_INPUT_ATTRIBS \
"in vec4 attrTexCoords;\n" \
"in vec3 attrTangents0;\n" \
"in vec3 attrTangents1;\n" \
"in vec3 attrNormal;\n" \
"mat3x3 u_lightMatrix = mat3x3 (attrTangents0, attrTangents1, attrNormal);\n\n"
#define GLSL_UNIFORMS \
"uniform vec4 u_light_origin;\n" \
"uniform vec4 u_view_origin;\n" \
"uniform vec4 u_color_modulate;\n" \
"uniform vec4 u_color_add;\n" \
"uniform mat2x4 u_diffMatrix;\n" \
"uniform mat2x4 u_bumpMatrix;\n" \
"uniform mat2x4 u_specMatrix;\n" \
"uniform mat4x4 u_projMatrix;\n" \
"uniform mat4x4 u_fallMatrix;\n" \
"uniform sampler2D bumpImage;\n" \
"uniform sampler2D lightFalloffImage;\n" \
"uniform sampler2D lightProjectImage;\n" \
"uniform sampler2D diffuseImage;\n" \
"uniform sampler2D specularImage;\n" \
"uniform vec4 u_constant_diffuse;\n" \
"uniform vec4 u_constant_specular;\n\n"
#define GLSL_VARYINGS \
"varying vec2 diffCoords;\n" \
"varying vec2 bumpCoords;\n" \
"varying vec2 specCoords;\n" \
"varying vec4 projCoords;\n" \
"varying vec4 fallCoords;\n" \
"varying vec3 lightDir;\n" \
"varying vec3 halfAngle;\n" \
"varying vec4 Color;\n"
// these are our GLSL interaction shaders
#define interaction_vs \
GLSL_VERSION_ATTRIBS \
GLSL_INPUT_ATTRIBS \
GLSL_UNIFORMS \
GLSL_VARYINGS \
"void main ()\n" \
"{\n" \
" // we must use ftransform as Doom 3 needs invariant position\n" \
" gl_Position = ftransform ();\n" \
"\n" \
" diffCoords = attrTexCoords * u_diffMatrix;\n" \
" bumpCoords = attrTexCoords * u_bumpMatrix;\n" \
" specCoords = attrTexCoords * u_specMatrix;\n" \
"\n" \
" projCoords = gl_Vertex * u_projMatrix;\n" \
" fallCoords = gl_Vertex * u_fallMatrix;\n" \
"\n" \
" Color = (gl_Color * u_color_modulate) + u_color_add;\n" \
"\n" \
" vec3 OffsetViewOrigin = (u_view_origin - gl_Vertex).xyz;\n" \
" vec3 OffsetLightOrigin = (u_light_origin - gl_Vertex).xyz;\n" \
"\n" \
" lightDir = OffsetLightOrigin * u_lightMatrix;\n" \
" halfAngle = (normalize (OffsetViewOrigin) + normalize (OffsetLightOrigin)) * u_lightMatrix;\n" \
"}\n\n"
#define interaction_fs \
GLSL_VERSION_ATTRIBS \
GLSL_UNIFORMS \
GLSL_VARYINGS \
"void main ()\n" \
"{\n" \
" vec3 normalMap = texture2D (bumpImage, bumpCoords).agb * 2.0 - 1.0;\n" \
" vec4 lightMap = texture2DProj (lightProjectImage, projCoords);\n" \
"\n" \
" lightMap *= dot (normalize (lightDir), normalMap);\n" \
" lightMap *= texture2DProj (lightFalloffImage, fallCoords);\n" \
" lightMap *= Color;\n" \
"\n" \
" vec4 diffuseMap = texture2D (diffuseImage, diffCoords) * u_constant_diffuse;\n" \
" float specularComponent = clamp ((dot (normalize (halfAngle), normalMap) - 0.75) * 4.0, 0.0, 1.0);\n" \
"\n" \
" vec4 specularResult = u_constant_specular * (specularComponent * specularComponent);\n" \
" vec4 specularMap = texture2D (specularImage, specCoords) * 2.0;\n" \
"\n" \
" gl_FragColor = (diffuseMap + (specularResult * specularMap)) * lightMap;\n" \
"}\n\n"
/*
===========================================================================
DEFAULT GLSL SHADER
===========================================================================
*/
/* 32 bit hexadecimal 0, BFG had this set to a negative value which is illegal on unsigned */
static const GLuint INVALID_PROGRAM = 0x00000000;
static GLuint u_light_origin = 0x00000000;
static GLuint u_view_origin = 0x00000000;
static GLuint u_color_modulate = 0x00000000;
static GLuint u_color_add = 0x00000000;
static GLuint u_constant_diffuse = 0x00000000;
static GLuint u_constant_specular = 0x00000000;
static GLuint u_diffMatrix = 0x00000000;
static GLuint u_bumpMatrix = 0x00000000;
static GLuint u_specMatrix = 0x00000000;
static GLuint u_projMatrix = 0x00000000;
static GLuint u_fallMatrix = 0x00000000;
static GLuint rb_glsl_interaction_program = 0x00000000;
/*
=========================================================================================
GENERAL INTERACTION RENDERING
=========================================================================================
*/
/*
==================
RB_ARB2_BindTexture
==================
*/
static void RB_ARB2_BindTexture( int unit, idImage *tex )
{
backEnd.glState.currenttmu = unit;
glActiveTexture( GL_TEXTURE0_ARB + unit );
tex->Bind();
}
/*
==================
RB_ARB2_DrawInteraction
==================
*/
static void RB_ARB2_UnbindTexture( int unit )
{
backEnd.glState.currenttmu = unit;
glActiveTexture( GL_TEXTURE0_ARB + unit );
globalImages->BindNull();
}
/*
==================
RB_ARB2_BindInteractionTextureSet
==================
*/
static void RB_ARB2_BindInteractionTextureSet( const drawInteraction_t *din )
{
// texture 1 will be the per-surface bump map
RB_ARB2_BindTexture( 1, din->bumpImage );
// texture 2 will be the light falloff texture
RB_ARB2_BindTexture( 2, din->lightFalloffImage );
// texture 3 will be the light projection texture
RB_ARB2_BindTexture( 3, din->lightImage );
// texture 4 is the per-surface diffuse map
RB_ARB2_BindTexture( 4, din->diffuseImage );
// texture 5 is the per-surface specular map
RB_ARB2_BindTexture( 5, din->specularImage );
}
/*
==================
RB_GLSL_MakeMatrix
==================
*/
static float *RB_GLSL_MakeMatrix( const float *in1 = NULL, const float *in2 = NULL, const float *in3 = NULL, const float *in4 = NULL )
{
static float m[16];
if ( in1 )
{
SIMDProcessor->Memcpy( &m[0], in1, sizeof( float ) * 4 );
}
if ( in2 )
{
SIMDProcessor->Memcpy( &m[4], in2, sizeof( float ) * 4 );
}
if ( in3 )
{
SIMDProcessor->Memcpy( &m[8], in3, sizeof( float ) * 4 );
}
if ( in4 )
{
SIMDProcessor->Memcpy( &m[12], in4, sizeof( float ) * 4 );
}
return m;
}
/* Calculate matrix offsets */
#define DIFFMATRIX(ofs) din->diffuseMatrix[ofs].ToFloatPtr ()
#define BUMPMATRIX(ofs) din->bumpMatrix[ofs].ToFloatPtr ()
#define SPECMATRIX(ofs) din->specularMatrix[ofs].ToFloatPtr ()
#define PROJMATRIX(ofs) din->lightProjection[ofs].ToFloatPtr ()
/* Half Lambertian constants */
static const float whalf[] = { 0.0f, 0.0f, 0.0f, 0.5f };
static const float wzero[] = { 0.0f, 0.0f, 0.0f, 0.0f };
static const float wone[] = { 0.0f, 0.0f, 0.0f, 1.0f };
/* Lambertian constants */
static const float zero[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
static const float one[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static const float negOne[4] = { -1.0f, -1.0f, -1.0f, -1.0f };
/*
==================
RB_GLSL_DrawInteraction
==================
*/
static void RB_GLSL_DrawInteraction( const drawInteraction_t *din )
{
glUniform4fv( u_light_origin, 1, din->localLightOrigin.ToFloatPtr() );
glUniform4fv( u_view_origin, 1, din->localViewOrigin.ToFloatPtr() );
glUniformMatrix2x4fv( u_diffMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( DIFFMATRIX( 0 ), DIFFMATRIX( 1 ) ) );
glUniformMatrix2x4fv( u_bumpMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( BUMPMATRIX( 0 ), BUMPMATRIX( 1 ) ) );
glUniformMatrix2x4fv( u_specMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( SPECMATRIX( 0 ), SPECMATRIX( 1 ) ) );
glUniformMatrix4fv( u_projMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( PROJMATRIX( 0 ), PROJMATRIX( 1 ), wzero, PROJMATRIX( 2 ) ) );
glUniformMatrix4fv( u_fallMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( PROJMATRIX( 3 ), whalf, wzero, wone ) );
switch ( din->vertexColor )
{
case SVC_IGNORE:
glUniform4fv( u_color_modulate, 1, zero );
glUniform4fv( u_color_add, 1, one );
break;
case SVC_MODULATE:
glUniform4fv( u_color_modulate, 1, one );
glUniform4fv( u_color_add, 1, zero );
break;
case SVC_INVERSE_MODULATE:
glUniform4fv( u_color_modulate, 1, negOne );
glUniform4fv( u_color_add, 1, one );
break;
}
// set the constant colors
glUniform4fv( u_constant_diffuse, 1, din->diffuseColor.ToFloatPtr() );
glUniform4fv( u_constant_specular, 1, din->specularColor.ToFloatPtr() );
// set the textures
RB_ARB2_BindInteractionTextureSet( din );
// draw it
RB_DrawElementsWithCounters( din->surf->geo );
}
/*
==================
RB_ARB2_DrawInteraction
==================
*/
static void RB_ARB2_DrawInteraction( const drawInteraction_t *din )
{
// load all the vertex program parameters
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
// testing fragment based normal mapping
if ( r_testARBProgram.GetBool() )
{
glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
}
switch ( din->vertexColor )
{
case SVC_IGNORE:
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
break;
case SVC_MODULATE:
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
break;
case SVC_INVERSE_MODULATE:
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
glProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
break;
}
// set the constant colors
glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
glProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
// set the textures
RB_ARB2_BindInteractionTextureSet( din );
// draw it
RB_DrawElementsWithCounters( din->surf->geo );
}
/*
==================
RB_ARB2_SharedSurfaceSetup
Shared between the GLSL and ARB renderer,
cuts down on some state changes.
==================
*/
static void RB_ARB2_SharedSurfaceSetup( const drawSurf_t *surf )
{
// set the vertex pointers
idDrawVert *ac = ( idDrawVert * ) vertexCache.Position( surf->geo->ambientCache );
glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), reinterpret_cast<const GLvoid *>( &ac->color ) );
glVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
glVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
glVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
glVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
}
/*
=============
RB_ARB2_CreateDrawInteractions
=============
*/
static void RB_ARB2_CreateDrawInteractions( const drawSurf_t *surf )
{
if ( !surf )
{
return;
}
// perform setup here that will be constant for all interactions
GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
// check for enabled GLSL program first, if it fails go back to ARB
if ( rb_glsl_interaction_program != INVALID_PROGRAM )
{
// bind GLSL program
glUseProgram( rb_glsl_interaction_program );
// enable the vertex arrays
glEnableVertexAttribArrayARB( 8 );
glEnableVertexAttribArrayARB( 9 );
glEnableVertexAttribArrayARB( 10 );
glEnableVertexAttribArrayARB( 11 );
glEnableClientState( GL_COLOR_ARRAY );
// texture 0 is the normalization cube map for the vector towards the light
if ( backEnd.vLight->lightShader->IsAmbientLight() )
{
RB_ARB2_BindTexture( 0, globalImages->ambientNormalMap );
}
else
{
RB_ARB2_BindTexture( 0, globalImages->normalCubeMapImage );
}
// no test program in GLSL renderer
RB_ARB2_BindTexture( 6, globalImages->specularTableImage );
for ( /**/; surf; surf = surf->nextOnLight )
{
// perform setup here that will not change over multiple interaction passes
RB_ARB2_SharedSurfaceSetup( surf );
// this may cause RB_ARB2_DrawInteraction to be executed multiple
// times with different colors and images if the surface or light have multiple layers
RB_CreateSingleDrawInteractions( surf, RB_GLSL_DrawInteraction );
}
glDisableVertexAttribArrayARB( 8 );
glDisableVertexAttribArrayARB( 9 );
glDisableVertexAttribArrayARB( 10 );
glDisableVertexAttribArrayARB( 11 );
glDisableClientState( GL_COLOR_ARRAY );
// back to fixed (or ARB program)
glUseProgram( INVALID_PROGRAM );
}
else // Do it the old way
{
// Enable vertex programs
glEnable( GL_VERTEX_PROGRAM_ARB );
glEnable( GL_FRAGMENT_PROGRAM_ARB );
// bind the vertex program
if ( r_testARBProgram.GetBool() )
{
glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
}
else
{
glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
}
// enable the vertex arrays
glEnableVertexAttribArrayARB( 8 );
glEnableVertexAttribArrayARB( 9 );
glEnableVertexAttribArrayARB( 10 );
glEnableVertexAttribArrayARB( 11 );
glEnableClientState( GL_COLOR_ARRAY );
// texture 0 is the normalization cube map for the vector towards the light
if ( backEnd.vLight->lightShader->IsAmbientLight() )
{
RB_ARB2_BindTexture( 0, globalImages->ambientNormalMap );
}
else
{
RB_ARB2_BindTexture( 0, globalImages->normalCubeMapImage );
}
if ( r_testARBProgram.GetBool() )
{
RB_ARB2_BindTexture( 6, globalImages->specular2DTableImage );
}
else
{
RB_ARB2_BindTexture( 6, globalImages->specularTableImage );
}
for ( /**/; surf; surf = surf->nextOnLight )
{
// perform setup here that will not change over multiple interaction passes
RB_ARB2_SharedSurfaceSetup( surf );
// this may cause RB_ARB2_DrawInteraction to be executed multiple
// times with different colors and images if the surface or light have multiple layers
RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
}
glDisableVertexAttribArrayARB( 8 );
glDisableVertexAttribArrayARB( 9 );
glDisableVertexAttribArrayARB( 10 );
glDisableVertexAttribArrayARB( 11 );
glDisableClientState( GL_COLOR_ARRAY );
// disable vertex programs
glDisable( GL_VERTEX_PROGRAM_ARB );
glDisable( GL_FRAGMENT_PROGRAM_ARB );
// back to fixed (or GLSL program)
glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NONE );
}
// disable features
for ( int i = 6; i >= 0; i-- )
{
RB_ARB2_UnbindTexture( i );
}
backEnd.glState.currenttmu = -1;
GL_SelectTexture( 0 );
}
/*
==================
RB_ARB2_InteractionPass
Another shared function used to cut down on state changes.
==================
*/
static void RB_ARB2_InteractionPass( const drawSurf_t *shadowSurfs, const drawSurf_t *lightSurfs )
{
// save on state changes by not bothering to setup / takedown all the messy states when there are no surfs to draw
if ( shadowSurfs )
{
// stencil shadows are ARB only...
glEnable( GL_VERTEX_PROGRAM_ARB );
glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
RB_StencilShadowPass( shadowSurfs );
// if there weren't any globalInteractions, it would have stayed on
glDisable( GL_VERTEX_PROGRAM_ARB );
}
// now do light interactions (GLSL or ARB)
if ( lightSurfs )
{
RB_ARB2_CreateDrawInteractions( lightSurfs );
}
}
/*
==================
RB_ARB2_DrawInteractions
==================
*/
void RB_ARB2_DrawInteractions( void )
{
viewLight_t *vLight;
// turning off texcoords here causes an invalid enum to be thrown, was also removed from darkmod.
GL_SelectTexture( 0 );
// for each light, perform adding and shadowing
for ( vLight = backEnd.viewDef->viewLights; vLight; vLight = vLight->next )
{
backEnd.vLight = vLight;
// do fogging later
if ( vLight->lightShader->IsFogLight() )
{
continue;
}
// blend light interactions later
if ( vLight->lightShader->IsBlendLight() )
{
continue;
}
// nothing to see here; these aren't the surfaces you're looking for; move along
if ( !vLight->localInteractions &&
!vLight->globalInteractions &&
!vLight->translucentInteractions )
{
continue;
}
// clear the stencil buffer if needed
if ( vLight->globalShadows || vLight->localShadows )
{
backEnd.currentScissor = vLight->scissorRect;
if ( r_useScissor.GetBool() )
{
glScissor( 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 );
}
glClear( GL_STENCIL_BUFFER_BIT );
}
else
{
// no shadows, so no need to read or write the stencil buffer
// we might in theory want to use GL_ALWAYS instead of disabling
// completely, to satisfy the invarience rules
glStencilFunc( GL_ALWAYS, 128, 255 );
}
// run our passes for global and local
RB_ARB2_InteractionPass( vLight->globalShadows, vLight->localInteractions );
RB_ARB2_InteractionPass( vLight->localShadows, vLight->globalInteractions );
// translucent surfaces never get stencil shadowed
if ( r_skipTranslucent.GetBool() )
{
continue;
}
glStencilFunc( GL_ALWAYS, 128, 255 );
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
RB_ARB2_CreateDrawInteractions( vLight->translucentInteractions );
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
}
glDisable( GL_VERTEX_PROGRAM_ARB );
glDisable( GL_FRAGMENT_PROGRAM_ARB );
// disable stencil shadow test
glStencilFunc( GL_ALWAYS, 128, 255 );
GL_SelectTexture( 0 );
}
//===================================================================================
typedef struct
{
GLenum target;
GLuint ident;
char name[64];
} progDef_t;
static const int MAX_GLPROGS = 256;
// a single file can have both a vertex program and a fragment program
static progDef_t progs[MAX_GLPROGS] =
{
{ GL_VERTEX_PROGRAM_ARB, VPROG_TEST, "test.vfp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST, "test.vfp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION, "interaction.vfp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION, "interaction.vfp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT, "ambientLight.vfp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT, "ambientLight.vfp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW, "shadow.vp" },
{ GL_VERTEX_PROGRAM_ARB, VPROG_ENVIRONMENT, "environment.vfp" },
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_ENVIRONMENT, "environment.vfp" }
// additional programs can be dynamically specified in materials
};
/*
=================
R_LoadShaderProgram
=================
*/
static void R_LoadShaderProgram( int progIndex )
{
int ofs;
int err;
idStr fullPath = "glprogs/";
fullPath += progs[progIndex].name;
char *fileBuffer;
char *start, *end;
if ( !glConfig.isInitialized )
{
return;
}
common->Printf( "%s", fullPath.c_str() );
// load the program even if we don't support it, so
// fs_copyfiles can generate cross-platform data dumps
fileSystem->ReadFile( fullPath.c_str(), reinterpret_cast<void **>( &fileBuffer ), NULL );
if ( !fileBuffer )
{
common->Printf( ": File not found\n" );
return;
}
// copy to stack memory
DYNAMIC_STACK_ARRAY( char, buffer, strlen( fileBuffer ) + 1 );
strcpy( buffer, fileBuffer );
fileSystem->FreeFile( fileBuffer );
// submit the program string at start to GL
if ( progs[progIndex].ident == 0 )
{
// allocate a new identifier for this program
progs[progIndex].ident = PROG_USER + progIndex;
}
// vertex and fragment programs can both be present in a single file, so
// scan for the proper header to be the start point, and stamp a 0 in after the end
if ( progs[progIndex].target == GL_VERTEX_PROGRAM_ARB )
{
start = strstr( buffer, "!!ARBvp" );
}
if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB )
{
start = strstr( buffer, "!!ARBfp" );
}
if ( !start )
{
common->Printf( ": !!ARB not found\n" );
return;
}
end = strstr( start, "END" );
if ( !end )
{
common->Printf( ": END not found\n" );
return;
}
end[3] = 0;
glBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
glGetError();
glProgramStringARB( progs[progIndex].target, GL_PROGRAM_FORMAT_ASCII_ARB, strlen( start ), reinterpret_cast<unsigned char *>( start ) );
err = glGetError();
glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, reinterpret_cast<GLint *>( &ofs ) );
if ( err == GL_INVALID_OPERATION )
{
const GLubyte *str = glGetString( GL_PROGRAM_ERROR_STRING_ARB );
common->Printf( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str );
if ( ofs < 0 )
{
common->Printf( "GL_PROGRAM_ERROR_POSITION_ARB < 0 with error in %s\n", buffer );
}
else if ( ofs >= static_cast<int>( strlen( reinterpret_cast<const char *>( start ) ) ) )
{
common->Printf( "error at end of program %s\n", buffer );
}
else
{
common->Printf( "error at %i:\n%s", ofs, start + ofs );
}
return;
}
if ( ofs != -1 )
{
// Means the program loaded an unsupported vertex operation (non fatal)
common->Printf( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error in %s\n", buffer );
return;
}
common->Printf( "\n" );
}
/*
==================
R_FindShaderProgram
Returns a GL identifier that can be bound to the given target, parsing
a text file if it hasn't already been loaded.
==================
*/
int R_FindShaderProgram( GLenum target, const char *program )
{
int i;
idStr stripped = program;
stripped.StripFileExtension();
// see if it is already loaded
for ( i = 0; progs[i].name[0]; i++ )
{
if ( progs[i].target != target )
{
continue;
}
idStr compare = progs[i].name;
compare.StripFileExtension();
if ( !idStr::Icmp( stripped.c_str(), compare.c_str() ) )
{
return progs[i].ident;
}
}
if ( i == MAX_GLPROGS )
{
common->Error( "R_FindARBProgram: MAX_GLPROGS" );
}
// add it to the list and load it
progs[i].ident = ( program_t ) 0; // will be gen'd by R_LoadShaderProgram
progs[i].target = target;
strncpy( progs[i].name, program, sizeof( progs[i].name ) - 1 );
R_LoadShaderProgram( i );
return progs[i].ident;
}
/*
==================
GL_GetShaderInfoLog
==================
*/
static void GL_GetShaderInfoLog( GLuint s, GLchar *src, bool isprog )
{
static char infolog[4096];
int outlen = 0;
infolog[0] = GL_NONE;
if ( isprog )
{
glGetProgramInfoLog( s, 4095, &outlen, infolog );
}
else
{
glGetShaderInfoLog( s, 4095, &outlen, infolog );
}
common->Warning( "Shader Source:\n\n%s\n\n%s\n\n", src, infolog );
}
/*
==================
GL_CompileShader
==================
*/
static bool GL_CompileShader( GLuint sh, GLchar *src )
{
if ( sh && src )
{
int result = 0;
glGetError();
glShaderSource( sh, 1, ( const GLchar ** ) &src, NULL );
glCompileShader( sh );
glGetShaderiv( sh, GL_COMPILE_STATUS, &result );
if ( result != GL_TRUE )
{
GL_GetShaderInfoLog( sh, src, false );
return false;
}
else if ( glGetError() != GL_NO_ERROR )
{
GL_GetShaderInfoLog( sh, src, false );
}
}
return true;
}
//===================================================================================
struct glsltable_t
{
GLuint slot;
GLchar *name;
};
// doom actually emulates immediate function modes with quite a bit of the vertex attrib calls, like glVertex3f = attrPosition or glColor3/4f = attribColor etc.
// this is also the reason our first attempts at replacing them with vertex array pointers failed,
// because those index positions are not declared in the shader at all.
// the uncommented ones below are the ones missing from the shaders,
// i only left them in in case someone wanted to make an effort in that regard.
glsltable_t interactionAttribs[] =
{
/*{0, "attrPosition"}, // does not exist in shader
{2, "attrNormal"}, // ditto and we have two normal indexes (one is used to get texture coordinates for skyportals)
{3, "attrColor"},*/ // sigh...
{8, "attrTexCoords"},
{9, "attrTangents0"},
{10, "attrTangents1"},
{11, "attrNormal"}
};
/*
==================
GL_CreateGLSLProgram
Checks and creates shader programs for GLSL
Modified to throw invalid program if something fails.
==================
*/
static GLuint GL_CreateGLSLProgram( GLchar *vssrc, GLchar *fssrc, glsltable_t *attribs, GLuint numattribs )
{
GLuint progid;
GLuint vs;
GLuint fs;
glGetError();
// create vertex shader object
if ( vssrc )
{
vs = glCreateShader( GL_VERTEX_SHADER );
}
// create fragment shader object
if ( fssrc )
{
fs = glCreateShader( GL_FRAGMENT_SHADER );
}
// vertex shader failed to compile
if ( vs && vssrc && !GL_CompileShader( vs, vssrc ) )
{
return INVALID_PROGRAM;
}
// fragment shader failed to compile
if ( fs && fssrc && !GL_CompileShader( fs, fssrc ) )
{
return INVALID_PROGRAM;
}
progid = glCreateProgram();
if ( vs && vssrc )
{
glAttachShader( progid, vs );
}
if ( fs && fssrc )
{
glAttachShader( progid, fs );
}
// bind attrib index numbers
// we could actually bind the emulated ones here as well and then vertex attribs should work.
if ( attribs && numattribs )
{
for ( GLuint i = 0; i < numattribs; i++ )
{
glBindAttribLocation( progid, attribs[i].slot, attribs[i].name );
}
}
GLint result = GL_FALSE;
glLinkProgram( progid );
glGetProgramiv( progid, GL_LINK_STATUS, &result );
glDeleteShader( vs );
glDeleteShader( fs );
if ( result != GL_TRUE )
{
GL_GetShaderInfoLog( progid, "", true );
return INVALID_PROGRAM;
}
return progid;
}
//===================================================================================
struct sampleruniforms_t
{
GLchar *name;
GLint binding;
};
sampleruniforms_t rb_interactionsamplers[] =
{
{"bumpImage", 1},
{"lightFalloffImage", 2},
{"lightProjectImage", 3},
{"diffuseImage", 4},
{"specularImage", 5}
};
/*
==================
GL_SetupSamplerUniforms
==================
*/
static void GL_SetupSamplerUniforms( GLuint progid, sampleruniforms_t *uniForms, GLuint numUniforms )
{
// setup uniform locations - this is needed even on nvidia
glUseProgram( progid );
for ( GLuint i = 0; i < numUniforms; i++ )
{
glUniform1i( glGetUniformLocation( progid, uniForms[i].name ), uniForms[i].binding );
}
}
/*
==================
GL_GetGLSLFromFile
==================
*/
static GLchar *GL_GetGLSLFromFile( const GLchar *name )
{
idStr fullPath = "glprogs130/";
fullPath += name;
GLchar *fileBuffer;
GLchar *buffer;
if ( !glConfig.isInitialized )
{
return NULL;
}
common->Printf( "%s", fullPath.c_str() );
fileSystem->ReadFile( fullPath.c_str(), reinterpret_cast<void **>( &fileBuffer ), NULL );
if ( !fileBuffer )
{
common->Printf( ": File not found, using internal shaders\n" );
return NULL;
}
// copy to stack memory
buffer = reinterpret_cast<char *>( malloc( strlen( fileBuffer ) + 1 ) );
strcpy( buffer, fileBuffer );
fileSystem->FreeFile( fileBuffer );
common->Printf( "\n" );
return buffer;
}
/*
==================
R_ReloadShaders_f
==================
*/
void R_ReloadShaders_f( const idCmdArgs &args )
{
// load ARB programs by default
common->Printf( "----- R_ReloadShaders_f -----\n" );
for ( int i = 0; progs[i].name[0]; i++ )
{
R_LoadShaderProgram( i );
}
// load GLSL interaction programs if enabled
if ( r_useGLSL.GetBool() && glConfig.ARBShadingLanguageAvailable )
{
glDeleteProgram( rb_glsl_interaction_program );
GLchar *vs = GL_GetGLSLFromFile( "interaction_vs.glsl" );
GLchar *fs = GL_GetGLSLFromFile( "interaction_fs.glsl" );
// replace ARB interaction shaders with GLSL counterparts, it is possible to use external GLSL shaders as well.
rb_glsl_interaction_program = GL_CreateGLSLProgram( ( vs != NULL ) ? vs : interaction_vs, ( fs != NULL ) ? fs : interaction_fs, interactionAttribs, sizeof( interactionAttribs ) / sizeof( interactionAttribs[0] ) );
if ( vs != NULL )
{
free( vs );
}
if ( fs != NULL )
{
free( fs );
}
if ( rb_glsl_interaction_program != INVALID_PROGRAM )
{
// made sure shaders are valid coming in here
GL_SetupSamplerUniforms( rb_glsl_interaction_program, rb_interactionsamplers, sizeof( rb_interactionsamplers ) / sizeof( rb_interactionsamplers[0] ) );
u_light_origin = glGetUniformLocation( rb_glsl_interaction_program, "u_light_origin" );
u_view_origin = glGetUniformLocation( rb_glsl_interaction_program, "u_view_origin" );
u_color_modulate = glGetUniformLocation( rb_glsl_interaction_program, "u_color_modulate" );
u_color_add = glGetUniformLocation( rb_glsl_interaction_program, "u_color_add" );
u_constant_diffuse = glGetUniformLocation( rb_glsl_interaction_program, "u_constant_diffuse" );
u_constant_specular = glGetUniformLocation( rb_glsl_interaction_program, "u_constant_specular" );
u_diffMatrix = glGetUniformLocation( rb_glsl_interaction_program, "u_diffMatrix" );
u_bumpMatrix = glGetUniformLocation( rb_glsl_interaction_program, "u_bumpMatrix" );
u_specMatrix = glGetUniformLocation( rb_glsl_interaction_program, "u_specMatrix" );
u_projMatrix = glGetUniformLocation( rb_glsl_interaction_program, "u_projMatrix" );
u_fallMatrix = glGetUniformLocation( rb_glsl_interaction_program, "u_fallMatrix" );
}
}
common->Printf("-------------------------------\n");
}
No its really simple to add if your own engine uses GLew and has the old render paths removed, then its simply a matter of replacing the contents of draw_arb2.cpp with this .
i been testing this one for ages because i was not sure i really got that lucky, but my modifications to the original code work (yes even with sikkmod, allthough if you want to use the parralax shader you will have to turn of r_useGLSL) since that is an ARB interaction shader and not a GLSL one, tbh its not worth it with the parallax shader since its bugs out on certain textures anyway.
Originally there was a lot of hacking about to shift between the ARB and GLSL paths by invalidating the shader, i brought those hacks down to 1... global one, and i can assure that you wont even see the switch now if you toy with the r_useGLSL value .
This one only covers the interactions, the shadows and other effects are still covered by the ARB shaders.