mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-11-26 06:11:26 +00:00
6dbcf6523e
so far the code assumed that "result.color" is always used directly, but the ARB shaders allow creating an alias with the aforementioned syntax. So turn that into an variable-alias for dhewm3tmpres.
669 lines
22 KiB
C++
669 lines
22 KiB
C++
/*
|
|
===========================================================================
|
|
|
|
Doom 3 GPL Source Code
|
|
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
|
|
|
|
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
|
|
|
|
Doom 3 Source Code is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
Doom 3 Source Code is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
|
|
|
|
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
|
|
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "sys/platform.h"
|
|
#include "renderer/VertexCache.h"
|
|
|
|
#include "renderer/tr_local.h"
|
|
|
|
/*
|
|
=========================================================================================
|
|
|
|
GENERAL INTERACTION RENDERING
|
|
|
|
=========================================================================================
|
|
*/
|
|
|
|
/*
|
|
====================
|
|
GL_SelectTextureNoClient
|
|
====================
|
|
*/
|
|
static void GL_SelectTextureNoClient( int unit ) {
|
|
backEnd.glState.currenttmu = unit;
|
|
qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
RB_ARB2_DrawInteraction
|
|
==================
|
|
*/
|
|
void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
|
|
// load all the vertex program parameters
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
|
|
|
|
// testing fragment based normal mapping
|
|
if ( r_testARBProgram.GetBool() ) {
|
|
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
|
|
}
|
|
|
|
static const float zero[4] = { 0, 0, 0, 0 };
|
|
static const float one[4] = { 1, 1, 1, 1 };
|
|
static const float negOne[4] = { -1, -1, -1, -1 };
|
|
|
|
switch ( din->vertexColor ) {
|
|
case SVC_IGNORE:
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
|
break;
|
|
case SVC_MODULATE:
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
|
|
break;
|
|
case SVC_INVERSE_MODULATE:
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
|
|
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
|
|
break;
|
|
}
|
|
|
|
// set the constant colors
|
|
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
|
|
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
|
|
|
|
// DG: brightness and gamma in shader as program.env[4]
|
|
if ( r_gammaInShader.GetBool() ) {
|
|
// program.env[4].xyz are all r_brightness, program.env[4].w is 1.0/r_gamma
|
|
float parm[4];
|
|
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
|
|
parm[3] = 1.0/r_gamma.GetFloat(); // 1.0/gamma so the shader doesn't have to do this calculation
|
|
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 4, parm );
|
|
}
|
|
|
|
// set the textures
|
|
|
|
// texture 1 will be the per-surface bump map
|
|
GL_SelectTextureNoClient( 1 );
|
|
din->bumpImage->Bind();
|
|
|
|
// texture 2 will be the light falloff texture
|
|
GL_SelectTextureNoClient( 2 );
|
|
din->lightFalloffImage->Bind();
|
|
|
|
// texture 3 will be the light projection texture
|
|
GL_SelectTextureNoClient( 3 );
|
|
din->lightImage->Bind();
|
|
|
|
// texture 4 is the per-surface diffuse map
|
|
GL_SelectTextureNoClient( 4 );
|
|
din->diffuseImage->Bind();
|
|
|
|
// texture 5 is the per-surface specular map
|
|
GL_SelectTextureNoClient( 5 );
|
|
din->specularImage->Bind();
|
|
|
|
// draw it
|
|
RB_DrawElementsWithCounters( din->surf->geo );
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
RB_ARB2_CreateDrawInteractions
|
|
|
|
=============
|
|
*/
|
|
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 );
|
|
|
|
// bind the vertex program
|
|
if ( r_testARBProgram.GetBool() ) {
|
|
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
|
|
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
|
|
} else {
|
|
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
|
|
qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
|
|
}
|
|
|
|
qglEnable(GL_VERTEX_PROGRAM_ARB);
|
|
qglEnable(GL_FRAGMENT_PROGRAM_ARB);
|
|
|
|
// enable the vertex arrays
|
|
qglEnableVertexAttribArrayARB( 8 );
|
|
qglEnableVertexAttribArrayARB( 9 );
|
|
qglEnableVertexAttribArrayARB( 10 );
|
|
qglEnableVertexAttribArrayARB( 11 );
|
|
qglEnableClientState( GL_COLOR_ARRAY );
|
|
|
|
// texture 0 is the normalization cube map for the vector towards the light
|
|
GL_SelectTextureNoClient( 0 );
|
|
if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
|
|
globalImages->ambientNormalMap->Bind();
|
|
} else {
|
|
globalImages->normalCubeMapImage->Bind();
|
|
}
|
|
|
|
// texture 6 is the specular lookup table
|
|
GL_SelectTextureNoClient( 6 );
|
|
if ( r_testARBProgram.GetBool() ) {
|
|
globalImages->specular2DTableImage->Bind(); // variable specularity in alpha channel
|
|
} else {
|
|
globalImages->specularTableImage->Bind();
|
|
}
|
|
|
|
|
|
for ( ; surf ; surf=surf->nextOnLight ) {
|
|
// perform setup here that will not change over multiple interaction passes
|
|
|
|
// set the vertex pointers
|
|
idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
|
|
qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
|
|
qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
|
|
qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
|
|
qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
|
|
qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
|
|
qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
|
|
|
|
// this may cause RB_ARB2_DrawInteraction to be exacuted multiple
|
|
// times with different colors and images if the surface or light have multiple layers
|
|
RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
|
|
}
|
|
|
|
qglDisableVertexAttribArrayARB( 8 );
|
|
qglDisableVertexAttribArrayARB( 9 );
|
|
qglDisableVertexAttribArrayARB( 10 );
|
|
qglDisableVertexAttribArrayARB( 11 );
|
|
qglDisableClientState( GL_COLOR_ARRAY );
|
|
|
|
// disable features
|
|
GL_SelectTextureNoClient( 6 );
|
|
globalImages->BindNull();
|
|
|
|
GL_SelectTextureNoClient( 5 );
|
|
globalImages->BindNull();
|
|
|
|
GL_SelectTextureNoClient( 4 );
|
|
globalImages->BindNull();
|
|
|
|
GL_SelectTextureNoClient( 3 );
|
|
globalImages->BindNull();
|
|
|
|
GL_SelectTextureNoClient( 2 );
|
|
globalImages->BindNull();
|
|
|
|
GL_SelectTextureNoClient( 1 );
|
|
globalImages->BindNull();
|
|
|
|
backEnd.glState.currenttmu = -1;
|
|
GL_SelectTexture( 0 );
|
|
|
|
qglDisable(GL_VERTEX_PROGRAM_ARB);
|
|
qglDisable(GL_FRAGMENT_PROGRAM_ARB);
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
RB_ARB2_DrawInteractions
|
|
==================
|
|
*/
|
|
void RB_ARB2_DrawInteractions( void ) {
|
|
viewLight_t *vLight;
|
|
|
|
GL_SelectTexture( 0 );
|
|
qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
if ( vLight->lightShader->IsBlendLight() ) {
|
|
continue;
|
|
}
|
|
|
|
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() ) {
|
|
qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
|
|
backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
|
|
backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
|
|
backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
|
|
}
|
|
qglClear( 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
|
|
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
|
}
|
|
|
|
if ( r_useShadowVertexProgram.GetBool() ) {
|
|
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
|
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
|
RB_StencilShadowPass( vLight->globalShadows );
|
|
RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
|
|
qglEnable( GL_VERTEX_PROGRAM_ARB );
|
|
qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
|
|
RB_StencilShadowPass( vLight->localShadows );
|
|
RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
|
|
qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
|
|
} else {
|
|
RB_StencilShadowPass( vLight->globalShadows );
|
|
RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
|
|
RB_StencilShadowPass( vLight->localShadows );
|
|
RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
|
|
}
|
|
|
|
// translucent surfaces never get stencil shadowed
|
|
if ( r_skipTranslucent.GetBool() ) {
|
|
continue;
|
|
}
|
|
|
|
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
|
|
|
backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
|
|
RB_ARB2_CreateDrawInteractions( vLight->translucentInteractions );
|
|
|
|
backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
|
|
}
|
|
|
|
// disable stencil shadow test
|
|
qglStencilFunc( GL_ALWAYS, 128, 255 );
|
|
|
|
GL_SelectTexture( 0 );
|
|
qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
}
|
|
|
|
//===================================================================================
|
|
|
|
|
|
typedef struct {
|
|
GLenum target;
|
|
GLuint ident;
|
|
char name[64];
|
|
} progDef_t;
|
|
|
|
static const int MAX_GLPROGS = 200;
|
|
|
|
// 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" },
|
|
{ GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
|
|
{ GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
|
|
|
|
// additional programs can be dynamically specified in materials
|
|
};
|
|
|
|
/*
|
|
=================
|
|
R_LoadARBProgram
|
|
=================
|
|
*/
|
|
|
|
static char* findLineThatStartsWith( char* text, const char* findMe ) {
|
|
char* res = strstr( text, findMe );
|
|
while ( res != NULL ) {
|
|
// skip whitespace before match, if any
|
|
char* cur = res;
|
|
if ( cur > text ) {
|
|
--cur;
|
|
}
|
|
while ( cur > text && ( *cur == ' ' || *cur == '\t' ) ) {
|
|
--cur;
|
|
}
|
|
// now we should be at a newline (or at the beginning)
|
|
if ( cur == text ) {
|
|
return cur;
|
|
}
|
|
if ( *cur == '\n' || *cur == '\r' ) {
|
|
return cur+1;
|
|
}
|
|
// otherwise maybe we're in commented out text or whatever, search on
|
|
res = strstr( res+1, findMe );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static ID_INLINE bool isARBidentifierChar( int c ) {
|
|
// according to chapter 3.11.2 in ARB_fragment_program.txt identifiers can only
|
|
// contain these chars (first char mustn't be a number, but hat doesn't matter here)
|
|
// NOTE: isalnum() or isalpha() apparently doesn't work, as it also matches spaces (?!)
|
|
return c == '$' || c == '_'
|
|
|| (c >= '0' && c <= '9')
|
|
|| (c >= 'A' && c <= 'Z')
|
|
|| (c >= 'a' && c <= 'z');
|
|
}
|
|
|
|
void R_LoadARBProgram( int progIndex ) {
|
|
int ofs;
|
|
int err;
|
|
idStr fullPath = "glprogs/";
|
|
fullPath += progs[progIndex].name;
|
|
char *fileBuffer;
|
|
char *buffer;
|
|
char *start = NULL, *end;
|
|
|
|
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(), (void **)&fileBuffer, NULL );
|
|
if ( !fileBuffer ) {
|
|
common->Printf( ": File not found\n" );
|
|
return;
|
|
}
|
|
|
|
// copy to stack memory and free
|
|
buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
|
|
strcpy( buffer, fileBuffer );
|
|
fileSystem->FreeFile( fileBuffer );
|
|
|
|
if ( !glConfig.isInitialized ) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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 ) {
|
|
if ( !glConfig.ARBVertexProgramAvailable ) {
|
|
common->Printf( ": GL_VERTEX_PROGRAM_ARB not available\n" );
|
|
return;
|
|
}
|
|
start = strstr( buffer, "!!ARBvp" );
|
|
}
|
|
if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
|
|
if ( !glConfig.ARBFragmentProgramAvailable ) {
|
|
common->Printf( ": GL_FRAGMENT_PROGRAM_ARB not available\n" );
|
|
return;
|
|
}
|
|
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;
|
|
|
|
// DG: hack gamma correction into shader
|
|
if ( r_gammaInShader.GetBool() && progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
|
|
|
|
// note that strlen("dhewm3tmpres") == strlen("result.color")
|
|
const char* tmpres = "TEMP dhewm3tmpres; # injected by dhewm3 for gamma correction\n";
|
|
|
|
// Note: program.env[4].xyz = r_brightness; program.env[4].w = 1.0/r_gamma
|
|
// outColor.rgb = pow(dhewm3tmpres.rgb*r_brightness, vec3(1.0/r_gamma))
|
|
// outColor.a = dhewm3tmpres.a;
|
|
const char* extraLines =
|
|
"# gamma correction in shader, injected by dhewm3 \n"
|
|
"MUL dhewm3tmpres.xyz, program.env[4], dhewm3tmpres;\n" // first multiply with brightness
|
|
"POW result.color.x, dhewm3tmpres.x, program.env[4].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
|
|
"POW result.color.y, dhewm3tmpres.y, program.env[4].w;\n" // (apparently POW only supports scalars, not whole vectors)
|
|
"POW result.color.z, dhewm3tmpres.z, program.env[4].w;\n"
|
|
"MOV result.color.w, dhewm3tmpres.w;\n" // alpha remains unmodified
|
|
"\nEND\n\n"; // we add this block right at the end, replacing the original "END" string
|
|
|
|
int fullLen = strlen( start ) + strlen( tmpres ) + strlen( extraLines );
|
|
char* outStr = (char*)_alloca( fullLen + 1 );
|
|
|
|
// add tmpres right after OPTION line (if any)
|
|
char* insertPos = findLineThatStartsWith( start, "OPTION" );
|
|
if ( insertPos == NULL ) {
|
|
// no OPTION? then just put it after the first line (usually sth like "!!ARBfp1.0\n")
|
|
insertPos = start;
|
|
}
|
|
// but we want the position *after* that line
|
|
while( *insertPos != '\0' && *insertPos != '\n' && *insertPos != '\r' ) {
|
|
++insertPos;
|
|
}
|
|
// skip the newline character(s) as well
|
|
while( *insertPos == '\n' || *insertPos == '\r' ) {
|
|
++insertPos;
|
|
}
|
|
|
|
// copy text up to insertPos
|
|
int curLen = insertPos-start;
|
|
memcpy( outStr, start, curLen );
|
|
// copy tmpres ("TEMP dhewm3tmpres; # ..")
|
|
memcpy( outStr+curLen, tmpres, strlen( tmpres ) );
|
|
curLen += strlen( tmpres );
|
|
// copy remaining original shader up to (excluding) "END"
|
|
int remLen = end - insertPos;
|
|
memcpy( outStr+curLen, insertPos, remLen );
|
|
curLen += remLen;
|
|
|
|
outStr[curLen] = '\0'; // make sure it's NULL-terminated so normal string functions work
|
|
|
|
// replace all existing occurrences of "result.color" with "dhewm3tmpres"
|
|
for( char* resCol = strstr( outStr, "result.color" );
|
|
resCol != NULL; resCol = strstr( resCol+13, "result.color" ) ) {
|
|
memcpy( resCol, "dhewm3tmpres", 12 ); // both strings have the same length.
|
|
|
|
// if this was part of "OUTPUT bla = result.color;", replace
|
|
// "OUTPUT bla" with "ALIAS bla" (so it becomes "ALIAS bla = dhewm3tmpres;")
|
|
{
|
|
char* s = resCol - 1;
|
|
// first skip whitespace before "result.color"
|
|
while( s > outStr && (*s == ' ' || *s == '\t') ) {
|
|
--s;
|
|
}
|
|
// if there's no '=' before result.color, this line can't be affected
|
|
if ( *s != '=' || s <= outStr + 8 ) {
|
|
continue; // go on with next "result.color" in the for-loop
|
|
}
|
|
--s; // we were on '=', so go to the char before and it's time to skip whitespace again
|
|
while( s > outStr && ( *s == ' ' || *s == '\t' ) ) {
|
|
--s;
|
|
}
|
|
// now we should be at the end of "bla" (or however the variable/alias is called)
|
|
if ( s <= outStr+7 || !isARBidentifierChar( *s ) ) {
|
|
continue;
|
|
}
|
|
--s;
|
|
// skip all the remaining chars that are legal in identifiers
|
|
while( s > outStr && isARBidentifierChar( *s ) ) {
|
|
--s;
|
|
}
|
|
// there should be at least one space/tab between "OUTPUT" and "bla"
|
|
if ( s <= outStr + 6 || ( *s != ' ' && *s != '\t' ) ) {
|
|
continue;
|
|
}
|
|
--s;
|
|
// skip remaining whitespace (if any)
|
|
while( s > outStr && ( *s == ' ' || *s == '\t' ) ) {
|
|
--s;
|
|
}
|
|
// now we should be at "OUTPUT" (specifically at its last 'T'),
|
|
// if this is indeed such a case
|
|
if ( s <= outStr + 5 || *s != 'T' ) {
|
|
continue;
|
|
}
|
|
s -= 5; // skip to start of "OUTPUT", if this is indeed "OUTPUT"
|
|
if ( idStr::Cmpn( s, "OUTPUT", 6 ) == 0 ) {
|
|
// it really is "OUTPUT" => replace "OUTPUT" with "ALIAS "
|
|
memcpy(s, "ALIAS ", 6);
|
|
}
|
|
}
|
|
}
|
|
|
|
assert( curLen + strlen( extraLines ) <= fullLen );
|
|
|
|
// now add extraLines that calculate and set a gamma-corrected result.color
|
|
// strcat() should be safe because fullLen was calculated taking all parts into account
|
|
strcat( outStr, extraLines );
|
|
start = outStr;
|
|
}
|
|
|
|
qglBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
|
|
qglGetError();
|
|
|
|
qglProgramStringARB( progs[progIndex].target, GL_PROGRAM_FORMAT_ASCII_ARB,
|
|
strlen( start ), start );
|
|
|
|
err = qglGetError();
|
|
qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, (GLint *)&ofs );
|
|
if ( err == GL_INVALID_OPERATION ) {
|
|
const GLubyte *str = qglGetString( 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\n" );
|
|
} else if ( ofs >= (int)strlen( start ) ) {
|
|
common->Printf( "error at end of program\n" );
|
|
} else {
|
|
int printOfs = Max( ofs - 20, 0 ); // DG: print some more context
|
|
common->Printf( "error at %i:\n%s", ofs, start + printOfs );
|
|
}
|
|
return;
|
|
}
|
|
if ( ofs != -1 ) {
|
|
common->Printf( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error\n" );
|
|
return;
|
|
}
|
|
|
|
common->Printf( "\n" );
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_FindARBProgram
|
|
|
|
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_FindARBProgram( 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_LoadARBProgram
|
|
progs[i].target = target;
|
|
strncpy( progs[i].name, program, sizeof( progs[i].name ) - 1 );
|
|
|
|
R_LoadARBProgram( i );
|
|
|
|
return progs[i].ident;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_ReloadARBPrograms_f
|
|
==================
|
|
*/
|
|
void R_ReloadARBPrograms_f( const idCmdArgs &args ) {
|
|
int i;
|
|
|
|
common->Printf( "----- R_ReloadARBPrograms -----\n" );
|
|
for ( i = 0 ; progs[i].name[0] ; i++ ) {
|
|
R_LoadARBProgram( i );
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
R_ARB2_Init
|
|
|
|
==================
|
|
*/
|
|
void R_ARB2_Init( void ) {
|
|
glConfig.allowARB2Path = false;
|
|
|
|
common->Printf( "ARB2 renderer: " );
|
|
|
|
if ( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) {
|
|
common->Printf( "Not available.\n" );
|
|
return;
|
|
}
|
|
|
|
common->Printf( "Available.\n" );
|
|
|
|
glConfig.allowARB2Path = true;
|
|
}
|