Forum

Doom 3 engine release and game code

Discuss programming topics for any language, any source base. If it is programming related but doesn't fit in one of the below categories, it goes here.

Moderator: InsideQC Admins

Re: Doom 3 engine release and game code

Postby revelator » Tue Nov 13, 2012 6:20 am

Btw if mh looks by i need his opinion on some hackery i cooked up.

Doom3 newer free's vbo's and while thats ok while the game runs it causes a memory leak at shutdown so i cooked up this.

Code: Select all
    // temp blocks are in a shared space that won't be freed
    if (block->tag != TAG_TEMP)
    {
      bool done = false;

        this->staticAllocTotal -= block->size;
        this->staticCountTotal--;

      // hack hack hack :) look ma no goto's
      if (block->vbo && !done)
      {
         // called at exit and only there.
         if (deleteAllBuffers)
         {
            glDeleteBuffers(1, &block->vbo);
            block->vbo = 0;
         }

         // mark it as done even though we dont free this.
         done = true;
      }
      else if (block->virtMem && !done)
      {
         // if we are a VBO this gets skipped.
         Mem_Free(block->virtMem);
         block->virtMem = NULL;
      }
    }



The deleteAllBuffers is just a boolean check which i set in idVertexCache::Init with its value false and again in idVertexCache::Shutdown() where i set it to true.

idVertexCache::Shutdown() only gets called at game shutdown and my hackery seems to work as the OpenGL debugger now states that the memory leak is no longer present :)
its a somewhat ugly way to do it though so i wanted your opinion.

Oh btw if you wonder about the done bool i use it as a replacement to get out of the function that was supposed to free vbo's looks a little less akward than an empty call heh :P
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby mh » Tue Nov 13, 2012 6:27 pm

VBOs should be automatically destroyed when the GL context is destroyed, so there's no need to free them. They're just GPU resources same as textures.

That's assuming that your driver is doing it's job right, of course.

On the other hand, if it improves the signal-to-noise ratio of any debugging tools you're using, then by all means do it.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Doom 3 engine release and game code

Postby revelator » Tue Nov 13, 2012 8:03 pm

Ok then :)

Im allmost certain i got the showel to the performance bug also but the bugger was not where i expected :!:

I had a look at some debug warnings and noticed the console spamming messages like empty filename whenever performance took a nosedive so i did some searching and i think i now understand why
using _malloca gave me atleast some fps increase. The function spamming that message uses _alloca to allocate messages but what happens when it tries to pass a NULL pointer (empty filename) to the stack :evil:
So i disabled the spammer and just skipped processing it in that case. Well the result was blowing my mind my FPS skyrocketed somewhat (200% increase) :shock: the funny thing is i newer touched that codepart before i noticed the spam so my other fixes actually worked, now color me pink and call me an elephant :oops:
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby nbohr1more » Tue Nov 13, 2012 8:50 pm

reckless wrote:Ok then :)

Im allmost certain i got the showel to the performance bug also but the bugger was not where i expected :!:

I had a look at some debug warnings and noticed the console spamming messages like empty filename whenever performance took a nosedive so i did some searching and i think i now understand why
using _malloca gave me atleast some fps increase. The function spamming that message uses _alloca to allocate messages but what happens when it tries to pass a NULL pointer (empty filename) to the stack :evil:
So i disabled the spammer and just skipped processing it in that case. Well the result was blowing my mind my FPS skyrocketed somewhat (200% increase) :shock: the funny thing is i newer touched that codepart before i noticed the spam so my other fixes actually worked, now color me pink and call me an elephant :oops:


Congrats!


Also, (sheepishly) did you get a change to merge those changes into the TDM source?

Finally, would you kindly post the files that were altered to fix your issue so I can bring this to the team as separate talking point?

Thanks.

:)
nbohr1more
 
Posts: 54
Joined: Fri Dec 09, 2011 7:04 am

Re: Doom 3 engine release and game code

Postby revelator » Tue Nov 13, 2012 10:02 pm

Still testing since it seems to break on some other stuff as well (models in d3xp spam quite a load of warnings).
Just got home today so that i even got this far means i must be speedy gonzales :P but not yet though ill take that on next.
Ill probaly post a diff against my old source since theres quite a few changes.

I noticed you used boost for some unix path magic in the DLL loader so heres a mkdir that also handles that :)

Code: Select all
/*
==============
Sys_Mkdir
==============
*/
void Sys_Mkdir(const char *path)
{
   int      ret;
   char   *dup, *c;

   ret = _mkdir(path);

   // if it allready exists no need to recreate it.
   if (!ret || (errno == EEXIST))
   {
      return;
   }
   dup = _strdup(path);

   c = dup;

   // unix path magic
   if (*c == '/')
   {
      c++;
   }

   while (*c)
   {
      if (*c == '/')
      {
         *c = '\0';

         ret = _mkdir(dup);

         if (ret && (errno != EEXIST))
         {
            Sys_Error("ERROR: Can not make path %s %s (%s)\n", dup, path, strerror(errno));
         }
         *c = '/';
      }
      c++;
   }
   ret = _mkdir(dup);

   if (ret && (errno != EEXIST))
   {
      Sys_Error("ERROR: Can not make path %s (%s)\n", dup, strerror(errno));
   }
}
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby revelator » Tue Nov 13, 2012 10:05 pm

Btw i updated the OpenAL / ogg and vorbis sources to latest versions in my engine :)
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby nbohr1more » Wed Nov 14, 2012 4:29 am

Thanks again.

I forgot to mention that read-only SVN access to the latest development builds is also available.

http://www.thedarkmod.com/downloads/

Though it would probably be best to pilot this stuff in the current release.

The mkdir changes you've done look familiar... I think one of the folks working on an OSX build mentioned something
similar...
nbohr1more
 
Posts: 54
Joined: Fri Dec 09, 2011 7:04 am

Re: Doom 3 engine release and game code

Postby revelator » Wed Nov 14, 2012 9:16 am

The mkdir function i posted been in my realm engine for ages :)
I think it originated from bengt jardrups quake port.
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby nbohr1more » Wed Nov 14, 2012 3:39 pm

Good deal.

Looks like one of the devs has been hankering to get rid of Boost dependencies because of all the problems that have cropped-up
in modern Linux distros. One down, ???# to go...
nbohr1more
 
Posts: 54
Joined: Fri Dec 09, 2011 7:04 am

Re: Doom 3 engine release and game code

Postby revelator » Wed Nov 14, 2012 6:37 pm

Probably preferable to use STLPort functions instead ?
Also one of the things im cleaning up as i go is the abundance of C type calls like (void *) and instead using real C++ like reinterpret_cast<void *>(somepointer);
some types have to be const_cast or static_cast but msvc is pretty good at pointing that out :)

As for STLPort i posted in another thread here the latest version patched for msvc2010 and 2012 ;) since the dev who maintains it have not gotten around to that yet.
It does work cause i built my Doom3 against it hehe.
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby revelator » Wed Nov 14, 2012 9:12 pm

Something else i been trying to wrap my head around.

Since i changed the old glVertexPointer glTexCoordPointer calls to use glVertexAttribPointer i had to do quite some guesswork as to the index locations.
For one glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->normal.ToFloatPtr()); in draw_common.cpp uses index 2 while it uses index 11 in draw_arb2.cpp
changing it to 11 works but i noticed something strange with reflections being REALLY reflective if i do that :lol:
So normals can have another index and reacts differently hum ??? now i know with glsl that i can get the correct index number via a glGet call but im not sure how to go about this with ARB assembly.

Ill only use this to get the known index numbers which i will then hardcode into an enumerator (should be somewhat faster) but i need a function that can do this.

these are the ones i been able to find so far.

glEnableVertexAttribArray(0); // vertex
glDisableVertexAttribArray(2); // also uses the normals for reflection
glDisableVertexAttribArray(3); // color
glEnableVertexAttribArray (8); // texcoord
glDisableVertexAttribArray(9); // tangents 0
glDisableVertexAttribArray(10); // tangents 1
glDisableVertexAttribArray(11); // normals

this is from the setup that works.
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby mh » Thu Nov 15, 2012 1:18 am

You need glBindAttribLocation: http://www.opengl.org/sdk/docs/man/xhtm ... cation.xml for GL 2.x support, or if you're aiming higher you can use "layout(location=" syntax in your shaders: http://www.opengl.org/wiki/Type_Qualifi ... bute_index

That's for GLSL. For ARB programs the mapping is well-defined in the GL_ARB_vertex_program spec: http://oss.sgi.com/projects/ogl-sample/ ... rogram.txt

Code: Select all
    Generic
    Attribute   Conventional Attribute       Conventional Attribute Command
    ---------   ------------------------     ------------------------------
         0      vertex position              Vertex
         1      vertex weights 0-3           WeightARB, VertexWeightEXT
         2      normal                       Normal
         3      primary color                Color
         4      secondary color              SecondaryColorEXT
         5      fog coordinate               FogCoordEXT
         6      -                            -
         7      -                            -
         8      texture coordinate set 0     MultiTexCoord(TEXTURE0, ...)
         9      texture coordinate set 1     MultiTexCoord(TEXTURE1, ...)
        10      texture coordinate set 2     MultiTexCoord(TEXTURE2, ...)
        11      texture coordinate set 3     MultiTexCoord(TEXTURE3, ...)
        12      texture coordinate set 4     MultiTexCoord(TEXTURE4, ...)
        13      texture coordinate set 5     MultiTexCoord(TEXTURE5, ...)
        14      texture coordinate set 6     MultiTexCoord(TEXTURE6, ...)
        15      texture coordinate set 7     MultiTexCoord(TEXTURE7, ...)
       8+n      texture coordinate set n     MultiTexCoord(TEXTURE0+n, ...)
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
User avatar
mh
 
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Doom 3 engine release and game code

Postby revelator » Thu Nov 15, 2012 7:38 am

so array index 11 is using normals as texture coordinates :?: and the higher numbered indexes are multitexture.
I seen an example where the glsl backend used offsetof to get the index number directly from the shader :)
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby revelator » Fri Nov 16, 2012 11:53 am

example of new layout.

i changed all the old C style casts to C++

vrefs = reinterpret_cast<vertRef_t **>(_alloca32(numVerts * sizeof(*vrefs)));

_alloca32 is my own implementation that uses SEH exceptions to try and avoid stack failures. Atm its a function but im working on turning it into a macro.

typecasting in C++ can be dangerous as opposed to C style casting it actually converts the return values to the type casted to, it seems to work allright though :)
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

Re: Doom 3 engine release and game code

Postby revelator » Fri Nov 16, 2012 11:37 pm

Might be of use to others so heres the shared path ARB2/GLSL backend in its current state.
Work originally by MH polished it a bit myself and it uses the new vertex attrib interface.

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 120\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

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

static GLuint u_light_origin = 0;
static GLuint u_view_origin = 0;

static GLuint u_color_modulate = 0;
static GLuint u_color_add = 0;

static GLuint u_constant_diffuse = 0;
static GLuint u_constant_specular = 0;

static GLuint u_diffMatrix = 0;
static GLuint u_bumpMatrix = 0;
static GLuint u_specMatrix = 0;

static GLuint u_projMatrix = 0;
static GLuint u_fallMatrix = 0;

static GLuint rb_glsl_interaction_program = 0;

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

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_CopyMemory

Major optimization yields about 10 fps+
==================
*/
static void *RB_GLSL_CopyMemory(void *dst, const void *src, size_t count)
{
    __asm
    {
        mov         esi, dword ptr [src]
        mov         edi, dword ptr [dst]

        cmp         dword ptr [count], 64
        jl         TryCopyQWord32

CopyQWord64:
        movq      mm0, [esi]
        movq      mm1, [esi + 8]
        movq      mm2, [esi + 16]
        movq      mm3, [esi + 24]
        movq      mm4, [esi + 32]
        movq      mm5, [esi + 40]
        movq      mm6, [esi + 48]
        movq      mm7, [esi + 56]
        add         esi, 64

        movntq      [edi], mm0
        movntq      [edi + 8], mm1
        movntq      [edi + 16], mm2
        movntq      [edi + 24], mm3
        movntq      [edi + 32], mm4
        movntq      [edi + 40], mm5
        movntq      [edi + 48], mm6
        movntq      [edi + 56], mm7
        add         edi, 64

        sub         dword ptr [count], 64
        cmp         dword ptr [count], 64
        jge         CopyQWord64

TryCopyQWord32:
        cmp         dword ptr [count], 32
        jl         TryCopyQWord16

CopyQWord32:
        movq      mm0, [esi]
        movq      mm1, [esi + 8]
        movq      mm2, [esi + 16]
        movq      mm3, [esi + 24]
        add         esi, 32

        movntq      [edi], mm0
        movntq      [edi + 8], mm1
        movntq      [edi + 16], mm2
        movntq      [edi + 24], mm3
        add         edi, 32

        sub         dword ptr [count], 32
        cmp         dword ptr [count], 32
        jge         CopyQWord32

TryCopyQWord16:
        cmp         dword ptr [count], 16
        jl         TryCopyQWord8

CopyQWord16:
        movq      mm0, [esi]
        movq      mm1, [esi + 8]
        add         esi, 16

        movntq      [edi], mm0
        movntq      [edi + 8], mm1
        add         edi, 16

        sub         dword ptr [count], 16
        cmp         dword ptr [count], 16
        jge         CopyQWord16

TryCopyQWord8:
        cmp         dword ptr [count], 8
        jl         TryCopyDWord

CopyQWord8:
        movq      mm0, [esi]
        add         esi, 8

        movntq      [edi], mm0
        add         edi, 8

        sub         dword ptr [count], 8
        cmp         dword ptr [count], 8
        jge         CopyQWord8

TryCopyDWord:
        cmp         dword ptr [count], 3
        jle         TryCopyWord

        mov         ecx, dword ptr [count]
        shr         ecx, 2
        mov         eax, ecx
        rep movsd

        shl         eax, 2
        sub         dword ptr [count], eax

TryCopyWord:
        cmp         dword ptr [count], 1
        jle         TryCopyByte

        movsw

        sub         dword ptr [count], 2

TryCopyByte:
        cmp         dword ptr [count], 0
        je         CopyDone

        movsb

CopyDone:
        emms
        sfence
        mov         eax, [dst]
    }
}

/*
==================
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)
    {
        RB_GLSL_CopyMemory(&m[0], in1, sizeof(float) * 4);
    }

    if (in2)
    {
        RB_GLSL_CopyMemory(&m[4], in2, sizeof(float) * 4);
    }

    if (in3)
    {
        RB_GLSL_CopyMemory(&m[8], in3, sizeof(float) * 4);
    }

    if (in4)
    {
        RB_GLSL_CopyMemory(&m[12], in4, sizeof(float) * 4);
    }
    return m;
}

#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 ()

/*
==================
RB_GLSL_DrawInteraction
==================
*/
static void RB_GLSL_DrawInteraction(const drawInteraction_t *din)
{
    static const float whalf[] = {0.0, 0.0, 0.0, 0.5};
    static const float wzero[] = {0.0, 0.0, 0.0, 0.0};
    static const float wone[] = {0.0, 0.0, 0.0, 1.0};

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

    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:
        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());
    }
    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
==================
*/
static void RB_ARB2_SharedSurfaceSetup(const drawSurf_t *surf)
{
    // set the vertex pointers
    idDrawVert *ac = (idDrawVert *) vertexCache.Position(surf->geo->ambientCache);
    glVertexAttribPointer(11, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->normal.ToFloatPtr());
    glVertexAttribPointer(10, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->tangents[1].ToFloatPtr());
    glVertexAttribPointer(9, 3, GL_FLOAT, false, sizeof(idDrawVert), ac->tangents[0].ToFloatPtr());
    glVertexAttribPointer(8, 2, GL_FLOAT, false, sizeof(idDrawVert), ac->st.ToFloatPtr());
    glVertexAttribPointer(3, 4, GL_UNSIGNED_BYTE, true, sizeof(idDrawVert), reinterpret_cast<void *>(&ac->color));
    glVertexAttribPointer(0, 3, GL_FLOAT, false, 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);

    // enable the vertex arrays
    glEnableVertexAttribArray(3);
    glEnableVertexAttribArray(8);
    glEnableVertexAttribArray(9);
    glEnableVertexAttribArray(10);
    glEnableVertexAttribArray(11);

    if (r_useGLSL.GetBool() && glConfig.ARBShadingLanguageAvailable && rb_glsl_interaction_program)
    {
        glUseProgram(rb_glsl_interaction_program);

        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(0);
    }
    else
    {
        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);
        }

        // texture 6 is the specular lookup table
        if (r_testARBProgram.GetBool())
        {
            RB_ARB2_BindTexture(6, globalImages->specular2DTableImage);
        }
        else
        {
            RB_ARB2_BindTexture(6, globalImages->specularTableImage);
        }

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

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

        // back to fixed (or GLSL program)
        glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);

        glDisable(GL_VERTEX_PROGRAM_ARB);
        glDisable(GL_FRAGMENT_PROGRAM_ARB);
    }
    glDisableVertexAttribArray(3);
    glDisableVertexAttribArray(8);
    glDisableVertexAttribArray(9);
    glDisableVertexAttribArray(10);
    glDisableVertexAttribArray(11);

    // 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
==================
*/
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 state when there are no surfs to draw
    if (shadowSurfs)
    {
        if (r_useShadowVertexProgram.GetBool())
        {
            glEnable(GL_VERTEX_PROGRAM_ARB);
            glBindProgramARB(GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW);

            RB_StencilShadowPass(shadowSurfs);

            // need to disable ASM programs when switching to GLSL
            if (r_useGLSL.GetBool() && glConfig.ARBShadingLanguageAvailable && rb_glsl_interaction_program)
            {
                glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
                glDisable(GL_VERTEX_PROGRAM_ARB);
            }
        }
        else
        {
            RB_StencilShadowPass(shadowSurfs);
        }
    }

    if (lightSurfs)
    {
        RB_ARB2_CreateDrawInteractions(lightSurfs);
    }
}

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

   // shut down texcoords
    GL_SelectTexture(0);
   glDisableVertexAttribArray(8);

    // 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->globalShadows &&
            !vLight->localShadows &&
            !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;
    }

    // ensure that these are down
    glBindProgramARB(GL_VERTEX_PROGRAM_ARB, 0);
    glDisable(GL_VERTEX_PROGRAM_ARB);
    glUseProgram(0);

    // disable stencil shadow test
    glStencilFunc(GL_ALWAYS, 128, 255);

   // fire up texcoords
   glEnableVertexAttribArray(8);
    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" },
    { GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
    { GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
    // additional programs can be dynamically specified in materials
};

/*
=================
R_LoadARBProgram
=================
*/
static void R_LoadARBProgram(int progIndex)
{
    int      ofs;
    int      err;
    idStr   fullPath = "glprogs/";
    fullPath += progs[progIndex].name;
    char   *fileBuffer;
    char   *buffer;
    char   *start, *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(), reinterpret_cast<void **>(&fileBuffer), NULL);

    if (!fileBuffer)
    {
        common->Printf(": File not found\n");
        return;
    }

    // copy to stack memory and free
    buffer = (char *) malloc(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)
    {
        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), (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->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((char *) start))
        {
            common->Printf("error at end of program\n");
        }
        else
        {
            common->Printf("error at %i:\n%s", ofs, start + ofs);
        }
        return;
    }

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

/*
==================
R_FindARBProgram

Returns a GL identifier that can be bound to the given target, parsing
a text file if it hasn't already been loaded.
==================
*/
int R_FindARBProgram(GLenum target, const char *program)
{
    int      i;
    idStr   stripped = program;

    stripped.StripFileExtension();

    // see if it is already loaded
    for (i = 0; progs[i].name[0]; i++)
    {
        if (progs[i].target != target)
        {
            continue;
        }
        idStr   compare = progs[i].name;
        compare.StripFileExtension();

        if (!idStr::Icmp(stripped.c_str(), compare.c_str()))
        {
            return progs[i].ident;
        }
    }

    if (i == MAX_GLPROGS)
    {
        common->Error("R_FindARBProgram: MAX_GLPROGS");
    }

    // add it to the list and load it
    progs[i].ident = (program_t) 0;   // will be gen'd by R_LoadARBProgram
    progs[i].target = target;
    strncpy(progs[i].name, program, sizeof(progs[i].name) - 1);

    R_LoadARBProgram(i);

    return progs[i].ident;
}

/*
==================
GL_GetShaderInfoLog
==================
*/
static void GL_GetShaderInfoLog(GLuint s, char *src, bool isprog)
{
    static char infolog[4096];
    int         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, char *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;
};

glsltable_t interactionAttribs[] =
{
    {8, "attrTexCoords"},
    {9, "attrTangents0"},
    {10, "attrTangents1"},
    {11, "attrNormal"}
};

/*
==================
GL_CreateGLSLProgram
==================
*/
static GLuint GL_CreateGLSLProgram(char *vssrc, char *fssrc, glsltable_t *attribs, int numattribs)
{
    GLuint   progid = 0;
    int      result = 0;
    GLenum   err = GL_NONE;

    GLuint vs = vssrc ? glCreateShader(GL_VERTEX_SHADER) : 0;
    GLuint fs = fssrc ? glCreateShader(GL_FRAGMENT_SHADER) : 0;

    glGetError();
   
    if (vs && vssrc && !GL_CompileShader(vs, vssrc))
    {
        return 0;
    }

    if (fs && fssrc && !GL_CompileShader(fs, fssrc))
    {
        return 0;
    }
    progid = glCreateProgram();

    if (vs && vssrc)
    {
        glAttachShader(progid, vs);
    }

    if (fs && fssrc)
    {
        glAttachShader(progid, fs);
    }

    if (attribs && numattribs)
    {
        for (int i = 0; i < numattribs; i++)
        {
            glBindAttribLocation(progid, attribs[i].slot, attribs[i].name);
         common->Printf("Occupied Attrib slot %u Attrib name %s\n", attribs[i].slot, attribs[i].name);
        }
    }
    glLinkProgram(progid);
    glGetProgramiv(progid, GL_LINK_STATUS, &result);

    glDeleteShader(vs);
    glDeleteShader(fs);

    if (result != GL_TRUE)
    {
        GL_GetShaderInfoLog(progid, "", true);
        return 0;
    }
    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, int numUniforms)
{
   // setup uniform locations - this is needed even on nvidia
   glUseProgram(progid);

   for (int i = 0; i < numUniforms; i++)
   {
      GLuint slot = glGetUniformLocation (progid, uniForms[i].name);
      glUniform1i (slot, uniForms[i].binding);
   }
}

/*
==================
GL_GetGLSLFromFile
==================
*/
static char *GL_GetGLSLFromFile(const char *name)
{
    idStr   fullPath = "glprogs/";
    fullPath += name;
    char   *fileBuffer;
    char   *buffer;

    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 main memory and free
    buffer = (char *) malloc(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);
    }
    common->Printf("-------------------------------\n");

    if (glConfig.ARBShadingLanguageAvailable)
    {
        // load GLSL programs
        glDeleteProgram(rb_glsl_interaction_program);

        char *vs = GL_GetGLSLFromFile("interaction_vs.glsl");
        char *fs = GL_GetGLSLFromFile("interaction_fs.glsl");

        rb_glsl_interaction_program = GL_CreateGLSLProgram(vs ? vs : interaction_vs, fs ? fs : interaction_fs, interactionAttribs, sizeof(interactionAttribs) / sizeof(interactionAttribs[0]));

        if (vs)
        {
            free(vs);
        }

        if (fs)
        {
            free(fs);
        }

        if (rb_glsl_interaction_program)
        {           
         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");
        }
        glUseProgram(0);
    }
}


tested and its just as fast as the normal ARB2 render backend :) ofc theres a few more changes to make this work in your own project like changing stuff to use vertex attrib pointers etc.
If you need any help getting it up and running ill try to pitch in.

You can run the GLSL backend without shaders as it has hardcoded defaults but if you want to toy with things its easier to use external ones.
Productivity is a state of mind.
User avatar
revelator
 
Posts: 2567
Joined: Thu Jan 24, 2008 12:04 pm
Location: inside tha debugger

PreviousNext

Return to General Programming

Who is online

Users browsing this forum: No registered users and 1 guest