the code pieces that needs modifying are these ->
/*
==================
RB_GLSL_DrawInteraction
==================
*/
static void RB_GLSL_DrawInteraction( const drawInteraction_t *din ) {
/* 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 };
// load all the vertex program parameters
qglUniform4fv( u_light_origin, 1, din->localLightOrigin.ToFloatPtr() );
qglUniform4fv( u_view_origin, 1, din->localViewOrigin.ToFloatPtr() );
qglUniformMatrix2x4fv( u_diffMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( DIFFMATRIX( 0 ), DIFFMATRIX( 1 ) ) );
qglUniformMatrix2x4fv( u_bumpMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( BUMPMATRIX( 0 ), BUMPMATRIX( 1 ) ) );
qglUniformMatrix2x4fv( u_specMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( SPECMATRIX( 0 ), SPECMATRIX( 1 ) ) );
qglUniformMatrix4fv( u_projMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( PROJMATRIX( 0 ), PROJMATRIX( 1 ), wzero, PROJMATRIX( 2 ) ) );
qglUniformMatrix4fv( u_fallMatrix, 1, GL_FALSE, RB_GLSL_MakeMatrix( PROJMATRIX( 3 ), whalf, wzero, wone ) );
/* 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 };
switch ( din->vertexColor ) {
case SVC_IGNORE:
qglUniform4fv( u_color_modulate, 1, zero );
qglUniform4fv( u_color_add, 1, one );
break;
case SVC_MODULATE:
qglUniform4fv( u_color_modulate, 1, one );
qglUniform4fv( u_color_add, 1, zero );
break;
case SVC_INVERSE_MODULATE:
qglUniform4fv( u_color_modulate, 1, negOne );
qglUniform4fv( u_color_add, 1, one );
break;
}
// set the constant colors
qglUniform4fv( u_constant_diffuse, 1, din->diffuseColor.ToFloatPtr() );
qglUniform4fv( u_constant_specular, 1, din->specularColor.ToFloatPtr() );
// TODO: shader gamma for GLSL.
// 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
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.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 };
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.0f / r_gamma
float parm[4];
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
parm[3] = 1.0f / r_gamma.GetFloat(); // 1.0f / gamma so the shader doesn't have to do this calculation
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
}
// set the textures
RB_ARB2_BindInteractionTextureSet( din );
// draw it
RB_DrawElementsWithCounters( din->surf->geo );
}
i left in the arb version so you can get an idea of what is going on, also this piece ->
/*
=================
R_LoadARBProgram
=================
*/
void R_LoadARBProgram( int progIndex ) {
int ofs;
int err;
char *buffer;
char *start = NULL, *end;
#if D3_INTEGRATE_SOFTPART_SHADERS
if ( progs[progIndex].ident == VPROG_SOFT_PARTICLE || progs[progIndex].ident == FPROG_SOFT_PARTICLE ) {
// these shaders are loaded directly from a string
common->Printf( "<internal> %s", progs[progIndex].name );
const char *srcstr = ( progs[progIndex].ident == VPROG_SOFT_PARTICLE ) ? softpartVShader : softpartFShader;
// copy to stack memory
buffer = ( char * )_alloca( strlen( srcstr ) + 1 );
strcpy( buffer, srcstr );
} else
#endif // D3_INTEGRATE_SOFTPART_SHADERS
{
idStr fullPath = "glprogs/";
fullPath += progs[progIndex].name;
char *fileBuffer;
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. TODO shader gamma for GLSL
if ( r_gammaInShader.GetBool() && progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB && strstr( start, "nodhewm3gammahack" ) == NULL ) {
// note that strlen("dhewm3tmpres") == strlen("result.color")
const char *tmpres = "TEMP dhewm3tmpres; # injected by dhewm3 for gamma correction\n";
// Note: program.env[21].xyz = r_brightness; program.env[21].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_SAT clamps the result to [0, 1] - it must not be negative because
// POW might not work with a negative base (it looks wrong with intel's Linux driver)
// and clamping values > 1 to 1 is ok because when writing to result.color
// it's clamped anyway and pow(base, exp) is always >= 1 for base >= 1
"MUL_SAT dhewm3tmpres.xyz, program.env[21], dhewm3tmpres;\n" // first multiply with brightness
"POW result.color.x, dhewm3tmpres.x, program.env[21].w;\n" // then do pow(dhewm3tmpres.xyz, vec3(1/gamma))
"POW result.color.y, dhewm3tmpres.y, program.env[21].w;\n" // (apparently POW only supports scalars, not whole vectors)
"POW result.color.z, dhewm3tmpres.z, program.env[21].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 = R_FindArbShaderComment( start, "OPTION" );
if ( insertPos == NULL ) {
// no OPTION? then just put it after the first line (usually something 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 || !R_IsArbIdentifier( *s ) ) {
continue;
}
--s;
// skip all the remaining chars that are legal in identifiers
while ( s > outStr && R_IsArbIdentifier( *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" );
}
in draw_arb2.cpp and
/*
==================
RB_SetProgramEnvironment
Sets variables that can be used by all vertex programs
[SteveL #3877] Note on the use of fragment program environmental variables.
Parameters 0 and 1 are set here to allow conversion of screen coordinates to
texture coordinates, for use when sampling _currentRender.
Those same parameters 0 and 1, plus 2 and 3, are given entirely different
meanings in draw_arb2.cpp while light interactions are being drawn.
This function is called again before currentRender size is needed by post processing
effects are done, so there's no clash.
Only parameters 0..3 were in use before #3877 - and in dhewm3 also 4, for gamma in shader.
Now I've used a new parameter 22 for the size of _currentDepth. It's needed throughout,
including by light interactions, and its size might in theory differ from _currentRender.
Parameters 23 and 24 are used by soft particles #3878. Note these can be freely reused by different draw calls.
==================
*/
void RB_SetProgramEnvironment( bool isPostProcess ) {
float parm[4];
int pot;
if ( !glConfig.ARBVertexProgramAvailable ) {
return;
}
#if 0
// screen power of two correction factor, one pixel in so we don't get a bilerp
// of an uncopied pixel
int w = backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1;
pot = globalImages->currentRenderImage->uploadWidth;
if ( w == pot ) {
parm[0] = 1.0;
} else {
parm[0] = ( float )( w - 1 ) / pot;
}
int h = backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1;
pot = globalImages->currentRenderImage->uploadHeight;
if ( h == pot ) {
parm[1] = 1.0;
} else {
parm[1] = ( float )( h - 1 ) / pot;
}
parm[2] = 0;
parm[3] = 1;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 0, parm );
#else
// screen power of two correction factor, assuming the copy to _currentRender
// also copied an extra row and column for the bilerp
int w = backEnd.viewDef->viewport.x2 - backEnd.viewDef->viewport.x1 + 1;
pot = globalImages->currentRenderImage->uploadWidth;
parm[0] = ( float )w / pot;
int h = backEnd.viewDef->viewport.y2 - backEnd.viewDef->viewport.y1 + 1;
pot = globalImages->currentRenderImage->uploadHeight;
parm[1] = ( float )h / pot;
parm[2] = 0;
parm[3] = 1;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 0, parm );
#endif
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, parm );
// window coord to 0.0 to 1.0 conversion
parm[0] = 1.0 / w;
parm[1] = 1.0 / h;
parm[2] = 0;
parm[3] = 1;
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, parm );
// DG: brightness and gamma in shader as program.env[4]. TODO: shader gamma for GLSL
if ( r_gammaInShader.GetBool() ) {
// program.env[4].xyz are all r_brightness, program.env[4].w is 1.0/r_gamma
if ( !isPostProcess ) {
parm[0] = parm[1] = parm[2] = r_brightness.GetFloat();
parm[3] = 1.0f / r_gamma.GetFloat(); // 1.0f / gamma so the shader doesn't have to do this calculation
} else {
// don't apply gamma/brightness in postprocess passes to avoid applying them twice
// (setting them to 1.0 makes them no-ops)
parm[0] = parm[1] = parm[2] = parm[3] = 1.0f;
}
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_GAMMA_BRIGHTNESS, parm );
}
// #3877: Allow shaders to access depth buffer.
// Two useful ratios are packed into this parm: [0] and [1] hold the x,y multipliers you need to map a screen
// coordinate (fragment position) to the depth image: those are simply the reciprocal of the depth
// image size, which has been rounded up to a power of two. Slots [3] and [4] hold the ratio of the depth image
// size to the current render image size. These sizes can differ if the game crops the render viewport temporarily
// during post-processing effects. The depth render is smaller during the effect too, but the depth image doesn't
// need to be downsized, whereas the current render image does get downsized when it's captured by the game after
// the skybox render pass. The ratio is needed to map between the two render images.
parm[0] = 1.0f / globalImages->currentDepthImage->uploadWidth;
parm[1] = 1.0f / globalImages->currentDepthImage->uploadHeight;
parm[2] = static_cast<float>( globalImages->currentRenderImage->uploadWidth ) / globalImages->currentDepthImage->uploadWidth;
parm[3] = static_cast<float>( globalImages->currentRenderImage->uploadHeight ) / globalImages->currentDepthImage->uploadHeight;
qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, PP_CURDEPTH_RECIPR, parm );
//
// set eye position in global space
//
parm[0] = backEnd.viewDef->renderView.vieworg[0];
parm[1] = backEnd.viewDef->renderView.vieworg[1];
parm[2] = backEnd.viewDef->renderView.vieworg[2];
parm[3] = 1.0;
qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, 1, parm );
}
in draw_common.cpp.
note the TODO sections.
biggest problem right now is probably in R_LoadARBProgram which is not used by the GLSL backend at all since it only handles the interaction shaders atm. it does have its own version of this function but it is lobotomized and only used if for some reason a user wants to use an external shader.it is here in draw_arb2.cpp ->
/*
==================
GL_GetGLSLFromFile
==================
*/
static GLchar *GL_GetGLSLFromFile( GLchar *name ) {
idStr fullPath = "glprogs130/";
fullPath += name;
GLchar *fileBuffer;
GLchar *buffer;
if ( !glConfig.isInitialized ) {
return NULL;
}
fileSystem->ReadFile( fullPath.c_str(), reinterpret_cast<void **>( &fileBuffer ), NULL );
if ( !fileBuffer ) {
common->Printf( "%s: File not found, using internal shaders\n", fullPath.c_str() );
return NULL;
}
// copy to stack memory (do not use _alloca here!!! it breaks loading the shaders)
buffer = reinterpret_cast<GLchar *>( Mem_Alloc( strlen( fileBuffer ) + 1 ) );
strcpy( buffer, fileBuffer );
fileSystem->FreeFile( fileBuffer );
common->Printf( "%s: loaded\n", fullPath.c_str() );
return buffer;
}
as you can see it is somewhat more empty than the arb shaders version, but would probably suffice for the same thing.