Revelation Test

Discuss anything not covered by any of the other categories.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

And here is MH's hybrid ARB / GLSL renderer ->

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");
}
This is pretty much plug and pray :mrgreen:
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.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

These codebits plus a heavily modified version of MH's VBO code for Doom3 has gone into a new dhewm3 build.

I have not tried to rectify any bugs in dhewm3 (it should be pretty bug free by default), instead i just added many of my working codepieces.
Dhewm3 does have one bug though, the intro video does not play (but who cares :P).
Also the menu does not autoscale on widescreen monitors, theres a cvar for that, but its off by default.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

https://sourceforge.net/projects/cbadva ... z/download

so here it is, dhewm3 with some goodies like MH's hybrid GLSL / ARB interaction renderer, Better VBO code, Updated Targa loader (TGA2), SSE enhanced matrix operations, and better resampling.

The engine defaults to GLSL interactions (looks better), but if you want to use the old assembler shaders open the console and write R_useGLSL 0 then hit enter and then reloadARBprograms followed by enter again.

Unfortunatly Doom3 newer had the success as a modding engine that quake had, mostly because the mapping tools suck (they are pretty much neutered versions of the old idtech3 tools and buggy as hell).

ID actually admitted that they didnt use the tools themselves, but wrote most of the game in notepad of all things :shock:

Also if you want to use things like sikkmod you will have to rebuild the game dll's with dhewm's SDK, this also goes for any mod you want to play.
Productivity is a state of mind.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Re: Revelation Test

Post by toneddu2000 »

Do you know if Doom 3 has parallel shadow maps support?
Meadow Fun!! - my first commercial game, made with FTEQW game engine
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

vanilla does'nt, fhdoom has shadowmaps but im not sure if they are parallel.
It does use a new GLSL backend as well as framebuffers though :).

The only gripe atm. with fhdoom is that the expansion ressurection of evil cannot be played with it because the few special shaders it used are not yet ported to GLSL.
Someone with a good grip on GLSL should have no problem finishing that part (2 shaders need to be ported), unfortunatly that is not me heh.

The dhewm3 package i posted only uses GLSL for the interactions, besides shifting to half lambertian for better visuals.
The stencil shadows in it are still the plain old ARB based ones.

Atm im fighting a loosing battle with my old revelation doom3 because i lost some assets i had fixed, so im slowly trying to get around that.
Unfortunatly somewhere along the line i broke the expansion pack in revelation, flashlights go bonkers and some models like mc neil's draw inverted without bumpmaps :S
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

Decided to toy a bit with the source i based revelation on ( MHDoom ).

removed all my old hacked in BFG stuff like SSE intrinsics and replaced them with more vanilla friendly code.
reverted some mistakes i made which even though it did not break the engine was way wrong like using a version of _alloca alligned to 1 byte :S instead of just plain _alloca.
readded MH's hybrid GLSL ARB2 backend, this one was allways a little hairy since it was pretty fragile and some mods broke the interactions.
I spent several years trying to get it stable enough that it would newer break no matter what, and i think i succeded :).
The hybrid backend can even be used together with sikkmod since all sikkmods effects are loaded from materials.
One caveat though is that you cannot use sikkpins parallax shader since that one is an interaction shader and this engine defaults to GLSL interactions if your card supports it (all cards today do), you can however write one in GLSL and use that, this will probably also be better than using the old assembler based one.

added grebos fix for shadows causing triangles to go dark when compiling with msvc 2013 or later.

stencil shadow volumes still use ARB assembler, and i have no current plan on changing that since it will become a mess if this engine is to keep compatibility with older shaders.

If you still want the old parallax shaders, you will have to create a cvar for turning of the GLSL interactions.

code is here https://github.com/revelator/MHDoom
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

For those interrested in only the hybrid ARB2/GLSL backend, here is the final version ->

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 "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"

/* 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 = INVALID_PROGRAM;
static GLuint u_view_origin = INVALID_PROGRAM;

static GLuint u_color_modulate = INVALID_PROGRAM;
static GLuint u_color_add = INVALID_PROGRAM;

static GLuint u_constant_diffuse = INVALID_PROGRAM;
static GLuint u_constant_specular = INVALID_PROGRAM;

static GLuint u_diffMatrix = INVALID_PROGRAM;
static GLuint u_bumpMatrix = INVALID_PROGRAM;
static GLuint u_specMatrix = INVALID_PROGRAM;

static GLuint u_projMatrix = INVALID_PROGRAM;
static GLuint u_fallMatrix = INVALID_PROGRAM;

static GLuint rb_glsl_interaction_program = INVALID_PROGRAM;

/*
==================
RB_GLSL_MakeMatrix
==================
*/
static float *RB_GLSL_MakeMatrix( const float *in1 = 0, const float *in2 = 0, const float *in3 = 0, const float *in4 = 0 )
{
    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 ()

/*
=========================================================================================

GENERAL INTERACTION RENDERING

=========================================================================================
*/

/*
==================
RB_ARB2_BindTexture
==================
*/
void RB_ARB2_BindTexture( int unit, idImage *tex )
{
    backEnd.glState.currenttmu = unit;
    glActiveTextureARB( GL_TEXTURE0_ARB + unit );
    tex->BindFragment();
}

/*
==================
RB_ARB2_UnbindTexture
==================
*/
void RB_ARB2_UnbindTexture( int unit )
{
    backEnd.glState.currenttmu = unit;
    glActiveTextureARB( GL_TEXTURE0_ARB + unit );
    globalImages->BindNull();
}

/*
==================
RB_ARB2_BindInteractionTextureSet
==================
*/
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_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
    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 ) );

    /* 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:
        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
==================
*/
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() );
    }
    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:
        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
=============
*/
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 ), 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
=============
*/
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 );

    // enable the vertex arrays
    glEnableVertexAttribArrayARB( 8 );
    glEnableVertexAttribArrayARB( 9 );
    glEnableVertexAttribArrayARB( 10 );
    glEnableVertexAttribArrayARB( 11 );
    glEnableClientState( GL_COLOR_ARRAY );

    // check for enabled GLSL program first, if it fails go back to ARB
    if( rb_glsl_interaction_program != INVALID_PROGRAM )
    {
        // enable GLSL programs
        glUseProgram( rb_glsl_interaction_program );

        // 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 );
        }

        // back to fixed (or ARB program)
        glUseProgram( INVALID_PROGRAM );
    }
    else // Do it the old way
    {
        // enable ASM programs
        glEnable( GL_VERTEX_PROGRAM_ARB );
        glEnable( GL_FRAGMENT_PROGRAM_ARB );

        // 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 );
        }

        // bind the vertex program
        if( r_testARBProgram.GetBool() )
        {
            RB_ARB2_BindTexture( 6, globalImages->specular2DTableImage );

            glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
            glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
        }
        else
        {
            RB_ARB2_BindTexture( 6, globalImages->specularTableImage );

            glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
            glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
        }

        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 exacuted multiple
            // times with different colors and images if the surface or light have multiple layers
            RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
        }

        // need to disable ASM programs again
        glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NONE );
        glBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, VPROG_NONE );

		// back to fixed (or GLSL program)
        glDisable( GL_VERTEX_PROGRAM_ARB );
        glDisable( GL_FRAGMENT_PROGRAM_ARB );
    }

    // disable vertex arrays
    glDisableVertexAttribArrayARB( 8 );
    glDisableVertexAttribArrayARB( 9 );
    glDisableVertexAttribArrayARB( 10 );
    glDisableVertexAttribArrayARB( 11 );
    glDisableClientState( GL_COLOR_ARRAY );

    // disable features
    RB_ARB2_UnbindTexture( 6 );
    RB_ARB2_UnbindTexture( 5 );
    RB_ARB2_UnbindTexture( 4 );
    RB_ARB2_UnbindTexture( 3 );
    RB_ARB2_UnbindTexture( 2 );
    RB_ARB2_UnbindTexture( 1 );

    backEnd.glState.currenttmu = -1;
    GL_SelectTexture( 0 );
}

/*
==================
RB_ARB2_InteractionPass
==================
*/
void RB_ARB2_InteractionPass( const drawSurf_t *shadowSurfs, const drawSurf_t *lightSurfs )
{
	// these are allway's enabled since we do not yet use GLSL shaders for the shadows.
	glEnable( GL_VERTEX_PROGRAM_ARB );
	glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );

    // save on state changes by not bothering to setup/takedown all the messy states when there are no surfs to draw
    if( shadowSurfs )
    {
        RB_StencilShadowPass( shadowSurfs );
    }

    if( lightSurfs )
    {
        RB_ARB2_CreateDrawInteractions( lightSurfs );
    }

	// need to disable ASM programs again, we do not check for GLSL here since we do not use it for shadows.
	glBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NONE );
	glDisable( GL_VERTEX_PROGRAM_ARB );
}

/*
==================
RB_ARB2_DrawInteractions
==================
*/
void RB_ARB2_DrawInteractions( void )
{
    viewLight_t	*vLight;

    GL_SelectTexture( 0 );

	// ensure that GLSL is down comming in here.
	glUseProgram( INVALID_PROGRAM );

    // 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;
        }

        // 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;
    }

    // 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
// removed old invalid shaders, ARB2 is default nowadays and we override the interaction shaders with GLSL anyway if availiable.
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_LoadARBProgram
=================
*/
void R_LoadARBProgram( int progIndex )
{
    int		ofs;
    int		err;
    idStr	fullPath = "glprogs/";
    fullPath += progs[progIndex].name;
    char	*fileBuffer;
    char	*buffer;
    char	*start = '\0', *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 = static_cast<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( ( char * ) 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( ( char * ) 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 ), ( unsigned char * ) start );

    err = glGetError();
    glGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, ( GLint * ) &ofs );

    if( err == GL_INVALID_OPERATION )
    {
        const GLubyte *str = glGetString( GL_PROGRAM_ERROR_STRING_ARB );
        common->Warning( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str );

        if( ofs < 0 )
        {
            common->Warning( "GL_PROGRAM_ERROR_POSITION_ARB < 0 with error\n" );
        }
        else if( ofs >= ( int ) strlen( ( char * ) start ) )
        {
            common->Warning( "error at end of program\n" );
        }
        else
        {
            common->Warning( "error at %i:\n%s", ofs, start + ofs );
        }
        return;
    }

    if( ofs != -1 )
    {
        common->Warning( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error\n" );
        return;
    }
    common->Printf( "\n" );

    // need to strip the extension.
    fullPath.StripFileExtension();

    // output separated fragment / vertex shaders.
	if ( r_printGLProgs.GetBool() )
	{
		if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB )
		{
			fileSystem->WriteFile( ( fullPath + ".fp" ).c_str(), start, strlen( start ) );
		}
		else
		{
			fileSystem->WriteFile( ( fullPath + ".vp" ).c_str(), start, strlen( start ) );
		}
	}
}

/*
==================
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 );

    common->Printf( "Finding program %s\n", program );
    return progs[i].ident;
}

/*
==================
GL_GetShaderInfoLog
==================
*/
static void GL_GetShaderInfoLog( GLuint s, GLchar *src, bool isprog )
{
	static GLchar	infolog[4096];
	GLsizei			outlen = 0;

    infolog[0] = 0;

    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 )
    {
        GLint result = GL_FALSE;

        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 = vssrc ? glCreateShader( GL_VERTEX_SHADER ) : INVALID_PROGRAM;
    GLuint	fs = fssrc ? glCreateShader( GL_FRAGMENT_SHADER ) : INVALID_PROGRAM;

    glGetError();

    // vertex shader failed to compile
    if( vs && vssrc && !GL_CompileShader( vs, vssrc ) )
    {
		// delete the invalid vertex shader
		glDeleteShader( vs );

		// mark it as invalid
        return INVALID_PROGRAM;
    }

    // fragment shader failed to compile
    if( fs && fssrc && !GL_CompileShader( fs, fssrc ) )
    {
		// delete the invalid fragment shader
		glDeleteShader( fs );

		// mark it as invalid
        return INVALID_PROGRAM;
    }
    progid = glCreateProgram();

	if ( !progid )
	{
		// delete the invalid shader program
		glDeleteProgram( progid );

		// mark it as invalid
		return INVALID_PROGRAM;
	}

    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 *>( Mem_Alloc( strlen( fileBuffer ) + 1 ) );
    strcpy( buffer, fileBuffer );
    fileSystem->FreeFile( fileBuffer );

    common->Printf( "\n" );

    return buffer;
}

/*
==================
R_ReloadARBPrograms_f
==================
*/
void R_ReloadARBPrograms_f( const idCmdArgs &args )
{
    common->Printf( "----- R_ReloadARBPrograms -----\n" );

    for( int i = 0; progs[i].name[0]; i++ )
    {
        R_LoadARBProgram( i );
    }

    // load GLSL interaction programs if enabled
	if ( glConfig.ARBShadingLanguageAvailable && r_useGLSLInterActions.GetBool() )
    {
		common->Printf( "----- Using GLSL interactions (forced) -----\n" );

		// according to khronos this might not actually delete the shader program.
        glDeleteProgram( rb_glsl_interaction_program );

		// try to load from file, use internal shader if not available.
        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] ) );

		// free externally loaded vertex shader.
        if( vs != NULL )
        {
            Mem_Free( vs );
        }

		// free externally loaded fragment shader.
        if( fs != NULL )
        {
            Mem_Free( fs );
        }

		// if the shader did not run into problems load it up.
        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] ) );

			// set shader uniforms
            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" );
        }
        glUseProgram( INVALID_PROGRAM );
    }
    common->Printf( "-------------------------------\n" );
}

/*
==================
R_ARB2_Init
==================
*/
void R_ARB2_Init( void )
{
    glConfig.allowARB2Path = false;

    common->Printf( "---------- R_ARB2_Init ----------\n" );

    if( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable )
    {
        common->DWarning( "Not available.\n" );
        return;
    }
    common->Printf( "Available.\n" );
    common->Printf( "---------------------------------\n" );

    glConfig.allowARB2Path = true;
}

This works like a charm on unmodified Doom3 but i noticed that specular would sometimes fuck´up in places with lots of blood giving the blood a slightly blue hue.
This also happens with raynors GLSL backend if you try to do specular on ambient passes so he turned off drawing specular when in ambient.

you can switch effortlessly between GLSL and ARB2 now with r_useGLSLInterActions, no reloadARBPrograms needed anymore and the backend is a lot less clutttered now.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

Added the darkmod updates to support AVX and AVX2 cpu paths as well as the SMP changes for better multithreaded gaming.
Changes like the hybrid GLSL backend and SMP changes might also make it into dhewm3 officially after discussing it with daniel.

Removed the weird typeinfo program completely from sources, while a neat idea it was a pain in the behind to maintain because it broke more often than not.

Next on the table is updating the old openal and EAX to openal-soft and EFX for better support.
Im also looking at occlusion queries again since i recently got those working correctly in quake.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

Next in line was Doom's thread handler, Doom3 uses 2 worker threads. One for background downloads and one for the rest.
Doom3 has no thread cleanup, so if something shitty happens you might end up with it running in the background even if you closed the game, so lets do something about that.

Code: Select all

/*
==================
Sys_DestroyThread
==================
*/
void Sys_DestroyThread( xthreadInfo &info )
{
	DWORD	exitCode = 0;

	// must have a running thread handle
	if( !info.threadHandle )
	{
	    return;
	}
	Sys_EnterCriticalSection();

    // sleep a little
    Sleep( 3000 );

    // the thread handle is signaled - the thread has terminated
    if( GetExitCodeThread( ( HANDLE )info.threadHandle, &exitCode ) != 0 )
    {
        // handle error from GetExitCodeThread()...
        if( exitCode != STILL_ACTIVE )
        {
			// thread is no longer active
			Sys_LeaveCriticalSection();
			return;            
        }
    }

	// thread may be suspended, so resume before shutting down
	ResumeThread( ( HANDLE )info.threadHandle );

    // wait for the thread to exit
    if( WaitForSingleObject( ( HANDLE )info.threadHandle, 3000 ) == WAIT_TIMEOUT )
    {
		// force the thread to close
		TerminateThread( ( HANDLE )info.threadHandle, exitCode );

		// close handle and null it out
        if( CloseHandle( ( HANDLE )info.threadHandle ) != 0 )
        {
			Sys_LeaveCriticalSection();
			info.threadHandle = 0;
        }
    }
	Sys_LeaveCriticalSection();
}
This function was newer used in the win32 builds, so i took great care that it works as it should.

And here's the thread handler ->

Code: Select all

/*
===================
CreateThreadStartRoutine
===================
*/
typedef std::pair<xthread_t, LPVOID> CreateThreadStartParams;
DWORD WINAPI CreateThreadStartRoutine( LPVOID lpThreadParameter )
{
    std::pair<xthread_t, LPVOID> arg = *( ( CreateThreadStartParams * )lpThreadParameter );
    delete( ( CreateThreadStartParams * )lpThreadParameter );
    return arg.first( arg.second );
}

/*
==================
Sys_Createthread
==================
*/
void Sys_CreateThread( xthread_t function, LPVOID parms, xthreadPriority priority, xthreadInfo &info, const char *name, xthreadInfo *threads[MAX_THREADS], int *thread_count )
{
	Sys_EnterCriticalSection();

	// it is illegal to reinterpret cdecl function as stdcall function (in 32-bit case)
	// so we have to pass a helper function here, which would in turn call original callback
	LPVOID helperParam = new CreateThreadStartParams( function, parms );
	HANDLE temp = CreateThread( NULL,						// LPSECURITY_ATTRIBUTES lpsa,
	                            0,							// DWORD cbStack,
	                            CreateThreadStartRoutine,	// LPTHREAD_START_ROUTINE lpStartAddr,
	                            helperParam,				// LPVOID lpvThreadParm,
	                            0,							// DWORD fdwCreate,
	                            &info.threadId );
	info.threadHandle = ( uintptr_t ) temp;					// Was intptr_t...

	// uh oh ....
	if( !info.threadHandle )
	{
	    idLib::common->FatalError( "CreateThread() error is: %u\n", GetLastError() );
	}

	if( priority == THREAD_HIGHEST )
	{
		SetThreadPriority( ( HANDLE ) info.threadHandle, THREAD_PRIORITY_HIGHEST );		//  we better sleep enough to do this
	}
	else if( priority == THREAD_ABOVE_NORMAL )
	{
		SetThreadPriority( ( HANDLE ) info.threadHandle, THREAD_PRIORITY_ABOVE_NORMAL );
	}
	else if ( priority == THREAD_BELOW_NORMAL )
	{
		SetThreadPriority( ( HANDLE ) info.threadHandle, THREAD_PRIORITY_BELOW_NORMAL );
	}
	else if ( priority == THREAD_LOWEST )
	{
		SetThreadPriority( ( HANDLE ) info.threadHandle, THREAD_PRIORITY_LOWEST );
	}
	info.name = name;
	
	if( *thread_count < MAX_THREADS )
	{
		threads[( *thread_count )++] = &info;
	}
	else
	{
		common->Warning( "WARNING: MAX_THREADS reached\n" );
	}
	Sys_LeaveCriticalSection();
}
This is a modified version of darkmods function. Most of the thread priority stuff is not used but left in if someone wants to change that.

you need to include these two std headers for the thread wrapper ->
#include <utility>
#include <mutex>

So how do we use it ?

in void Sys_Error( const char *error, ... ) at the buttom just before exit(1); add Sys_DestroyThread( threadInfo );
and in void Sys_Quit( void ) at the buttom again just before ExitProcess(0); add Sys_DestroyThread( threadInfo );

This will kill all running threads at exit or fatal error where the engine will crash anyway but might leave a running thread.
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

Refined the thread exit code a bit so that it can newer exit a thread unless the handle is closed.

Running into a rather bizarre problem with mods that use sikkmods shaders, at first i thought it was caused by something in the hybrid GLSL renderer but reverting it showed the same symptoms.
Basically what happens is this decals go bad in some locations (see screenshot) and i cannot for the life of me figure out why since i havent touched the decal code :surprised:

https://ibb.co/p3ddKVm

if someone has any idea what might cause this, it would be a big help, since i made major strides porting darkmods SMP system and other enhancements netting a really nice fps increase on mods that use heavy shader operations like sikkmod (SSAO and softshadow are still major fps sinks but you can now have everything else on and still get allmost 60 fps :cool:
Productivity is a state of mind.
revelator
Posts: 2621
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Revelation Test

Post by revelator »

Hmm seems to be mirrors acting up as i noticed it only happens in places with loads of reflections :shock:
Went through the code with a toothcomb and there is no difference to original code so ugh...

Then i tried with an unmodified Doom3 and it still happens, so this seems to be something caused by AMD's drivers :evil:
Game works fine with mods that do not rely on sikkmods shaders.
The GLSL interactions also does make the game look better so maybe i can get away with just adding some bloom postprocessing to the backend (simple might actually be better in some cases).

Also removed a load of dead code in the compilers and render system (the compilers still had code for drawing opengl context which is moot since they use the games renderer now, and the renderer had unfinished thread code which would be nice, but probably better to just use the main thread handler for render context threads instead of having two sperate thread functions).
Productivity is a state of mind.
Post Reply