/*
Copyright (C) 2001-2002 Charles Hollemeersch
Parhelia version Copyright (C) 2002-2003 Jarno Paananen

This program 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 2
of the License, or (at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

PENTA: the whole file is freakin penta...

Same as gl_bumpmap.c but Matrox Parhelia optimized 
These routines require 4 texture units and EXT_vertex_shader and
MTX_fragment shader extensions.

Most lights reqire 2 passes this way
1 diffuse
2 specular

If a light has a cubemap filter it requires 3 passes
1 attenuation
2 diffuse
3 specular
*/

#include "quakedef.h"

#include "glATI.h" // Yes, for EXT_vertex_shader

static GLuint vertex_shaders;
static GLuint fragment_shaders;
static GLuint lightPos, eyePos; // vertex shader constants

//#define PARHELIADEBUG

#if defined(PARHELIADEBUG) && defined(_WIN32)
#define Parhelia_checkerror() checkrealerror(__LINE__)
static void checkrealerror(int line)
{
    GLuint error = glGetError();
    if ( error != GL_NO_ERROR )
    {
        char    buf[128];
	wsprintf(buf, "ERROR (%08lx) on line %d\n", error, line );
	MessageBox( NULL, buf, "ERROR", MB_OK );
    }
}
#else

#define Parhelia_checkerror() do { } while(0)

#endif


// --- MTX_Fragment Shader Extension ---
#define GL_FRAGMENT_SHADER_MTX          0x8A80

// Get Parameters
#define GL_FS_BINDING_MTX               0x8A81
#define GL_MAX_TA_OPS_MTX               0x8A82
#define GL_OPT_TA_OPS_MTX               0x8A83
#define GL_TA_OPS_MTX                   0x8A84
#define GL_MAX_FP_OPS_MTX               0x8A85
#define GL_MAX_FP_TEXTURES_MTX          0x8A86
#define GL_MAX_FP_COLOURS_MTX           0x8A87
#define GL_OPT_FP_OPS_MTX               0x8A88
#define GL_OPT_FP_TEXTURES_MTX          0x8A89
#define GL_OPT_FP_COLOURS_MTX           0x8A8A
#define GL_FP_OPS_MTX                   0x8A8B
#define GL_FP_TEXTURES_MTX              0x8A8C
#define GL_FP_COLOURS_MTX               0x8A8D

// Texture Opcodes
#define GL_OP_TEX_NOP_MTX               0x8A8E
#define GL_OP_TEX_MTX                   0x8A8F
#define GL_OP_TEX_COORD_MTX             0x8A90
#define GL_OP_TEXKILL_GE_MTX            0x8A91
#define GL_OP_TEXKILL_LT_MTX            0x8A92

#define GL_OP_TEXBEM_MTX                0x8A93
#define GL_OP_TEXDEP_AR_MTX             0x8A94
#define GL_OP_TEXDEP_GB_MTX             0x8A95
#define GL_OP_TEXDEP_RGB_MTX            0x8A96
#define GL_OP_TEXDOT3_COORD_MTX         0x8A97
#define GL_OP_TEXDOT3_MTX               0x8A98

#define GL_OP_TEXM3X2_MTX               0x8A99
#define GL_OP_TEXM3X2_DEPTH_MTX         0x8A9A

#define GL_OP_TEXM3X3_MTX               0x8A9B
#define GL_OP_TEXM3X3_COORD_MTX         0x8A9C
#define GL_OP_TEXM3X3_REFLECT_MTX       0x8A9D

#define GL_TEXBEM_MATRIX_MTX            0x8A9E
#define GL_TEXTURE_FORMAT_MTX           0x8A9F

// Texture Formats
#define GL_TEX_UNSIGNED_MTX             0x8AC0
#define GL_TEX_SIGNED_MTX               0x8AC1
#define GL_TEX_BIASED_MTX               0x8AC2

// Hints
#define GL_HINT_NEAREST_MTX             0x0001
#define GL_HINT_BILINEAR_MTX            0x0002
#define GL_HINT_TRILINEAR_MTX           0x0004
#define GL_HINT_ANISOTROPIC_MTX         0x0008
#define GL_HINT_TEXTURE_1D_MTX          0x0010
#define GL_HINT_TEXTURE_2D_MTX          0x0020
#define GL_HINT_TEXTURE_3D_MTX          0x0040
#define GL_HINT_TEXTURE_CUBE_MTX        0x0080
#define GL_HINT_TEXTURE_BEML_MTX        0x0100
#define GL_HINT_TEXTURE_BEM_MTX         0x0200

// Colour Opcodes
#define GL_OP_MOV_MTX                   0x8AA0
#define GL_OP_ADD_MTX                   0x8AA1
#define GL_OP_SUB_MTX                   0x8AA2
#define GL_OP_MUL_MTX                   0x8AA3
#define GL_OP_DP3_MTX                   0x8AA4
#define GL_OP_DP4_MTX                   0x8AA5
#define GL_OP_MAD_MTX                   0x8AA6
#define GL_OP_LRP_MTX                   0x8AA7
#define GL_OP_CND_MTX                   0x8AA8
#define GL_OP_CMV_MTX                   0x8AA9
#define GL_OP_CMP_MTX                   0x8AAA

// Result
#define GL_COLOR0_MTX                   0x0B00 // GL_CURRENT_COLOR
#define GL_COLOR1_MTX                   0x8459 // GL_CURRENT_SECONDARY_COLOR
#define GL_TEXTURE0                     0x84C0 // GL_TEXTURE0_ARB
#define GL_TEXTURE1                     0x84C1 // GL_TEXTURE1_ARB
#define GL_TEXTURE2                     0x84C2 // GL_TEXTURE2_ARB
#define GL_TEXTURE3                     0x84C3 // GL_TEXTURE3_ARB
#define GL_TEMPORARY0_MTX               0x8AB0
#define GL_TEMPORARY1_MTX               0x8AB1
#define GL_IGNORED_MTX                  0x8AB2
#define GL_OUTPUT_COLOR_MTX             0x8AB3

// Target
#define GL_RESULT_MTX                   0x8AC3
#define GL_ARGUMENT0_MTX                0x8AC4
#define GL_ARGUMENT1_MTX                0x8AC5
#define GL_ARGUMENT2_MTX                0x8AC6

// Result Modifier
#define GL_SATURATE_MTX                 0x8AC7
#define GL_SCALE_MTX                    0x8AC8

// Arguments Modifier
#define GL_REPLICATE_MTX                0x8AC9
#define GL_CONVERT_MTX                  0x8ACA

#define GL_NONE_MTX                     0x8ACB

// GL_SCALE_MTX
#define GL_SCALE_X2_MTX                 0x8ACC
#define GL_SCALE_X4_MTX                 0x8ACD
#define GL_SCALE_X8_MTX                 0x8ACE
#define GL_SCALE_D2_MTX                 0x8ACF
#define GL_SCALE_D4_MTX                 0x8AD0

// GL_CONVERT_MTX
#define GL_ARG_INVERT_MTX               0x8AB4
#define GL_ARG_NEGATE_MTX               0x8AB5
#define GL_ARG_BIAS_MTX                 0x8AB6
#define GL_ARG_BIAS_NEGATE_MTX          0x8AB7
#define GL_ARG_SIGNED_MTX               0x8AB8
#define GL_ARG_SIGNED_NEGATE_MTX        0x8AB9

// GL_REPLICATE_MTX
#define GL_RED_MTX                      0x8ABA
#define GL_GREEN_MTX                    0x8ABB
#define GL_BLUE_MTX                     0x8ABC
#define GL_ALPHA_MTX                    0x8ABD


/* Fragment_Shader_ext */
typedef GLvoid    (APIENTRY * PFNGLBEGINFRAGSHADEREXTPROC)          ( GLvoid );
typedef GLvoid    (APIENTRY * PFNGLBINDFRAGSHADEREXTPROC)           ( GLuint id);
typedef GLvoid    (APIENTRY * PFNGLDELETEFRAGSHADERSEXTPROC)        ( GLsizei n, const GLuint *shaders );
typedef GLvoid    (APIENTRY * PFNGLENDFRAGSHADEREXTPROC)            ( GLvoid );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCALPHAOP1EXTPROC)         ( GLenum op, GLenum res, GLenum arg0 );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCALPHAOP2EXTPROC)         ( GLenum op, GLenum res, GLenum arg0, GLenum arg1 );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCALPHAOP3EXTPROC)         ( GLenum op, GLenum res, GLenum arg0, GLenum arg1, GLenum arg2 );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCOPPARAMMTXEXTPROC)       ( GLenum target, GLenum pname, GLenum param );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCRGBAOP1EXTPROC)          ( GLenum op, GLenum res, GLenum arg0 );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCRGBAOP2EXTPROC)          ( GLenum op, GLenum res, GLenum arg0, GLenum arg1 );
typedef GLvoid    (APIENTRY * PFNGLFRAGPROCRGBAOP3EXTPROC)          ( GLenum op, GLenum res, GLenum arg0, GLenum arg1, GLenum arg2 );
typedef GLuint    (APIENTRY * PFNGLGENFRAGPROCINVARIANTBVEXTPROC)   ( GLbyte* values);
typedef GLuint    (APIENTRY * PFNGLGENFRAGPROCINVARIANTDVEXTPROC)   ( GLdouble* values);
typedef GLuint    (APIENTRY * PFNGLGENFRAGPROCINVARIANTFVEXTPROC)   ( GLfloat* values);
typedef GLuint    (APIENTRY * PFNGLGENFRAGPROCINVARIANTIVEXTPROC)   ( GLint* values);
typedef GLuint    (APIENTRY * PFNGLGENFRAGPROCINVARIANTSVEXTPROC)   ( GLshort* values);
typedef GLuint    (APIENTRY * PFNGLGENFRAGSHADERSEXTPROC)           ( GLsizei n );
typedef GLboolean (APIENTRY * PFNGLISFRAGSHADEREXTPROC)             ( GLuint id );
typedef GLvoid    (APIENTRY * PFNGLSETFRAGPROCINVARIANTBVPROC)      ( GLuint id, const GLbyte* values );
typedef GLvoid    (APIENTRY * PFNGLSETFRAGPROCINVARIANTDVPROC)      ( GLuint id, const GLdouble* values );
typedef GLvoid    (APIENTRY * PFNGLSETFRAGPROCINVARIANTFVPROC)      ( GLuint id, const GLfloat* values );
typedef GLvoid    (APIENTRY * PFNGLSETFRAGPROCINVARIANTIVPROC)      ( GLuint id, const GLint* values );
typedef GLvoid    (APIENTRY * PFNGLSETFRAGPROCINVARIANTSVPROC)      ( GLuint id, const GLshort* values );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSHINTSEXTPROC)          ( GLenum target, GLbitfield hints );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSOP1EXTPROC)            ( GLenum op, GLenum arg0 );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSOP2EXTPROC)            ( GLenum op, GLenum arg0, GLenum arg1);
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSOP3EXTPROC)            ( GLenum op, GLenum arg0, GLenum arg1, GLenum arg2 );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSOP4EXTPROC)            ( GLenum op, GLenum arg0, GLenum arg1, GLenum arg2, GLenum arg3 );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSPARAMFVEXTPROC)        ( GLenum target, GLenum pname, const GLfloat* param );
typedef GLvoid    (APIENTRY * PFNGLTEXADDRESSPARAMUIEXTPROC)        ( GLenum target, GLenum pname, GLuint param );
typedef GLvoid    (APIENTRY * PFNGLGETFRAGPROCINVARIANTBVEXTPROC)   ( GLuint id, GLbyte* values);
typedef GLvoid    (APIENTRY * PFNGLGETFRAGPROCINVARIANTDVEXTPROC)   ( GLuint id, GLdouble* values);
typedef GLvoid    (APIENTRY * PFNGLGETFRAGPROCINVARIANTFVEXTPROC)   ( GLuint id, GLfloat* values);
typedef GLvoid    (APIENTRY * PFNGLGETFRAGPROCINVARIANTIVEXTPROC)   ( GLuint id, GLint* values);
typedef GLvoid    (APIENTRY * PFNGLGETFRAGPROCINVARIANTSVEXTPROC)   ( GLuint id, GLshort* values);
typedef GLvoid    (APIENTRY * PFNGLGETTEXADDRESSPARAMFVEXTPROC)     ( GLenum target, GLenum pname, GLfloat* param);
typedef GLvoid    (APIENTRY * PFNGLGETTEXADDRESSPARAMUIVEXTPROC)    ( GLenum target, GLenum pname, GLuint* param);


// --- End of MTX_Fragment Shader Extension ---

// Fragment Shader Extension Entry Points (pointers to the extension functions)
// ----------------------------------------------------------------------------

PFNGLBEGINFRAGSHADEREXTPROC qglBeginFragShaderMTX = NULL;
PFNGLBINDFRAGSHADEREXTPROC  qglBindFragShaderMTX = NULL;
PFNGLDELETEFRAGSHADERSEXTPROC qglDeleteFragShadersMTX = NULL;
PFNGLENDFRAGSHADEREXTPROC qglEndFragShaderMTX = NULL;
PFNGLFRAGPROCALPHAOP1EXTPROC qglFragProcAlphaOp1MTX = NULL;
PFNGLFRAGPROCALPHAOP2EXTPROC qglFragProcAlphaOp2MTX = NULL;
PFNGLFRAGPROCALPHAOP3EXTPROC qglFragProcAlphaOp3MTX = NULL;
PFNGLFRAGPROCOPPARAMMTXEXTPROC qglFragProcOpParamMTX = NULL;
PFNGLFRAGPROCRGBAOP1EXTPROC qglFragProcRGBAOp1MTX = NULL;
PFNGLFRAGPROCRGBAOP2EXTPROC qglFragProcRGBAOp2MTX = NULL;
PFNGLFRAGPROCRGBAOP3EXTPROC qglFragProcRGBAOp3MTX = NULL;
PFNGLGENFRAGPROCINVARIANTBVEXTPROC qglGenFragProcInvariantBVMTX = NULL;
PFNGLGENFRAGPROCINVARIANTDVEXTPROC qglGenFragProcInvariantDVMTX = NULL;
PFNGLGENFRAGPROCINVARIANTFVEXTPROC qglGenFragProcInvariantFVMTX = NULL;
PFNGLGENFRAGPROCINVARIANTIVEXTPROC qglGenFragProcInvariantIVMTX = NULL;
PFNGLGENFRAGPROCINVARIANTSVEXTPROC qglGenFragProcInvariantSVMTX = NULL;
PFNGLGENFRAGSHADERSEXTPROC qglGenFragShadersMTX = NULL;
PFNGLISFRAGSHADEREXTPROC qglIsFragShaderMTX = NULL;
PFNGLSETFRAGPROCINVARIANTBVPROC qglSetFragProcInvariantBVMTX = NULL;
PFNGLSETFRAGPROCINVARIANTDVPROC qglSetFragProcInvariantDVMTX = NULL;
PFNGLSETFRAGPROCINVARIANTFVPROC qglSetFragProcInvariantFVMTX = NULL;
PFNGLSETFRAGPROCINVARIANTIVPROC qglSetFragProcInvariantIVMTX = NULL;
PFNGLSETFRAGPROCINVARIANTSVPROC qglSetFragProcInvariantSVMTX = NULL;
PFNGLTEXADDRESSHINTSEXTPROC qglTexAddressHintsMTX = NULL;
PFNGLTEXADDRESSOP1EXTPROC qglTexAddressOp1MTX = NULL;
PFNGLTEXADDRESSOP2EXTPROC qglTexAddressOp2MTX = NULL;
PFNGLTEXADDRESSOP3EXTPROC qglTexAddressOp3MTX = NULL;
PFNGLTEXADDRESSOP4EXTPROC qglTexAddressOp4MTX = NULL;
PFNGLTEXADDRESSPARAMFVEXTPROC qglTexAddressParamFVMTX = NULL;
PFNGLTEXADDRESSPARAMUIEXTPROC qglTexAddressParamUIMTX = NULL;
PFNGLGETFRAGPROCINVARIANTBVEXTPROC qglGetFragProcInvariantBVMTX = NULL;
PFNGLGETFRAGPROCINVARIANTDVEXTPROC qglGetFragProcInvariantDVMTX = NULL;
PFNGLGETFRAGPROCINVARIANTFVEXTPROC qglGetFragProcInvariantFVMTX = NULL;
PFNGLGETFRAGPROCINVARIANTIVEXTPROC qglGetFragProcInvariantIVMTX = NULL;
PFNGLGETFRAGPROCINVARIANTSVEXTPROC qglGetFragProcInvariantSVMTX = NULL;
PFNGLGETTEXADDRESSPARAMFVEXTPROC qglGetTexAddressParamFVMTX = NULL;
PFNGLGETTEXADDRESSPARAMUIVEXTPROC qglGetTexAddressParamUIVMTX = NULL;

// EXT_vertex_shader stuff is in gl_bumpradeon.c
extern PFNGLBEGINVERTEXSHADEREXTPROC           qglBeginVertexShaderEXT;
extern PFNGLENDVERTEXSHADEREXTPROC             qglEndVertexShaderEXT;
extern PFNGLBINDVERTEXSHADEREXTPROC            qglBindVertexShaderEXT;
extern PFNGLGENVERTEXSHADERSEXTPROC            qglGenVertexShadersEXT;
extern PFNGLDELETEVERTEXSHADEREXTPROC          qglDeleteVertexShaderEXT;
extern PFNGLSHADEROP1EXTPROC                   qglShaderOp1EXT;
extern PFNGLSHADEROP2EXTPROC                   qglShaderOp2EXT;
extern PFNGLSHADEROP3EXTPROC                   qglShaderOp3EXT;
extern PFNGLSWIZZLEEXTPROC                     qglSwizzleEXT;
extern PFNGLWRITEMASKEXTPROC                   qglWriteMaskEXT;
extern PFNGLINSERTCOMPONENTEXTPROC             qglInsertComponentEXT;
extern PFNGLEXTRACTCOMPONENTEXTPROC            qglExtractComponentEXT;
extern PFNGLGENSYMBOLSEXTPROC                  qglGenSymbolsEXT;
extern PFNGLSETINVARIANTEXTPROC                qglSetInvariantEXT;
extern PFNGLSETLOCALCONSTANTEXTPROC            qglSetLocalConstantEXT;
extern PFNGLVARIANTBVEXTPROC                   qglVariantbvEXT;
extern PFNGLVARIANTSVEXTPROC                   qglVariantsvEXT;
extern PFNGLVARIANTIVEXTPROC                   qglVariantivEXT;
extern PFNGLVARIANTFVEXTPROC                   qglVariantfvEXT;
extern PFNGLVARIANTDVEXTPROC                   qglVariantdvEXT;
extern PFNGLVARIANTUBVEXTPROC                  qglVariantubvEXT;
extern PFNGLVARIANTUSVEXTPROC                  qglVariantusvEXT;
extern PFNGLVARIANTUIVEXTPROC                  qglVariantuivEXT;
extern PFNGLVARIANTPOINTEREXTPROC              qglVariantPointerEXT;
extern PFNGLENABLEVARIANTCLIENTSTATEEXTPROC    qglEnableVariantClientStateEXT;
extern PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC   qglDisableVariantClientStateEXT;
extern PFNGLBINDLIGHTPARAMETEREXTPROC          qglBindLightParameterEXT;
extern PFNGLBINDMATERIALPARAMETEREXTPROC       qglBindMaterialParameterEXT;
extern PFNGLBINDTEXGENPARAMETEREXTPROC         qglBindTexGenParameterEXT;
extern PFNGLBINDTEXTUREUNITPARAMETEREXTPROC    qglBindTextureUnitParameterEXT;
extern PFNGLBINDPARAMETEREXTPROC               qglBindParameterEXT;
extern PFNGLISVARIANTENABLEDEXTPROC            qglIsVariantEnabledEXT;
extern PFNGLGETVARIANTBOOLEANVEXTPROC          qglGetVariantBooleanvEXT;
extern PFNGLGETVARIANTINTEGERVEXTPROC          qglGetVariantIntegervEXT;
extern PFNGLGETVARIANTFLOATVEXTPROC            qglGetVariantFloatvEXT;
extern PFNGLGETVARIANTPOINTERVEXTPROC          qglGetVariantPointervEXT;
extern PFNGLGETINVARIANTBOOLEANVEXTPROC        qglGetInvariantBooleanvEXT;
extern PFNGLGETINVARIANTINTEGERVEXTPROC        qglGetInvariantIntegervEXT;
extern PFNGLGETINVARIANTFLOATVEXTPROC          qglGetInvariantFloatvEXT;
extern PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC    qglGetLocalConstantBooleanvEXT;
extern PFNGLGETLOCALCONSTANTINTEGERVEXTPROC    qglGetLocalConstantIntegervEXT;
extern PFNGLGETLOCALCONSTANTFLOATVEXTPROC      qglGetLocalConstantFloatvEXT;


void Parhelia_CreateShaders()
{
    GLuint mvp, modelview, zcomp, tempvec;
    GLuint texturematrix;
    GLuint vertex, normal;
    GLuint texcoord0;
    GLuint texcoord1;
    GLuint texcoord2;
    GLuint texcoord3;
    GLuint color;
    GLuint disttemp, disttemp2;
    GLuint fogstart, fogend;
    GLuint scaler;
    GLfloat scalervalues[4] = { 0.5, 0.5, 0.5, 0.5 };
    int err;

#if !defined(__APPLE__) && !defined (MACOSX)
    SAFE_GET_PROC( qglBeginVertexShaderEXT, PFNGLBEGINVERTEXSHADEREXTPROC, "glBeginVertexShaderEXT");            
    SAFE_GET_PROC( qglEndVertexShaderEXT, PFNGLENDVERTEXSHADEREXTPROC, "glEndVertexShaderEXT");          
    SAFE_GET_PROC( qglBindVertexShaderEXT, PFNGLBINDVERTEXSHADEREXTPROC, "glBindVertexShaderEXT");               
    SAFE_GET_PROC( qglGenVertexShadersEXT, PFNGLGENVERTEXSHADERSEXTPROC, "glGenVertexShadersEXT");               
    SAFE_GET_PROC( qglDeleteVertexShaderEXT, PFNGLDELETEVERTEXSHADEREXTPROC, "glDeleteVertexShaderEXT");         
    SAFE_GET_PROC( qglShaderOp1EXT, PFNGLSHADEROP1EXTPROC, "glShaderOp1EXT");            
    SAFE_GET_PROC( qglShaderOp2EXT, PFNGLSHADEROP2EXTPROC, "glShaderOp2EXT");            
    SAFE_GET_PROC( qglShaderOp3EXT, PFNGLSHADEROP3EXTPROC, "glShaderOp3EXT");            
    SAFE_GET_PROC( qglSwizzleEXT, PFNGLSWIZZLEEXTPROC, "glSwizzleEXT");          
    SAFE_GET_PROC( qglWriteMaskEXT, PFNGLWRITEMASKEXTPROC, "glWriteMaskEXT");            
    SAFE_GET_PROC( qglInsertComponentEXT, PFNGLINSERTCOMPONENTEXTPROC, "glInsertComponentEXT");          
    SAFE_GET_PROC( qglExtractComponentEXT, PFNGLEXTRACTCOMPONENTEXTPROC, "glExtractComponentEXT");               
    SAFE_GET_PROC( qglGenSymbolsEXT, PFNGLGENSYMBOLSEXTPROC, "glGenSymbolsEXT");         
    SAFE_GET_PROC( qglSetInvariantEXT, PFNGLSETINVARIANTEXTPROC, "glSetInvariantEXT");           
    SAFE_GET_PROC( qglSetLocalConstantEXT, PFNGLSETLOCALCONSTANTEXTPROC, "glSetLocalConstantEXT");               
    SAFE_GET_PROC( qglVariantbvEXT, PFNGLVARIANTBVEXTPROC, "glVariantbvEXT");            
    SAFE_GET_PROC( qglVariantsvEXT, PFNGLVARIANTSVEXTPROC, "glVariantsvEXT");            
    SAFE_GET_PROC( qglVariantivEXT, PFNGLVARIANTIVEXTPROC, "glVariantivEXT");            
    SAFE_GET_PROC( qglVariantfvEXT, PFNGLVARIANTFVEXTPROC, "glVariantfvEXT");            
    SAFE_GET_PROC( qglVariantdvEXT, PFNGLVARIANTDVEXTPROC, "glVariantdvEXT");            
    SAFE_GET_PROC( qglVariantubvEXT, PFNGLVARIANTUBVEXTPROC, "glVariantubvEXT");         
    SAFE_GET_PROC( qglVariantusvEXT, PFNGLVARIANTUSVEXTPROC, "glVariantusvEXT");         
    SAFE_GET_PROC( qglVariantuivEXT, PFNGLVARIANTUIVEXTPROC, "glVariantuivEXT");         
    SAFE_GET_PROC( qglVariantPointerEXT, PFNGLVARIANTPOINTEREXTPROC, "glVariantPointerEXT");             
    SAFE_GET_PROC( qglEnableVariantClientStateEXT, PFNGLENABLEVARIANTCLIENTSTATEEXTPROC, "glEnableVariantClientStateEXT");
    SAFE_GET_PROC( qglDisableVariantClientStateEXT, PFNGLDISABLEVARIANTCLIENTSTATEEXTPROC, "glDisableVariantClientStateEXT");
    SAFE_GET_PROC( qglBindLightParameterEXT, PFNGLBINDLIGHTPARAMETEREXTPROC, "glBindLightParameterEXT");
    SAFE_GET_PROC( qglBindMaterialParameterEXT, PFNGLBINDMATERIALPARAMETEREXTPROC, "glBindMaterialParameterEXT");
    SAFE_GET_PROC( qglBindTexGenParameterEXT, PFNGLBINDTEXGENPARAMETEREXTPROC, "glBindTexGenParameterEXT");
    SAFE_GET_PROC( qglBindTextureUnitParameterEXT, PFNGLBINDTEXTUREUNITPARAMETEREXTPROC, "glBindTextureUnitParameterEXT");
    SAFE_GET_PROC( qglBindParameterEXT, PFNGLBINDPARAMETEREXTPROC, "glBindParameterEXT");
    SAFE_GET_PROC( qglIsVariantEnabledEXT, PFNGLISVARIANTENABLEDEXTPROC, "glIsVariantEnabledEXT");
    SAFE_GET_PROC( qglGetVariantBooleanvEXT, PFNGLGETVARIANTBOOLEANVEXTPROC, "glGetVariantBooleanvEXT");
    SAFE_GET_PROC( qglGetVariantIntegervEXT, PFNGLGETVARIANTINTEGERVEXTPROC, "glGetVariantIntegervEXT");
    SAFE_GET_PROC( qglGetVariantFloatvEXT, PFNGLGETVARIANTFLOATVEXTPROC, "glGetVariantFloatvEXT");
    SAFE_GET_PROC( qglGetVariantPointervEXT, PFNGLGETVARIANTPOINTERVEXTPROC, "glGetVariantPointervEXT");
    SAFE_GET_PROC( qglGetInvariantBooleanvEXT, PFNGLGETINVARIANTBOOLEANVEXTPROC, "glGetInvariantBooleanvEXT");
    SAFE_GET_PROC( qglGetInvariantIntegervEXT, PFNGLGETINVARIANTINTEGERVEXTPROC, "glGetInvariantIntegervEXT");
    SAFE_GET_PROC( qglGetInvariantFloatvEXT, PFNGLGETINVARIANTFLOATVEXTPROC, "glGetInvariantFloatvEXT");
    SAFE_GET_PROC( qglGetLocalConstantBooleanvEXT, PFNGLGETLOCALCONSTANTBOOLEANVEXTPROC, "glGetLocalConstantBooleanvEXT");
    SAFE_GET_PROC( qglGetLocalConstantIntegervEXT, PFNGLGETLOCALCONSTANTINTEGERVEXTPROC, "glGetLocalConstantIntegervEXT");
    SAFE_GET_PROC( qglGetLocalConstantFloatvEXT, PFNGLGETLOCALCONSTANTFLOATVEXTPROC, "glGetLocalConstantFloatvEXT");

    SAFE_GET_PROC( qglBeginFragShaderMTX, PFNGLBEGINFRAGSHADEREXTPROC, "glBeginFragShaderMTX");
    SAFE_GET_PROC( qglBindFragShaderMTX, PFNGLBINDFRAGSHADEREXTPROC, "glBindFragShaderMTX");
    SAFE_GET_PROC( qglDeleteFragShadersMTX, PFNGLDELETEFRAGSHADERSEXTPROC, "glDeleteFragShadersMTX");
    SAFE_GET_PROC( qglEndFragShaderMTX, PFNGLENDFRAGSHADEREXTPROC, "glEndFragShaderMTX");
    SAFE_GET_PROC( qglFragProcAlphaOp1MTX, PFNGLFRAGPROCALPHAOP1EXTPROC, "glFragProcAlphaOp1MTX");
    SAFE_GET_PROC( qglFragProcAlphaOp2MTX, PFNGLFRAGPROCALPHAOP2EXTPROC, "glFragProcAlphaOp2MTX");
    SAFE_GET_PROC( qglFragProcAlphaOp3MTX, PFNGLFRAGPROCALPHAOP3EXTPROC, "glFragProcAlphaOp3MTX");
    SAFE_GET_PROC( qglFragProcOpParamMTX, PFNGLFRAGPROCOPPARAMMTXEXTPROC, "glFragProcOpParamMTX");
    SAFE_GET_PROC( qglFragProcRGBAOp1MTX, PFNGLFRAGPROCRGBAOP1EXTPROC, "glFragProcRGBAOp1MTX");
    SAFE_GET_PROC( qglFragProcRGBAOp2MTX, PFNGLFRAGPROCRGBAOP2EXTPROC, "glFragProcRGBAOp2MTX");
    SAFE_GET_PROC( qglFragProcRGBAOp3MTX, PFNGLFRAGPROCRGBAOP3EXTPROC, "glFragProcRGBAOp3MTX");
    SAFE_GET_PROC( qglGenFragProcInvariantBVMTX, PFNGLGENFRAGPROCINVARIANTBVEXTPROC, "glGenFragProcInvariantbvMTX");
    SAFE_GET_PROC( qglGenFragProcInvariantDVMTX, PFNGLGENFRAGPROCINVARIANTDVEXTPROC, "glGenFragProcInvariantdvMTX");
    SAFE_GET_PROC( qglGenFragProcInvariantFVMTX, PFNGLGENFRAGPROCINVARIANTFVEXTPROC, "glGenFragProcInvariantfvMTX");
    SAFE_GET_PROC( qglGenFragProcInvariantIVMTX, PFNGLGENFRAGPROCINVARIANTIVEXTPROC, "glGenFragProcInvariantivMTX");
    SAFE_GET_PROC( qglGenFragProcInvariantSVMTX, PFNGLGENFRAGPROCINVARIANTSVEXTPROC, "glGenFragProcInvariantsvMTX");
    SAFE_GET_PROC( qglGenFragShadersMTX, PFNGLGENFRAGSHADERSEXTPROC, "glGenFragShadersMTX");
    SAFE_GET_PROC( qglIsFragShaderMTX, PFNGLISFRAGSHADEREXTPROC, "glIsFragShaderMTX");
    SAFE_GET_PROC( qglSetFragProcInvariantBVMTX, PFNGLSETFRAGPROCINVARIANTBVPROC, "glSetFragProcInvariantbvMTX");
    SAFE_GET_PROC( qglSetFragProcInvariantDVMTX, PFNGLSETFRAGPROCINVARIANTDVPROC, "glSetFragProcInvariantdvMTX");
    SAFE_GET_PROC( qglSetFragProcInvariantFVMTX, PFNGLSETFRAGPROCINVARIANTFVPROC, "glSetFragProcInvariantfvMTX");
    SAFE_GET_PROC( qglSetFragProcInvariantIVMTX, PFNGLSETFRAGPROCINVARIANTIVPROC, "glSetFragProcInvariantivMTX");
    SAFE_GET_PROC( qglSetFragProcInvariantSVMTX, PFNGLSETFRAGPROCINVARIANTSVPROC, "glSetFragProcInvariantsvMTX");
    SAFE_GET_PROC( qglTexAddressHintsMTX, PFNGLTEXADDRESSHINTSEXTPROC, "glTexAddressHintsMTX");
    SAFE_GET_PROC( qglTexAddressOp1MTX, PFNGLTEXADDRESSOP1EXTPROC, "glTexAddressOp1MTX");
    SAFE_GET_PROC( qglTexAddressOp2MTX, PFNGLTEXADDRESSOP2EXTPROC, "glTexAddressOp2MTX");
    SAFE_GET_PROC( qglTexAddressOp3MTX, PFNGLTEXADDRESSOP3EXTPROC, "glTexAddressOp3MTX");
    SAFE_GET_PROC( qglTexAddressOp4MTX, PFNGLTEXADDRESSOP4EXTPROC, "glTexAddressOp4MTX");
    SAFE_GET_PROC( qglTexAddressParamFVMTX, PFNGLTEXADDRESSPARAMFVEXTPROC, "glTexAddressParamfvMTX");
    SAFE_GET_PROC( qglTexAddressParamUIMTX, PFNGLTEXADDRESSPARAMUIEXTPROC, "glTexAddressParamuiMTX");
    SAFE_GET_PROC( qglGetFragProcInvariantBVMTX, PFNGLGETFRAGPROCINVARIANTBVEXTPROC, "glGetFragProcInvariantbvMTX");
    SAFE_GET_PROC( qglGetFragProcInvariantDVMTX, PFNGLGETFRAGPROCINVARIANTDVEXTPROC, "glGetFragProcInvariantdvMTX");
    SAFE_GET_PROC( qglGetFragProcInvariantFVMTX, PFNGLGETFRAGPROCINVARIANTFVEXTPROC, "glGetFragProcInvariantfvMTX");
    SAFE_GET_PROC( qglGetFragProcInvariantIVMTX, PFNGLGETFRAGPROCINVARIANTIVEXTPROC, "glGetFragProcInvariantivMTX");
    SAFE_GET_PROC( qglGetFragProcInvariantSVMTX, PFNGLGETFRAGPROCINVARIANTSVEXTPROC, "glGetFragProcInvariantsvMTX");

    SAFE_GET_PROC( qglGetTexAddressParamFVMTX, PFNGLGETTEXADDRESSPARAMFVEXTPROC, "glGetTexAddressParamfvMTX");
    SAFE_GET_PROC( qglGetTexAddressParamUIVMTX, PFNGLGETTEXADDRESSPARAMUIVEXTPROC, "glGetTexAddressParamuivMTX");
#endif

    //DEBUG
    // Con_Printf("Errors before creating shaders...\n%s\n",gluErrorString(glGetError()));
    // Con_Printf("----Fragment Shaders----\n");

	
    // Generate two shaders, diffuse and specular, with two variants for cube and 3d atten texture
    glEnable(GL_FRAGMENT_SHADER_MTX);
    
    //DEBUG
    // Con_Printf("Enabled GL_MTX_FRAGMENT_SHADER...\n%s\n",gluErrorString(glGetError()));


    fragment_shaders = qglGenFragShadersMTX(4);
    
    /*
      if (fragment_shaders == 0)
      {
      Con_Printf("Error: unable to allocate shaders: %s\n", gluErrorString(glGetError()));
      } else
      {
      Con_Printf("Allocated 4 fragment shaders\n");
      };	
    */


    // Diffuse shader 1 (3D atten)
    qglBindFragShaderMTX(fragment_shaders);
	
    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders,gluErrorString(glGetError()));


    
    qglBeginFragShaderMTX();
    
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX); //Matrox Work Around

    // tex t0
    qglTexAddressHintsMTX( GL_TEXTURE0, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE0, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE0 );

    // tex t1
    qglTexAddressHintsMTX( GL_TEXTURE1, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE1, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE1 );

    // tex t2
    qglTexAddressHintsMTX( GL_TEXTURE2, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE2, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE2 );

    // tex t3 (3D)
    qglTexAddressHintsMTX( GL_TEXTURE3, GL_HINT_TEXTURE_3D_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE3, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE3 );

    //dp3_sat r0, t0_bx2, t1_bx2
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_DP3_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE0, GL_TEXTURE1);

    //mul r0.rgb, r0, t2
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE2);
    
    //+add_x4 r1.a, t0_bx2.b, t0_bx2.b
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SCALE_MTX, GL_SCALE_X4_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_ADD_MTX, GL_TEMPORARY1_MTX, GL_TEXTURE0, GL_TEXTURE0);
    
    //mul r0, r0, v0
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_COLOR0_MTX);
    
    //mul r0, r0, t3
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE3);
    
    //mul r0, r0, r1.a
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_OUTPUT_COLOR_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX);
    
    qglEndFragShaderMTX();
    
    // DEBUG - display status of shader creation
    // Con_Printf("Diffuse 1 Status: %s\n",gluErrorString(glGetError()));


    // Diffuse shader 2 (cube atten)
    qglBindFragShaderMTX(fragment_shaders+1);

    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders+1,gluErrorString(glGetError()));

    qglBeginFragShaderMTX();
    
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX); //Matrox Work Around
    // tex t0
    qglTexAddressHintsMTX( GL_TEXTURE0, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE0, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE0 );

    // tex t1
    qglTexAddressHintsMTX( GL_TEXTURE1, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE1, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE1 );

    // tex t2
    qglTexAddressHintsMTX( GL_TEXTURE2, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE2, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE2 );

    // tex t3 (cube)
    qglTexAddressHintsMTX( GL_TEXTURE3, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE3, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE3 );

    //dp3_sat r0, t0_bx2, t1_bx2
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_DP3_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE0, GL_TEXTURE1);

    //mul r0.rgb, r0, t2
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE2);

    //+add_x4 r1.a, t0_bx2.b, t0_bx2.b
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SCALE_MTX, GL_SCALE_X4_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_ADD_MTX, GL_TEMPORARY1_MTX, GL_TEXTURE0, GL_TEXTURE0);

    //mul r0, r0, v0
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_COLOR0_MTX);

    //mul r0, r0, t3
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE3);

    //mul r0, r0, r1.a
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEMPORARY0_MTX); // [JPS] insert this
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_OUTPUT_COLOR_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX);

    qglEndFragShaderMTX();

    // DEBUG - display status of shader creation
    // Con_Printf("Diffuse 2 Status: %s\n",gluErrorString(glGetError()));

    // Specular shader 1 (3D atten)
    qglBindFragShaderMTX(fragment_shaders+2);
	
    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders+2,gluErrorString(glGetError()));

    qglBeginFragShaderMTX();
    
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX); //Matrox Work Around

    // tex t0
    qglTexAddressHintsMTX( GL_TEXTURE0, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE0, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE0 );

    // tex t1
    qglTexAddressHintsMTX( GL_TEXTURE1, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE1, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE1 );

    // tex t2
    qglTexAddressHintsMTX( GL_TEXTURE2, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE2, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE2 );

    // tex t3 (3D)
    qglTexAddressHintsMTX( GL_TEXTURE3, GL_HINT_TEXTURE_3D_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE3, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE3 );

    //dp3_sat r0, t0_bx2, t1_bx2
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_DP3_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE0, GL_TEXTURE1);

    //mul r1.rgb, t3, t0.a
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEXTURE3, GL_TEXTURE0);

    //+mad_x2_sat r1.a, r0.b, r0.b, c0
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SCALE_MTX, GL_SCALE_X2_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT2_MTX, GL_CONVERT_MTX, GL_ARG_NEGATE_MTX);
    scaler = qglGenFragProcInvariantFVMTX(scalervalues);
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp3MTX(GL_OP_MAD_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, scaler);

    //mul r0, r1, v0
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX);

    //+mul r1.a, r1.a, r1.a
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX);

    //mul r1.a, r1.a, r1.a
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX);


    //mul r0, r0, r1.a
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_OUTPUT_COLOR_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX);

    qglEndFragShaderMTX();

    // DEBUG - display status of shader creation
    // Con_Printf("Specular 1 Status: %s\n",gluErrorString(glGetError()));

    // Specular shader 2 (cube atten)
    qglBindFragShaderMTX(fragment_shaders+3);

    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders+3,gluErrorString(glGetError()));

    qglBeginFragShaderMTX();
	
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX); //Matrox Work Around    
    // tex t0
    qglTexAddressHintsMTX( GL_TEXTURE0, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE0, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE0 );

    // tex t1
    qglTexAddressHintsMTX( GL_TEXTURE1, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE1, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE1 );

    // tex t2
    qglTexAddressHintsMTX( GL_TEXTURE2, GL_HINT_TEXTURE_2D_MTX|GL_HINT_TRILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE2, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE2 );

    // tex t3 (cube)
    qglTexAddressHintsMTX( GL_TEXTURE3, GL_HINT_TEXTURE_CUBE_MTX|GL_HINT_BILINEAR_MTX);
    qglTexAddressParamUIMTX( GL_TEXTURE3, GL_TEXTURE_FORMAT_MTX, GL_TEX_UNSIGNED_MTX);
    qglTexAddressOp1MTX( GL_OP_TEX_MTX, GL_TEXTURE3 );

    //dp3_sat r0, t0_bx2, t1_bx2
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_CONVERT_MTX, GL_ARG_SIGNED_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_DP3_MTX, GL_TEMPORARY0_MTX, GL_TEXTURE0, GL_TEXTURE1);


    //mul r1.rgb, t3, t0.a
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEXTURE3, GL_TEXTURE0);

    //+mad_x2_sat r1.a, r0.b, r0.b, c0
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SATURATE_MTX, GL_TRUE);
    qglFragProcOpParamMTX(GL_RESULT_MTX, GL_SCALE_MTX, GL_SCALE_X2_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT0_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_BLUE_MTX);
    qglFragProcOpParamMTX(GL_ARGUMENT2_MTX, GL_CONVERT_MTX, GL_ARG_NEGATE_MTX);
    scaler = qglGenFragProcInvariantFVMTX(scalervalues);
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp3MTX(GL_OP_MAD_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY0_MTX, scaler);

    //mul r0, r1, v0
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX, GL_COLOR0_MTX);

    //+mul r1.a, r1.a, r1.a
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX);
    
    //mul r1.a, r1.a, r1.a
    qglFragProcRGBAOp1MTX(GL_OP_MOV_MTX, GL_IGNORED_MTX, GL_TEXTURE0); // [JPS] insert this
    qglFragProcAlphaOp2MTX(GL_OP_MUL_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX, GL_TEMPORARY1_MTX);

    //mul r0, r0, r1.a
    qglFragProcOpParamMTX(GL_ARGUMENT1_MTX, GL_REPLICATE_MTX, GL_ALPHA_MTX);
    qglFragProcRGBAOp2MTX(GL_OP_MUL_MTX, GL_OUTPUT_COLOR_MTX, GL_TEMPORARY0_MTX, GL_TEMPORARY1_MTX);

    qglEndFragShaderMTX();
	
    // DEBUG - display status of shader creation
    // Con_Printf("Specular 2 Status: %s\n",gluErrorString(glGetError()));
	
    glDisable(GL_FRAGMENT_SHADER_MTX);
    // Parhelia_checkerror();
    // Con_Printf("----Vertex Shaders----\n");
    // Generate vertex shader
    glEnable(GL_VERTEX_SHADER_EXT);
		
		
    //DEBUG
    // Con_Printf("Enabled GL_VERTEX_SHADER_EXT...\n%s\n",gluErrorString(glGetError()));

    vertex_shaders = qglGenVertexShadersEXT(2);

    lightPos = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, 
				GL_FULL_RANGE_EXT, 1);
    eyePos = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, 
			      GL_FULL_RANGE_EXT, 1);

     	
    /*
    //DEBUG
    if (fragment_shaders == 0)
    {
    Con_Printf("Error: unable to allocate shaders: %s\n", gluErrorString(glGetError()));
    } else
    {
    Con_Printf("Allocated 1 vertex shader\n");
    };
    */

    qglBindVertexShaderEXT(vertex_shaders);
    qglBeginVertexShaderEXT();

    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders,gluErrorString(glGetError()));

    mvp           = qglBindParameterEXT( GL_MVP_MATRIX_EXT );
    modelview     = qglBindParameterEXT( GL_MODELVIEW_MATRIX );
    vertex        = qglBindParameterEXT( GL_CURRENT_VERTEX_EXT );
    normal        = qglBindParameterEXT( GL_CURRENT_NORMAL );
    color         = qglBindParameterEXT( GL_CURRENT_COLOR );
    texturematrix = qglBindTextureUnitParameterEXT( GL_TEXTURE3_ARB, GL_TEXTURE_MATRIX );
    texcoord0     = qglBindTextureUnitParameterEXT( GL_TEXTURE0_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord1     = qglBindTextureUnitParameterEXT( GL_TEXTURE1_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord2     = qglBindTextureUnitParameterEXT( GL_TEXTURE2_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord3     = qglBindTextureUnitParameterEXT( GL_TEXTURE3_ARB, GL_CURRENT_TEXTURE_COORDS );
    fogstart      = qglBindParameterEXT( GL_FOG_START );
    Parhelia_checkerror();
    fogend        = qglBindParameterEXT( GL_FOG_END );
    Parhelia_checkerror();


    disttemp      = qglGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    disttemp2     = qglGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    zcomp         = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    tempvec       = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);


    // Generates a necessary input for the diffuse bumpmapping registers

    // Transform vertex to view-space
    qglShaderOp2EXT( GL_OP_MULTIPLY_MATRIX_EXT, GL_OUTPUT_VERTEX_EXT, mvp, vertex );
    
    // Transform vertex by texture matrix and copy to output
    qglShaderOp2EXT( GL_OP_MULTIPLY_MATRIX_EXT, GL_OUTPUT_TEXTURE_COORD3_EXT, texturematrix, vertex );

    // copy tex coords of unit 0 to unit 2
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, texcoord0);
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD2_EXT, texcoord0);
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_COLOR0_EXT, color);

    // Transform vertex and take z for fog
    qglExtractComponentEXT( zcomp, modelview, 2);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, zcomp, vertex );

    // calculate fog values end - z and end - start
    qglShaderOp2EXT( GL_OP_SUB_EXT, disttemp, fogend, disttemp);
    qglShaderOp2EXT( GL_OP_SUB_EXT, disttemp2, fogend, fogstart);

    // divide end - z by end - start, that's it
    qglShaderOp1EXT( GL_OP_RECIP_EXT, disttemp2, disttemp2);
    qglShaderOp2EXT( GL_OP_MUL_EXT, GL_OUTPUT_FOG_EXT, disttemp, disttemp2);

    // calculate light position
    qglShaderOp2EXT( GL_OP_SUB_EXT, zcomp, lightPos, vertex);
    qglShaderOp2EXT( GL_OP_DOT3_EXT, disttemp, zcomp, zcomp);
    qglShaderOp1EXT( GL_OP_RECIP_SQRT_EXT, disttemp, disttemp);
    qglShaderOp2EXT( GL_OP_MUL_EXT, tempvec, zcomp, disttemp);

    // Normalized light vec now in tempvec, transform to tex1
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord1);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 0);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord2);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 1);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord3);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 2);

    qglEndVertexShaderEXT();


    // The same for specular
    qglBindVertexShaderEXT(vertex_shaders+1);
    qglBeginVertexShaderEXT();

    //DEBUG
    // Con_Printf("Shader %i Bind Status: %s\n",fragment_shaders,gluErrorString(glGetError()));

    mvp           = qglBindParameterEXT( GL_MVP_MATRIX_EXT );
    modelview     = qglBindParameterEXT( GL_MODELVIEW_MATRIX );
    vertex        = qglBindParameterEXT( GL_CURRENT_VERTEX_EXT );
    normal        = qglBindParameterEXT( GL_CURRENT_NORMAL );
    color         = qglBindParameterEXT( GL_CURRENT_COLOR );
    texturematrix = qglBindTextureUnitParameterEXT( GL_TEXTURE3_ARB, GL_TEXTURE_MATRIX );
    texcoord0     = qglBindTextureUnitParameterEXT( GL_TEXTURE0_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord1     = qglBindTextureUnitParameterEXT( GL_TEXTURE1_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord2     = qglBindTextureUnitParameterEXT( GL_TEXTURE2_ARB, GL_CURRENT_TEXTURE_COORDS );
    texcoord3     = qglBindTextureUnitParameterEXT( GL_TEXTURE3_ARB, GL_CURRENT_TEXTURE_COORDS );
    fogstart      = qglBindParameterEXT( GL_FOG_START );
    Parhelia_checkerror();
    fogend        = qglBindParameterEXT( GL_FOG_END );
    Parhelia_checkerror();


    disttemp      = qglGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    disttemp2     = qglGenSymbolsEXT(GL_SCALAR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    zcomp         = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);
    tempvec       = qglGenSymbolsEXT(GL_VECTOR_EXT, GL_LOCAL_EXT, GL_FULL_RANGE_EXT, 1);


    // Generates a necessary input for the diffuse bumpmapping registers

    // Transform vertex to view-space
    qglShaderOp2EXT( GL_OP_MULTIPLY_MATRIX_EXT, GL_OUTPUT_VERTEX_EXT, mvp, vertex );
    
    // Transform vertex by texture matrix and copy to output
    qglShaderOp2EXT( GL_OP_MULTIPLY_MATRIX_EXT, GL_OUTPUT_TEXTURE_COORD3_EXT, texturematrix, vertex );

    // copy tex coords of unit 0 to unit 2
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD0_EXT, texcoord0);
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_TEXTURE_COORD2_EXT, texcoord0);
    qglShaderOp1EXT( GL_OP_MOV_EXT, GL_OUTPUT_COLOR0_EXT, color);

    // Transform vertex and take z for fog
    qglExtractComponentEXT( zcomp, modelview, 2);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, zcomp, vertex );

    // calculate fog values end - z and end - start
    qglShaderOp2EXT( GL_OP_SUB_EXT, disttemp, fogend, disttemp);
    qglShaderOp2EXT( GL_OP_SUB_EXT, disttemp2, fogend, fogstart);

    // divide end - z by end - start, that's it
    qglShaderOp1EXT( GL_OP_RECIP_EXT, disttemp2, disttemp2);
    qglShaderOp2EXT( GL_OP_MUL_EXT, GL_OUTPUT_FOG_EXT, disttemp, disttemp2);

    // calculate halfvec
    qglShaderOp2EXT( GL_OP_SUB_EXT, tempvec, lightPos, vertex);
    qglShaderOp2EXT( GL_OP_DOT3_EXT, disttemp, tempvec, tempvec);
    qglShaderOp1EXT( GL_OP_RECIP_SQRT_EXT, disttemp, disttemp);
    qglShaderOp2EXT( GL_OP_MUL_EXT, tempvec, tempvec, disttemp);
    qglShaderOp2EXT( GL_OP_SUB_EXT, zcomp, eyePos, vertex);
    qglShaderOp2EXT( GL_OP_ADD_EXT, tempvec, tempvec, zcomp);
    qglShaderOp2EXT( GL_OP_DOT3_EXT, disttemp, tempvec, tempvec);
    qglShaderOp1EXT( GL_OP_RECIP_SQRT_EXT, disttemp, disttemp);
    qglShaderOp2EXT( GL_OP_MUL_EXT, tempvec, tempvec, disttemp);

    // Normalized half vec now in tempvec, transform to tex1
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord1);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 0);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord2);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 1);
    qglShaderOp2EXT( GL_OP_DOT4_EXT, disttemp, tempvec, texcoord3);
    qglInsertComponentEXT( GL_OUTPUT_TEXTURE_COORD1_EXT, disttemp, 2);

    qglEndVertexShaderEXT();



    // DEBUG - display status of shader creation
    // Con_Printf("Vertex Status: %s\n",gluErrorString(glGetError()));
        
		
    glDisable(GL_VERTEX_SHADER_EXT);
    // Parhelia_checkerror();

}

/*
  Pixel shader for diffuse bump mapping does diffuse bumpmapping with norm
  cube, self shadowing & dist attent in 1 pass
*/
void Parhelia_EnableDiffuseShader(const transform_t *tr, const lightobject_t *lo)
{
	float temp[4];

    //tex 0 = normal map
    //tex 1 = normalization cube map (tangent space light vector)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings but the
    //        actual register combiner setup does not change only the bound 
    //        texture)

    glEnable(GL_VERTEX_SHADER_EXT);
    qglBindVertexShaderEXT( vertex_shaders );
    temp[0] = currentshadowlight->origin[0];
    temp[1] = currentshadowlight->origin[1];
    temp[2] = currentshadowlight->origin[2];
    temp[3] = 1.0f;
    qglSetInvariantEXT(lightPos, GL_FLOAT, &temp[0]);

    temp[0] = r_refdef.vieworg[0];
    temp[1] = r_refdef.vieworg[1];
    temp[2] = r_refdef.vieworg[2];
    qglSetInvariantEXT(eyePos, GL_FLOAT, &temp[0]);
    Parhelia_checkerror();

    glEnable(GL_FRAGMENT_SHADER_MTX );

    GL_SelectTexture(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_CUBE_MAP_ARB);
    glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, normcube_texture_object);

    GL_SelectTexture(GL_TEXTURE2_ARB);
    glEnable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    if (currentshadowlight->filtercube)
    {
	glEnable(GL_TEXTURE_CUBE_MAP_ARB);
	glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, currentshadowlight->filtercube);
	GL_SetupCubeMapMatrix(tr);

        qglBindFragShaderMTX( fragment_shaders + 1 );
        Parhelia_checkerror();
      
    }
    else
    {
	glEnable(GL_TEXTURE_3D);
	glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);

	glTranslatef(0.5,0.5,0.5);
	glScalef(0.5,0.5,0.5);
        glScalef(1.0f/(currentshadowlight->radiusv[0]),
                 1.0f/(currentshadowlight->radiusv[1]),
	         1.0f/(currentshadowlight->radiusv[2]));
		GL_SetupAttenMatrix(tr);

        qglBindFragShaderMTX( fragment_shaders );
        Parhelia_checkerror();
    }

    GL_SelectTexture(GL_TEXTURE0_ARB);

}

void Parhelia_DisableDiffuseShader()
{
    //tex 0 = normal map
    //tex 1 = normalization cube map (tangent space light vector)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings)

    glDisable(GL_VERTEX_SHADER_EXT);
    glDisable(GL_FRAGMENT_SHADER_MTX );

    GL_SelectTexture(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_CUBE_MAP_ARB);

    GL_SelectTexture(GL_TEXTURE2_ARB);
    glDisable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    if (currentshadowlight->filtercube)
    {
	glDisable(GL_TEXTURE_CUBE_MAP_ARB);
    }
    else
    {
	glDisable(GL_TEXTURE_3D);
    }
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

    GL_SelectTexture(GL_TEXTURE0_ARB);
}

void Parhelia_EnableSpecularShader(const transform_t *tr, const lightobject_t *lo,
				   qboolean alias)
{
    vec3_t scaler = {0.5f, 0.5f, 0.5f};
	float temp[4];

    glEnable(GL_VERTEX_SHADER_EXT);
    qglBindVertexShaderEXT( vertex_shaders+1 );
    temp[0] = currentshadowlight->origin[0];
    temp[1] = currentshadowlight->origin[1];
    temp[2] = currentshadowlight->origin[2];
    temp[3] = 1.0f;
    qglSetInvariantEXT(lightPos, GL_FLOAT, &temp[0]);

    temp[0] = r_refdef.vieworg[0];
    temp[1] = r_refdef.vieworg[1];
    temp[2] = r_refdef.vieworg[2];
    qglSetInvariantEXT(eyePos, GL_FLOAT, &temp[0]);
    Parhelia_checkerror();

    glEnable(GL_FRAGMENT_SHADER_MTX );

    //tex 0 = normal map
    //tex 1 = normalization cube map (tangent space half angle)
    //tex 2 = color map
    //tex 3 = (attenuation or light filter, depends on light settings)

    GL_SelectTexture(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_CUBE_MAP_ARB);
    glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, normcube_texture_object);

    GL_SelectTexture(GL_TEXTURE2_ARB);
    glEnable(GL_TEXTURE_2D);

    GL_SelectTexture(GL_TEXTURE3_ARB);
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();

    if (currentshadowlight->filtercube)
    {
	glEnable(GL_TEXTURE_CUBE_MAP_ARB);
	glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, currentshadowlight->filtercube);
	GL_SetupCubeMapMatrix(tr);

        qglBindFragShaderMTX( fragment_shaders + 3 );
        Parhelia_checkerror();
    }
    else
    {
	glEnable(GL_TEXTURE_3D);
	glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);

	glTranslatef(0.5,0.5,0.5);
	glScalef(0.5,0.5,0.5);
        glScalef(1.0f/(currentshadowlight->radiusv[0]),
                 1.0f/(currentshadowlight->radiusv[1]),
	         1.0f/(currentshadowlight->radiusv[2]));
		GL_SetupAttenMatrix(tr);

        qglBindFragShaderMTX( fragment_shaders + 2 );
        Parhelia_checkerror();
    }

    GL_SelectTexture(GL_TEXTURE0_ARB);

}

void Parhelia_EnableAttentShader(const transform_t *tr)
{
    glMatrixMode(GL_TEXTURE);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(0.5,0.5,0.5);
    glScalef(0.5,0.5,0.5);
    glScalef(1.0f/(currentshadowlight->radiusv[0]),
             1.0f/(currentshadowlight->radiusv[1]),
             1.0f/(currentshadowlight->radiusv[2]));
	GL_SetupAttenMatrix(tr);
	
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_TEXTURE_3D);
    glBindTexture(GL_TEXTURE_3D, atten3d_texture_object);
}

void Parhelia_DisableAttentShader()
{
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_TEXTURE_3D);
    glEnable(GL_TEXTURE_2D);
}


/************************

Shader utility routines

*************************/

void Parhelia_SetupTcMod(tcmod_t *tc)
{
    switch (tc->type)
    {
    case TCMOD_ROTATE:
	glTranslatef(0.5,0.5,0.0);
	glRotatef(cl.time * tc->params[0],0,0,1);
	glTranslatef(-0.5, -0.5, 0.0);
	break;
    case TCMOD_SCROLL:
	glTranslatef(cl.time * tc->params[0], cl.time * tc->params[1], 0.0);
	break;
    case TCMOD_SCALE:
	glScalef(tc->params[0],tc->params[1],1.0);
	break;
    case TCMOD_STRETCH:
	//PENTA: fixme
	glScalef(1.0, 1.0, 1.0);
	break;
    }
}


void Parhelia_SetupSimpleStage(stage_t *s)
{
    tcmod_t *tc;
    int i;

    if (s->type != STAGE_SIMPLE)
    {
	Con_Printf("Non simple stage, in simple stage list");
	return;
    }

    glMatrixMode(GL_TEXTURE);
    glPushMatrix();

    for (i=0; i<s->numtcmods; i++)
    {
	Parhelia_SetupTcMod(&s->tcmods[i]);	
    }

    if (s->src_blend > -1)
    {
	glBlendFunc(s->src_blend, s->dst_blend);
	glEnable(GL_BLEND);
    }

    if (s->alphatresh > 0)
    {
	glEnable(GL_ALPHA_TEST);
	glAlphaFunc(GL_GREATER, s->alphatresh);
    }

    if ((s->numtextures > 0) && (s->texture[0]))
	GL_Bind(s->texture[0]->texnum);
}

/************************

Generic triangle list routines

*************************/

void FormatError(); // In gl_bumpgf.c

void Parhelia_sendTriangleListWV(const vertexdef_t *verts, int *indecies,
				 int numIndecies)
{

    glVertexPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    //draw them
    glDrawElements(GL_TRIANGLES, numIndecies, GL_UNSIGNED_INT, indecies);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void Parhelia_sendTriangleListTA(const vertexdef_t *verts, int *indecies,
				 int numIndecies)
{
    glVertexPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    if (!verts->texcoords)
	FormatError();
    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(2, GL_FLOAT, verts->texcoordstride, verts->texcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if (!verts->tangents)
	FormatError();
    qglClientActiveTextureARB(GL_TEXTURE1_ARB);
    glTexCoordPointer(3, GL_FLOAT, verts->tangentstride, verts->tangents);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if (!verts->binormals)
	FormatError();
    qglClientActiveTextureARB(GL_TEXTURE2_ARB);
    glTexCoordPointer(3, GL_FLOAT, verts->binormalstride, verts->binormals);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if (!verts->normals)
	FormatError();
    qglClientActiveTextureARB(GL_TEXTURE3_ARB);
    glTexCoordPointer(3, GL_FLOAT, verts->normalstride, verts->normals);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    //draw them
    glDrawElements(GL_TRIANGLES, numIndecies, GL_UNSIGNED_INT, indecies);

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE2_ARB);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE1_ARB);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    GL_SelectTexture(GL_TEXTURE0_ARB);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void Parhelia_drawTriangleListBump (const vertexdef_t *verts, int *indecies,
				    int numIndecies, shader_t *shader,
				    const transform_t *tr, const lightobject_t *lo)
{
    if (currentshadowlight->filtercube)
    {
	//draw attent into dest alpha
	GL_DrawAlpha();
	Parhelia_EnableAttentShader(tr);
	Parhelia_sendTriangleListWV(verts,indecies,numIndecies);
	Parhelia_DisableAttentShader();
	GL_ModulateAlphaDrawColor();
    }
    else
    {
	GL_AddColor();
    }
    glColor3fv(&currentshadowlight->color[0]);

    Parhelia_EnableSpecularShader(tr, lo, true);
    //bind the correct texture
    GL_SelectTexture(GL_TEXTURE0_ARB);
    if (shader->numbumpstages > 0)
	GL_Bind(shader->bumpstages[0].texture[0]->texnum);
    GL_SelectTexture(GL_TEXTURE2_ARB);
    if (shader->numcolorstages > 0)
	GL_Bind(shader->colorstages[0].texture[0]->texnum);

    Parhelia_sendTriangleListTA(verts,indecies,numIndecies);
    Parhelia_DisableDiffuseShader();

    Parhelia_EnableDiffuseShader(tr, lo);
    //bind the correct texture
    GL_SelectTexture(GL_TEXTURE0_ARB);
    if (shader->numbumpstages > 0)
	GL_Bind(shader->bumpstages[0].texture[0]->texnum);
    GL_SelectTexture(GL_TEXTURE2_ARB);
    if (shader->numcolorstages > 0)
	GL_Bind(shader->colorstages[0].texture[0]->texnum);

    Parhelia_sendTriangleListTA(verts,indecies,numIndecies);
    Parhelia_DisableDiffuseShader();
}

void Parhelia_drawTriangleListBase (vertexdef_t *verts, int *indecies,
				    int numIndecies, shader_t *shader,
                                    int lightMapIndex)
{
    int i;

    glVertexPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(2, GL_FLOAT, verts->texcoordstride, verts->texcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if (!shader->cull)
    { 
	glDisable(GL_CULL_FACE); 
    } 

	glColor3ub(255,255,255);

    for ( i = 0; i < shader->numstages; i++)
    {
	Parhelia_SetupSimpleStage(&shader->stages[i]);
	glDrawElements(GL_TRIANGLES,numIndecies,GL_UNSIGNED_INT,indecies);
	glPopMatrix();
    }
    glMatrixMode(GL_MODELVIEW);

    if (verts->colors && (shader->flags & SURF_PPLIGHT))
    {
	glColorPointer(3, GL_UNSIGNED_BYTE, verts->colorstride, verts->colors);
	glEnableClientState(GL_COLOR_ARRAY);
	glShadeModel(GL_SMOOTH);

	if (shader->numstages && shader->numcolorstages)
	{
	    if (shader->colorstages[0].src_blend >= 0)
	    {
		glEnable(GL_BLEND);
		glBlendFunc(shader->colorstages[0].src_blend,
			    shader->colorstages[0].dst_blend);
	    }
	    else
	    {
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_ONE);
	    }
	}
	else
	{
	    glDisable(GL_BLEND);
	}

	if (shader->numcolorstages)
	{
	    if (shader->colorstages[0].numtextures)
		GL_Bind(shader->colorstages[0].texture[0]->texnum);

	    if (shader->colorstages[0].alphatresh > 0)
	    {
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GEQUAL, shader->colorstages[0].alphatresh);
	    }	
	}

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

	glDrawElements(GL_TRIANGLES,numIndecies,GL_UNSIGNED_INT,indecies);

	glDisableClientState(GL_COLOR_ARRAY);
    }
    else if (shader->flags & SURF_PPLIGHT)
    {
	glColor3f(0,0,0);

		if (shader->colorstages[0].src_blend >= 0) {
			glBlendFunc(shader->colorstages[0].src_blend, shader->colorstages[0].dst_blend);
			glEnable(GL_BLEND);
		} else {
			glDisable(GL_BLEND);
		}

	glDisable(GL_TEXTURE_2D);
	glDrawElements(GL_TRIANGLES,numIndecies,GL_UNSIGNED_INT,indecies);
	glEnable(GL_TEXTURE_2D);
    }

    if (!shader->cull)
    { 
	glEnable(GL_CULL_FACE); 
    } 

    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);
}

/*************************

Generic world surfaces routines

**************************/

void Parhelia_sendSurfacesBase(msurface_t **surfs, int numSurfaces,
			       qboolean bindLightmap)
{
    int i;
    glpoly_t *p;
    msurface_t *surf;

    for ( i = 0; i < numSurfaces; i++)
    {
	surf = surfs[i];
	if (surf->visframe != r_framecount)
	    continue;
	p = surf->polys;
	if (bindLightmap)
	{
	    if (surf->lightmaptexturenum < 0)
		continue;
	    GL_Bind(lightmap_textures+surf->lightmaptexturenum);
	}
	glDrawElements(GL_TRIANGLES, p->numindecies, GL_UNSIGNED_INT,
		       &p->indecies[0]);
    }
}

void Parhelia_drawSurfaceListBase (vertexdef_t* verts, msurface_t** surfs,
				   int numSurfaces, shader_t* shader)
{
    int i;

    glVertexPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glTexCoordPointer(2, GL_FLOAT, verts->texcoordstride, verts->texcoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glColor3ub(255,255,255);

    if (!shader->cull)
    {
	glDisable(GL_CULL_FACE);
	//Con_Printf("Cullstuff %s\n",shader->name);
    }

    for (i = 0; i < shader->numstages; i++)
    {
	Parhelia_SetupSimpleStage(&shader->stages[i]);
	Parhelia_sendSurfacesBase(surfs, numSurfaces, false);
	glPopMatrix();
    }

    if (verts->lightmapcoords && (shader->flags & SURF_PPLIGHT))
    {
	qglClientActiveTextureARB(GL_TEXTURE1_ARB);
	glTexCoordPointer(2, GL_FLOAT, verts->lightmapstride,
			  verts->lightmapcoords);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	if (shader->numstages && shader->numcolorstages)
	{
	    if (shader->colorstages[0].src_blend >= 0)
	    {
		glEnable(GL_BLEND);
		glBlendFunc(shader->colorstages[0].src_blend,
			    shader->colorstages[0].dst_blend);
	    }
	    else
	    {
		glEnable(GL_BLEND);
		glBlendFunc(GL_ONE, GL_ONE);
	    }
	}
	else 
	{
	    glDisable(GL_BLEND);
	}

	if (shader->numcolorstages)
	{
	    if (shader->colorstages[0].numtextures)
		GL_Bind(shader->colorstages[0].texture[0]->texnum);

	    if (shader->colorstages[0].alphatresh > 0)
	    {
		glEnable(GL_ALPHA_TEST);
		glAlphaFunc(GL_GEQUAL, shader->colorstages[0].alphatresh);
	    }	
	}

	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	GL_EnableMultitexture();
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glColor3f(sh_lightmapbright.value, sh_lightmapbright.value,
		  sh_lightmapbright.value);

	Parhelia_sendSurfacesBase(surfs, numSurfaces, true);

	glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	GL_DisableMultitexture();
	qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    }

    if (!shader->cull)
    {
	glEnable(GL_CULL_FACE);
    }

    glDisable(GL_ALPHA_TEST);
    glMatrixMode(GL_MODELVIEW);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisable(GL_BLEND);
}

void Parhelia_sendSurfacesTA(msurface_t** surfs, int numSurfaces)
{
    int i,j;
    glpoly_t *p;
    msurface_t *surf;
    shader_t *shader, *lastshader;
    float *v;
    qboolean cull;
    lastshader = NULL;

    cull = true;
    for ( i = 0; i < numSurfaces; i++)
    {
	surf = surfs[i];
	if (surf->visframe != r_framecount)
	    continue;

	if (!(surf->flags & SURF_PPLIGHT))
	    continue;

	p = surf->polys;
		
	shader = surfs[i]->shader->shader;

	//less state changes
	if (lastshader != shader)
	{
	    if (!shader->cull)
	    {
		glDisable(GL_CULL_FACE);
		cull = false;
	    }
	    else
	    {
		if (!cull)
		    glEnable(GL_CULL_FACE);
		cull = true;
	    }
	    //bind the correct texture
	    GL_SelectTexture(GL_TEXTURE0_ARB);
	    if (shader->numbumpstages > 0)
		GL_Bind(shader->bumpstages[0].texture[0]->texnum);
	    GL_SelectTexture(GL_TEXTURE2_ARB);
	    if (shader->numcolorstages > 0)
		GL_Bind(shader->colorstages[0].texture[0]->texnum);
	    lastshader = shader;
	}

	//Note: texture coords out of begin-end are not a problem...
	qglMultiTexCoord3fvARB(GL_TEXTURE1_ARB, &surf->tangent[0]);
	qglMultiTexCoord3fvARB(GL_TEXTURE2_ARB, &surf->binormal[0]);
	qglMultiTexCoord3fvARB(GL_TEXTURE3_ARB, &surf->plane->normal[0]);
	/*
	  glBegin(GL_POLYGON);
	  v = (float *)(&globalVertexTable[surf->polys->firstvertex]);
	  for (j=0; j<p->numverts; j++, v+= VERTEXSIZE) {
	  qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
	  glVertex3fv(&v[0]);
	  }
	  glEnd();
	*/
	glDrawElements(GL_TRIANGLES, p->numindecies, GL_UNSIGNED_INT, &p->indecies[0]);
    }

    if (!cull)
	glEnable(GL_CULL_FACE);
}


void Parhelia_sendSurfacesPlain(msurface_t** surfs, int numSurfaces)
{
    int i,j;
    glpoly_t *p;
    msurface_t *surf;
    shader_t *shader, *lastshader;
    float *v;
    qboolean cull;
    lastshader = NULL;

    cull = true;

    for (i = 0; i < numSurfaces; i++)
    {
	surf = surfs[i];
		
	if (surf->visframe != r_framecount)
	    continue;

	if (!(surf->flags & SURF_PPLIGHT))
	    continue;

	p = surf->polys;

	shader = surf->shader->shader;

	//less state changes
	if (lastshader != shader)
	{
	    if (!shader->cull)
	    {
		glDisable(GL_CULL_FACE);
		cull = false;
	    }
	    else
	    {
		if (!cull)
		    glEnable(GL_CULL_FACE);
		cull = true;
	    }
	    lastshader = shader;
	}
	/*		
		glBegin(GL_POLYGON);
		v = (float *)(&globalVertexTable[surf->polys->firstvertex]);
		for (j=0; j<p->numverts; j++, v+= VERTEXSIZE) {
		//qglMultiTexCoord2fARB(GL_TEXTURE0_ARB, v[3], v[4]);
		glVertex3fv(&v[0]);
		}
		glEnd();
	*/
	glDrawElements(GL_TRIANGLES, p->numindecies, GL_UNSIGNED_INT,
		       &p->indecies[0]);
    }

    if (!cull)
	glEnable(GL_CULL_FACE);
}

void Parhelia_drawSurfaceListBump (vertexdef_t *verts, msurface_t **surfs,
				   int numSurfaces,const transform_t *tr, const lightobject_t *lo)
{
    glVertexPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
    glEnableClientState(GL_VERTEX_ARRAY);

    qglClientActiveTextureARB(GL_TEXTURE0_ARB);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if (currentshadowlight->filtercube)
    {
	//draw attent into dest alpha
	GL_DrawAlpha();
	Parhelia_EnableAttentShader(tr);
		
	glTexCoordPointer(3, GL_FLOAT, verts->vertexstride, verts->vertices);
	Parhelia_sendSurfacesPlain(surfs,numSurfaces);

	Parhelia_DisableAttentShader();
	GL_ModulateAlphaDrawColor();
    }
    else
    {
	GL_AddColor();
    }
    glColor3fv(&currentshadowlight->color[0]);

    Parhelia_EnableSpecularShader(tr, lo, true);

    glTexCoordPointer(2, GL_FLOAT, verts->texcoordstride, verts->texcoords);
    Parhelia_sendSurfacesTA(surfs,numSurfaces);
    Parhelia_DisableDiffuseShader();

    Parhelia_EnableDiffuseShader(tr, lo);
    Parhelia_sendSurfacesTA(surfs,numSurfaces);
    Parhelia_DisableDiffuseShader();

    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}


typedef struct allocchain_s
{
    struct allocchain_s* next;
    char data[1];//variable sized
} allocchain_t;

static allocchain_t* allocChain = NULL;

void* Parhelia_getDriverMem(size_t size, drivermem_t hint)
{
    allocchain_t *r = (allocchain_t *)malloc(size+sizeof(void *));
    r->next = allocChain;
    allocChain = r;
    return &r->data[0];
}

void Parhelia_freeAllDriverMem(void)
{
    allocchain_t *r = allocChain;
    allocchain_t *next;

    while (r)
    {
	next = r->next;
	free(r);
	r = next;
    }
}

void Parhelia_freeDriver(void)
{
    //nothing here...
}


void BUMP_InitParhelia(void)
{
    GLint errPos, errCode;
    const GLubyte *errString;

    if ( gl_cardtype != PARHELIA ) return;

    Parhelia_CreateShaders();

    //bind the correct stuff to the bump mapping driver
    gl_bumpdriver.drawSurfaceListBase = Parhelia_drawSurfaceListBase;
    gl_bumpdriver.drawSurfaceListBump = Parhelia_drawSurfaceListBump;
    gl_bumpdriver.drawTriangleListBase = Parhelia_drawTriangleListBase;
    gl_bumpdriver.drawTriangleListBump = Parhelia_drawTriangleListBump;
    gl_bumpdriver.getDriverMem = Parhelia_getDriverMem;
    gl_bumpdriver.freeAllDriverMem = Parhelia_freeAllDriverMem;
    gl_bumpdriver.freeDriver = Parhelia_freeDriver;
}