DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

RenderProgs_GLSL.cpp (43234B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 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.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../idlib/precompiled.h"
     31 
     32 #include "tr_local.h"
     33 
     34 idCVar r_skipStripDeadCode( "r_skipStripDeadCode", "0", CVAR_BOOL, "Skip stripping dead code" );
     35 idCVar r_useUniformArrays( "r_useUniformArrays", "1", CVAR_BOOL, "" );
     36 
     37 
     38 #define VERTEX_UNIFORM_ARRAY_NAME				"_va_"
     39 #define FRAGMENT_UNIFORM_ARRAY_NAME				"_fa_"
     40 
     41 static const int AT_VS_IN  = BIT( 1 );
     42 static const int AT_VS_OUT = BIT( 2 );
     43 static const int AT_PS_IN  = BIT( 3 );
     44 static const int AT_PS_OUT = BIT( 4 );
     45 
     46 struct idCGBlock {
     47 	idStr prefix;	// tokens that comes before the name
     48 	idStr name;		// the name
     49 	idStr postfix;	// tokens that comes after the name
     50 	bool used;		// whether or not this block is referenced anywhere
     51 };
     52 
     53 /*
     54 ================================================
     55 attribInfo_t
     56 ================================================
     57 */
     58 struct attribInfo_t {
     59 	const char *	type;
     60 	const char *	name;
     61 	const char *	semantic;
     62 	const char *	glsl;
     63 	int				bind;
     64 	int				flags;
     65 	int				vertexMask;
     66 };
     67 
     68 /*
     69 ================================================
     70 vertexMask_t
     71 
     72 NOTE: There is a PS3 dependency between the bit flag specified here and the vertex
     73 attribute index and attribute semantic specified in DeclRenderProg.cpp because the
     74 stored render prog vertexMask is initialized with cellCgbGetVertexConfiguration().
     75 The ATTRIB_INDEX_ defines are used to make sure the vertexMask_t and attrib assignment
     76 in DeclRenderProg.cpp are in sync.
     77 
     78 Even though VERTEX_MASK_XYZ_SHORT and VERTEX_MASK_ST_SHORT are not real attributes,
     79 they come before the VERTEX_MASK_MORPH to reduce the range of vertex program
     80 permutations defined by the vertexMask_t bits on the Xbox 360 (see MAX_VERTEX_DECLARATIONS).
     81 ================================================
     82 */
     83 enum vertexMask_t {
     84 	VERTEX_MASK_XYZ			= BIT( PC_ATTRIB_INDEX_VERTEX ),
     85 	VERTEX_MASK_ST			= BIT( PC_ATTRIB_INDEX_ST ),
     86 	VERTEX_MASK_NORMAL		= BIT( PC_ATTRIB_INDEX_NORMAL ),
     87 	VERTEX_MASK_COLOR		= BIT( PC_ATTRIB_INDEX_COLOR ),
     88 	VERTEX_MASK_TANGENT		= BIT( PC_ATTRIB_INDEX_TANGENT ),
     89 	VERTEX_MASK_COLOR2		= BIT( PC_ATTRIB_INDEX_COLOR2 ),
     90 };
     91 
     92 attribInfo_t attribsPC[] = {
     93 	// vertex attributes
     94 	{ "float4",		"position",		"POSITION",		"in_Position",			PC_ATTRIB_INDEX_VERTEX,			AT_VS_IN,		VERTEX_MASK_XYZ },
     95 	{ "float2",		"texcoord",		"TEXCOORD0",	"in_TexCoord",			PC_ATTRIB_INDEX_ST,				AT_VS_IN,		VERTEX_MASK_ST },
     96 	{ "float4",		"normal",		"NORMAL",		"in_Normal",			PC_ATTRIB_INDEX_NORMAL,			AT_VS_IN,		VERTEX_MASK_NORMAL },
     97 	{ "float4",		"tangent",		"TANGENT",		"in_Tangent",			PC_ATTRIB_INDEX_TANGENT,		AT_VS_IN,		VERTEX_MASK_TANGENT },
     98 	{ "float4",		"color",		"COLOR0",		"in_Color",				PC_ATTRIB_INDEX_COLOR,			AT_VS_IN,		VERTEX_MASK_COLOR },
     99 	{ "float4",		"color2",		"COLOR1",		"in_Color2",			PC_ATTRIB_INDEX_COLOR2,			AT_VS_IN,		VERTEX_MASK_COLOR2 },
    100 
    101 	// pre-defined vertex program output
    102 	{ "float4",		"position",		"POSITION",		"gl_Position",			0,	AT_VS_OUT,		0 },
    103 	{ "float",		"clip0",		"CLP0",			"gl_ClipDistance[0]",	0,	AT_VS_OUT,		0 },
    104 	{ "float",		"clip1",		"CLP1",			"gl_ClipDistance[1]",	0,	AT_VS_OUT,		0 },
    105 	{ "float",		"clip2",		"CLP2",			"gl_ClipDistance[2]",	0,	AT_VS_OUT,		0 },
    106 	{ "float",		"clip3",		"CLP3",			"gl_ClipDistance[3]",	0,	AT_VS_OUT,		0 },
    107 	{ "float",		"clip4",		"CLP4",			"gl_ClipDistance[4]",	0,	AT_VS_OUT,		0 },
    108 	{ "float",		"clip5",		"CLP5",			"gl_ClipDistance[5]",	0,	AT_VS_OUT,		0 },
    109 
    110 	// pre-defined fragment program input
    111 	{ "float4",		"position",		"WPOS",			"gl_FragCoord",			0,	AT_PS_IN,		0 },
    112 	{ "half4",		"hposition",	"WPOS",			"gl_FragCoord",			0,	AT_PS_IN,		0 },
    113 	{ "float",		"facing",		"FACE",			"gl_FrontFacing",		0,	AT_PS_IN,		0 },
    114 
    115 	// fragment program output
    116 	{ "float4",		"color",		"COLOR",		"gl_FragColor",		0,	AT_PS_OUT,		0 }, // GLSL version 1.2 doesn't allow for custom color name mappings
    117 	{ "half4",		"hcolor",		"COLOR",		"gl_FragColor",		0,	AT_PS_OUT,		0 },
    118 	{ "float4",		"color0",		"COLOR0",		"gl_FragColor",		0,	AT_PS_OUT,		0 },
    119 	{ "float4",		"color1",		"COLOR1",		"gl_FragColor",		1,	AT_PS_OUT,		0 },
    120 	{ "float4",		"color2",		"COLOR2",		"gl_FragColor",		2,	AT_PS_OUT,		0 },
    121 	{ "float4",		"color3",		"COLOR3",		"gl_FragColor",		3,	AT_PS_OUT,		0 },
    122 	{ "float",		"depth",		"DEPTH",		"gl_FragDepth",		4,	AT_PS_OUT,		0 },
    123 
    124 	// vertex to fragment program pass through
    125 	{ "float4",		"color",		"COLOR",		"gl_FrontColor",			0,	AT_VS_OUT,	0 },
    126 	{ "float4",		"color0",		"COLOR0",		"gl_FrontColor",			0,	AT_VS_OUT,	0 },
    127 	{ "float4",		"color1",		"COLOR1",		"gl_FrontSecondaryColor",	0,	AT_VS_OUT,	0 },
    128 
    129 
    130 	{ "float4",		"color",		"COLOR",		"gl_Color",				0,	AT_PS_IN,	0 },
    131 	{ "float4",		"color0",		"COLOR0",		"gl_Color",				0,	AT_PS_IN,	0 },
    132 	{ "float4",		"color1",		"COLOR1",		"gl_SecondaryColor",	0,	AT_PS_IN,	0 },
    133 
    134 	{ "half4",		"hcolor",		"COLOR",		"gl_Color",				0,	AT_PS_IN,		0 },
    135 	{ "half4",		"hcolor0",		"COLOR0",		"gl_Color",				0,	AT_PS_IN,		0 },
    136 	{ "half4",		"hcolor1",		"COLOR1",		"gl_SecondaryColor",	0,	AT_PS_IN,		0 },
    137 
    138 	{ "float4",		"texcoord0",	"TEXCOORD0_centroid",	"vofi_TexCoord0",	0,	AT_PS_IN,	0 },
    139 	{ "float4",		"texcoord1",	"TEXCOORD1_centroid",	"vofi_TexCoord1",	0,	AT_PS_IN,	0 },
    140 	{ "float4",		"texcoord2",	"TEXCOORD2_centroid",	"vofi_TexCoord2",	0,	AT_PS_IN,	0 },
    141 	{ "float4",		"texcoord3",	"TEXCOORD3_centroid",	"vofi_TexCoord3",	0,	AT_PS_IN,	0 },
    142 	{ "float4",		"texcoord4",	"TEXCOORD4_centroid",	"vofi_TexCoord4",	0,	AT_PS_IN,	0 },
    143 	{ "float4",		"texcoord5",	"TEXCOORD5_centroid",	"vofi_TexCoord5",	0,	AT_PS_IN,	0 },
    144 	{ "float4",		"texcoord6",	"TEXCOORD6_centroid",	"vofi_TexCoord6",	0,	AT_PS_IN,	0 },
    145 	{ "float4",		"texcoord7",	"TEXCOORD7_centroid",	"vofi_TexCoord7",	0,	AT_PS_IN,	0 },
    146 	{ "float4",		"texcoord8",	"TEXCOORD8_centroid",	"vofi_TexCoord8",	0,	AT_PS_IN,	0 },
    147 	{ "float4",		"texcoord9",	"TEXCOORD9_centroid",	"vofi_TexCoord9",	0,	AT_PS_IN,	0 },
    148 
    149 	{ "float4",		"texcoord0",	"TEXCOORD0",	"vofi_TexCoord0",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    150 	{ "float4",		"texcoord1",	"TEXCOORD1",	"vofi_TexCoord1",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    151 	{ "float4",		"texcoord2",	"TEXCOORD2",	"vofi_TexCoord2",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    152 	{ "float4",		"texcoord3",	"TEXCOORD3",	"vofi_TexCoord3",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    153 	{ "float4",		"texcoord4",	"TEXCOORD4",	"vofi_TexCoord4",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    154 	{ "float4",		"texcoord5",	"TEXCOORD5",	"vofi_TexCoord5",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    155 	{ "float4",		"texcoord6",	"TEXCOORD6",	"vofi_TexCoord6",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    156 	{ "float4",		"texcoord7",	"TEXCOORD7",	"vofi_TexCoord7",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    157 	{ "float4",		"texcoord8",	"TEXCOORD8",	"vofi_TexCoord8",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    158 	{ "float4",		"texcoord9",	"TEXCOORD9",	"vofi_TexCoord9",		0,	AT_VS_OUT | AT_PS_IN,	0 },
    159 
    160 	{ "half4",		"htexcoord0",	"TEXCOORD0",	"vofi_TexCoord0",		0,	AT_PS_IN,		0 },
    161 	{ "half4",		"htexcoord1",	"TEXCOORD1",	"vofi_TexCoord1",		0,	AT_PS_IN,		0 },
    162 	{ "half4",		"htexcoord2",	"TEXCOORD2",	"vofi_TexCoord2",		0,	AT_PS_IN,		0 },
    163 	{ "half4",		"htexcoord3",	"TEXCOORD3",	"vofi_TexCoord3",		0,	AT_PS_IN,		0 },
    164 	{ "half4",		"htexcoord4",	"TEXCOORD4",	"vofi_TexCoord4",		0,	AT_PS_IN,		0 },
    165 	{ "half4",		"htexcoord5",	"TEXCOORD5",	"vofi_TexCoord5",		0,	AT_PS_IN,		0 },
    166 	{ "half4",		"htexcoord6",	"TEXCOORD6",	"vofi_TexCoord6",		0,	AT_PS_IN,		0 },
    167 	{ "half4",		"htexcoord7",	"TEXCOORD7",	"vofi_TexCoord7",		0,	AT_PS_IN,		0 },
    168 	{ "half4",		"htexcoord8",	"TEXCOORD8",	"vofi_TexCoord8",		0,	AT_PS_IN,		0 },
    169 	{ "half4",		"htexcoord9",	"TEXCOORD9",	"vofi_TexCoord9",		0,	AT_PS_IN,		0 },
    170 	{ "float",		"fog",			"FOG",			"gl_FogFragCoord",		0,	AT_VS_OUT,		0 },
    171 	{ "float4",		"fog",			"FOG",			"gl_FogFragCoord",		0,	AT_PS_IN,		0 },
    172 	{ NULL,			NULL,			NULL,			NULL,					0,	0,				0 }
    173 };
    174 
    175 const char * types[] = {
    176 	"int",
    177 	"float",
    178 	"half",
    179 	"fixed",
    180 	"bool",
    181 	"cint",
    182 	"cfloat",
    183 	"void"
    184 };
    185 static const int numTypes = sizeof( types ) / sizeof( types[0] );
    186 
    187 const char * typePosts[] = {
    188 	"1", "2", "3", "4",
    189 	"1x1", "1x2", "1x3", "1x4",
    190 	"2x1", "2x2", "2x3", "2x4",
    191 	"3x1", "3x2", "3x3", "3x4",
    192 	"4x1", "4x2", "4x3", "4x4"
    193 };
    194 static const int numTypePosts = sizeof( typePosts ) / sizeof( typePosts[0] );
    195 
    196 const char * prefixes[] = {
    197 	"static",
    198 	"const",
    199 	"uniform",
    200 	"struct",
    201 
    202 	"sampler",
    203 
    204 	"sampler1D",
    205 	"sampler2D",
    206 	"sampler3D",
    207 	"samplerCUBE",
    208 
    209 	"sampler1DShadow",		// GLSL
    210 	"sampler2DShadow",		// GLSL
    211 	"sampler3DShadow",		// GLSL
    212 	"samplerCubeShadow",	// GLSL
    213 
    214 	"sampler2DMS",			// GLSL
    215 };
    216 static const int numPrefixes = sizeof( prefixes ) / sizeof( prefixes[0] );
    217 
    218 // For GLSL we need to have the names for the renderparms so we can look up their run time indices within the renderprograms
    219 static const char * GLSLParmNames[] = {
    220 	"rpScreenCorrectionFactor",
    221 	"rpWindowCoord",
    222 	"rpDiffuseModifier",
    223 	"rpSpecularModifier",
    224 
    225 	"rpLocalLightOrigin",
    226 	"rpLocalViewOrigin",
    227 
    228 	"rpLightProjectionS",
    229 	"rpLightProjectionT",
    230 	"rpLightProjectionQ",
    231 	"rpLightFalloffS",
    232 
    233 	"rpBumpMatrixS",
    234 	"rpBumpMatrixT",
    235 
    236 	"rpDiffuseMatrixS",
    237 	"rpDiffuseMatrixT",
    238 
    239 	"rpSpecularMatrixS",
    240 	"rpSpecularMatrixT",
    241 
    242 	"rpVertexColorModulate",
    243 	"rpVertexColorAdd",
    244 
    245 	"rpColor",
    246 	"rpViewOrigin",
    247 	"rpGlobalEyePos",
    248 
    249 	"rpMVPmatrixX",
    250 	"rpMVPmatrixY",
    251 	"rpMVPmatrixZ",
    252 	"rpMVPmatrixW",
    253 
    254 	"rpModelMatrixX",
    255 	"rpModelMatrixY",
    256 	"rpModelMatrixZ",
    257 	"rpModelMatrixW",
    258 
    259 	"rpProjectionMatrixX",
    260 	"rpProjectionMatrixY",
    261 	"rpProjectionMatrixZ",
    262 	"rpProjectionMatrixW",
    263 
    264 	"rpModelViewMatrixX",
    265 	"rpModelViewMatrixY",
    266 	"rpModelViewMatrixZ",
    267 	"rpModelViewMatrixW",
    268 
    269 	"rpTextureMatrixS",
    270 	"rpTextureMatrixT",
    271 
    272 	"rpTexGen0S",
    273 	"rpTexGen0T",
    274 	"rpTexGen0Q",
    275 	"rpTexGen0Enabled",
    276 
    277 	"rpTexGen1S",
    278 	"rpTexGen1T",
    279 	"rpTexGen1Q",
    280 	"rpTexGen1Enabled",
    281 
    282 	"rpWobbleSkyX",
    283 	"rpWobbleSkyY",
    284 	"rpWobbleSkyZ",
    285 
    286 	"rpOverbright",
    287 	"rpEnableSkinning",
    288 	"rpAlphaTest"
    289 };
    290 
    291 /*
    292 ========================
    293 StripDeadCode
    294 ========================
    295 */
    296 idStr StripDeadCode( const idStr & in, const char * name ) {
    297 	if ( r_skipStripDeadCode.GetBool() ) {
    298 		return in;
    299 	}
    300 
    301 	//idLexer src( LEXFL_NOFATALERRORS );
    302 	idParser src( LEXFL_NOFATALERRORS );
    303 	src.LoadMemory( in.c_str(), in.Length(), name );
    304 	src.AddDefine("PC");
    305 
    306 	idList< idCGBlock, TAG_RENDERPROG > blocks;
    307 
    308 	blocks.SetNum( 100 );
    309 
    310 	idToken token;
    311 	while ( !src.EndOfFile() ) {
    312 		idCGBlock & block = blocks.Alloc();
    313 		// read prefix
    314 		while ( src.ReadToken( &token ) ) {
    315 			bool found = false;
    316 			for ( int i = 0; i < numPrefixes; i++ ) {
    317 				if ( token == prefixes[i] ) {
    318 					found = true;
    319 					break;
    320 				}
    321 			}
    322 			if ( !found ) {
    323 				for ( int i = 0; i < numTypes; i++ ) {
    324 					if ( token == types[i] ) {
    325 						found = true;
    326 						break;
    327 					}
    328 					int typeLen = idStr::Length( types[i] );
    329 					if ( token.Cmpn( types[i], typeLen ) == 0 ) {
    330 						for ( int j = 0; j < numTypePosts; j++ ) {
    331 							if ( idStr::Cmp( token.c_str() + typeLen, typePosts[j] ) == 0 ) {
    332 								found = true;
    333 								break;
    334 							}
    335 						}
    336 						if ( found ) {
    337 							break;
    338 						}
    339 					}
    340 				}
    341 			}
    342 			if ( found ) {
    343 				if ( block.prefix.Length() > 0 && token.WhiteSpaceBeforeToken() ) {
    344 					block.prefix += ' ';
    345 				}
    346 				block.prefix += token;
    347 			} else {
    348 				src.UnreadToken( &token );
    349 				break;
    350 			}
    351 		}
    352 		if ( !src.ReadToken( &token ) ) {
    353 			blocks.SetNum( blocks.Num() - 1 );
    354 			break;
    355 		}
    356 		block.name = token;
    357 
    358 		if ( src.PeekTokenString( "=" ) || src.PeekTokenString( ":" ) || src.PeekTokenString( "[" ) ) {
    359 			src.ReadToken( &token );
    360 			block.postfix = token;
    361 			while ( src.ReadToken( &token ) ) {
    362 				if ( token == ";" ) {
    363 					block.postfix += ';';
    364 					break;
    365 				} else {
    366 					if ( token.WhiteSpaceBeforeToken() ){
    367 						block.postfix += ' ';
    368 					}
    369 					block.postfix += token;
    370 				}
    371 			}
    372 		} else if ( src.PeekTokenString( "(" ) ) {
    373 			idStr parms, body;
    374 			src.ParseBracedSection( parms, -1, true, '(', ')' );
    375 			if ( src.CheckTokenString( ";" ) ) {
    376 				block.postfix = parms + ";";
    377 			} else {
    378 				src.ParseBracedSection( body, -1, true, '{', '}' );
    379 				block.postfix = parms + " " + body;
    380 			}
    381 		} else if ( src.PeekTokenString( "{" ) ) {
    382 			src.ParseBracedSection( block.postfix, -1, true, '{', '}' );
    383 			if ( src.CheckTokenString( ";" ) ) {
    384 				block.postfix += ';';
    385 			}
    386 		} else if ( src.CheckTokenString( ";" ) ) {
    387 			block.postfix = idStr( ';' );
    388 		} else {
    389 			src.Warning( "Could not strip dead code -- unknown token %s\n", token.c_str() );
    390 			return in;
    391 		}
    392 	}
    393 
    394 	idList<int, TAG_RENDERPROG> stack;
    395 	for ( int i = 0; i < blocks.Num(); i++ ) {
    396 		blocks[i].used = ( ( blocks[i].name == "main" )
    397 			|| blocks[i].name.Right( 4 ) == "_ubo"
    398 			);
    399 
    400 		if ( blocks[i].name == "include" ) {
    401 			blocks[i].used = true;
    402 			blocks[i].name = ""; // clear out the include tag
    403 		}
    404 
    405 		if ( blocks[i].used ) {
    406 			stack.Append( i );
    407 		}
    408 	}
    409 
    410 	while ( stack.Num() > 0 ) {
    411 		int i = stack[stack.Num() - 1];
    412 		stack.SetNum( stack.Num() - 1 );
    413 
    414 		idLexer src( LEXFL_NOFATALERRORS );
    415 		src.LoadMemory( blocks[i].postfix.c_str(), blocks[i].postfix.Length(), name );
    416 		while ( src.ReadToken( &token ) ) {
    417 			for ( int j = 0; j < blocks.Num(); j++ ) {
    418 				if ( !blocks[j].used ) {
    419 					if ( token == blocks[j].name ) {
    420 						blocks[j].used = true;
    421 						stack.Append( j );
    422 					}
    423 				}
    424 			}
    425 		}
    426 	}
    427 
    428 	idStr out;
    429 
    430 	for ( int i = 0; i < blocks.Num(); i++ ) {
    431 		if ( blocks[i].used ) {
    432 			out += blocks[i].prefix;
    433 			out += ' ';
    434 			out += blocks[i].name;
    435 			out += ' ';
    436 			out += blocks[i].postfix;
    437 			out += '\n';
    438 		}
    439 	}
    440 
    441 	return out;
    442 }
    443 
    444 struct typeConversion_t {
    445 	const char * typeCG;
    446 	const char * typeGLSL;
    447 } typeConversion[] = {
    448 	{ "void",				"void" },
    449 
    450 	{ "fixed",				"float" },
    451 
    452 	{ "float",				"float" },
    453 	{ "float2",				"vec2" },
    454 	{ "float3",				"vec3" },
    455 	{ "float4",				"vec4" },
    456 
    457 	{ "half",				"float" },
    458 	{ "half2",				"vec2" },
    459 	{ "half3",				"vec3" },
    460 	{ "half4",				"vec4" },
    461 
    462 	{ "int",				"int" },
    463 	{ "int2",				"ivec2" },
    464 	{ "int3",				"ivec3" },
    465 	{ "int4",				"ivec4" },
    466 
    467 	{ "bool",				"bool" },
    468 	{ "bool2",				"bvec2" },
    469 	{ "bool3",				"bvec3" },
    470 	{ "bool4",				"bvec4" },
    471 
    472 	{ "float2x2",			"mat2x2" },
    473 	{ "float2x3",			"mat2x3" },
    474 	{ "float2x4",			"mat2x4" },
    475 
    476 	{ "float3x2",			"mat3x2" },
    477 	{ "float3x3",			"mat3x3" },
    478 	{ "float3x4",			"mat3x4" },
    479 
    480 	{ "float4x2",			"mat4x2" },
    481 	{ "float4x3",			"mat4x3" },
    482 	{ "float4x4",			"mat4x4" },
    483 
    484 	{ "sampler1D",			"sampler1D" },
    485 	{ "sampler2D",			"sampler2D" },
    486 	{ "sampler3D",			"sampler3D" },
    487 	{ "samplerCUBE",		"samplerCube" },
    488 
    489 	{ "sampler1DShadow",	"sampler1DShadow" },
    490 	{ "sampler2DShadow",	"sampler2DShadow" },
    491 	{ "sampler3DShadow",	"sampler3DShadow" },
    492 	{ "samplerCubeShadow",	"samplerCubeShadow" },
    493 
    494 	{ "sampler2DMS",		"sampler2DMS" },
    495 
    496 	{ NULL, NULL }
    497 };
    498 
    499 const char * vertexInsert = {
    500 	"#version 150\n"
    501 	"#define PC\n"
    502 	"\n"
    503 	"float saturate( float v ) { return clamp( v, 0.0, 1.0 ); }\n"
    504 	"vec2 saturate( vec2 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    505 	"vec3 saturate( vec3 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    506 	"vec4 saturate( vec4 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    507 	"vec4 tex2Dlod( sampler2D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xy, texcoord.w ); }\n"
    508 	"\n"
    509 };
    510 
    511 const char * fragmentInsert = {
    512 	"#version 150\n"
    513 	"#define PC\n"
    514 	"\n"
    515 	"void clip( float v ) { if ( v < 0.0 ) { discard; } }\n"
    516 	"void clip( vec2 v ) { if ( any( lessThan( v, vec2( 0.0 ) ) ) ) { discard; } }\n"
    517 	"void clip( vec3 v ) { if ( any( lessThan( v, vec3( 0.0 ) ) ) ) { discard; } }\n"
    518 	"void clip( vec4 v ) { if ( any( lessThan( v, vec4( 0.0 ) ) ) ) { discard; } }\n"
    519 	"\n"
    520 	"float saturate( float v ) { return clamp( v, 0.0, 1.0 ); }\n"
    521 	"vec2 saturate( vec2 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    522 	"vec3 saturate( vec3 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    523 	"vec4 saturate( vec4 v ) { return clamp( v, 0.0, 1.0 ); }\n"
    524 	"\n"
    525 	"vec4 tex2D( sampler2D sampler, vec2 texcoord ) { return texture( sampler, texcoord.xy ); }\n"
    526 	"vec4 tex2D( sampler2DShadow sampler, vec3 texcoord ) { return vec4( texture( sampler, texcoord.xyz ) ); }\n"
    527 	"\n"
    528 	"vec4 tex2D( sampler2D sampler, vec2 texcoord, vec2 dx, vec2 dy ) { return textureGrad( sampler, texcoord.xy, dx, dy ); }\n"
    529 	"vec4 tex2D( sampler2DShadow sampler, vec3 texcoord, vec2 dx, vec2 dy ) { return vec4( textureGrad( sampler, texcoord.xyz, dx, dy ) ); }\n"
    530 	"\n"
    531 	"vec4 texCUBE( samplerCube sampler, vec3 texcoord ) { return texture( sampler, texcoord.xyz ); }\n"
    532 	"vec4 texCUBE( samplerCubeShadow sampler, vec4 texcoord ) { return vec4( texture( sampler, texcoord.xyzw ) ); }\n"
    533 	"\n"
    534 	"vec4 tex1Dproj( sampler1D sampler, vec2 texcoord ) { return textureProj( sampler, texcoord ); }\n"
    535 	"vec4 tex2Dproj( sampler2D sampler, vec3 texcoord ) { return textureProj( sampler, texcoord ); }\n"
    536 	"vec4 tex3Dproj( sampler3D sampler, vec4 texcoord ) { return textureProj( sampler, texcoord ); }\n"
    537 	"\n"
    538 	"vec4 tex1Dbias( sampler1D sampler, vec4 texcoord ) { return texture( sampler, texcoord.x, texcoord.w ); }\n"
    539 	"vec4 tex2Dbias( sampler2D sampler, vec4 texcoord ) { return texture( sampler, texcoord.xy, texcoord.w ); }\n"
    540 	"vec4 tex3Dbias( sampler3D sampler, vec4 texcoord ) { return texture( sampler, texcoord.xyz, texcoord.w ); }\n"
    541 	"vec4 texCUBEbias( samplerCube sampler, vec4 texcoord ) { return texture( sampler, texcoord.xyz, texcoord.w ); }\n"
    542 	"\n"
    543 	"vec4 tex1Dlod( sampler1D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.x, texcoord.w ); }\n"
    544 	"vec4 tex2Dlod( sampler2D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xy, texcoord.w ); }\n"
    545 	"vec4 tex3Dlod( sampler3D sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xyz, texcoord.w ); }\n"
    546 	"vec4 texCUBElod( samplerCube sampler, vec4 texcoord ) { return textureLod( sampler, texcoord.xyz, texcoord.w ); }\n"
    547 	"\n"
    548 };
    549 
    550 struct builtinConversion_t {
    551 	const char * nameCG;
    552 	const char * nameGLSL;
    553 } builtinConversion[] = {
    554 	{ "frac",		"fract" },
    555 	{ "lerp",		"mix" },
    556 	{ "rsqrt",		"inversesqrt" },
    557 	{ "ddx",		"dFdx" },
    558 	{ "ddy",		"dFdy" },
    559 
    560 	{ NULL, NULL }
    561 };
    562 
    563 struct inOutVariable_t {
    564 	idStr	type;
    565 	idStr	nameCg;
    566 	idStr	nameGLSL;
    567 	bool	declareInOut;
    568 };
    569 
    570 /*
    571 ========================
    572 ParseInOutStruct
    573 ========================
    574 */
    575 void ParseInOutStruct( idLexer & src, int attribType, idList< inOutVariable_t > & inOutVars ) {
    576 	src.ExpectTokenString( "{" );
    577 
    578 	while( !src.CheckTokenString( "}" ) ) {
    579 		inOutVariable_t var;
    580 
    581 		idToken token;
    582 		src.ReadToken( &token );
    583 		var.type = token;
    584 		src.ReadToken( &token );
    585 		var.nameCg = token;
    586 
    587 		if ( !src.CheckTokenString( ":" ) ) {
    588 			src.SkipUntilString( ";" );
    589 			continue;
    590 		}
    591 
    592 		src.ReadToken( &token );
    593 		var.nameGLSL = token;
    594 		src.ExpectTokenString( ";" );
    595 
    596 		// convert the type
    597 		for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) {
    598 			if ( var.type.Cmp( typeConversion[i].typeCG ) == 0 ) {
    599 				var.type = typeConversion[i].typeGLSL;
    600 				break;
    601 			}
    602 		}
    603 
    604 		// convert the semantic to a GLSL name
    605 		for ( int i = 0; attribsPC[i].semantic != NULL; i++ ) {
    606 			if ( ( attribsPC[i].flags & attribType ) != 0 ) {
    607 				if ( var.nameGLSL.Cmp( attribsPC[i].semantic ) == 0 ) {
    608 					var.nameGLSL = attribsPC[i].glsl;
    609 					break;
    610 				}
    611 			}
    612 		}
    613 
    614 		// check if it was defined previously
    615 		var.declareInOut = true;
    616 		for ( int i = 0; i < inOutVars.Num(); i++ ) {
    617 			if ( var.nameGLSL == inOutVars[i].nameGLSL ) {
    618 				var.declareInOut = false;
    619 				break;
    620 			}
    621 		}
    622 
    623 		inOutVars.Append( var );
    624 	}
    625 
    626 	src.ExpectTokenString( ";" );
    627 }
    628 
    629 /*
    630 ========================
    631 ConvertCG2GLSL
    632 ========================
    633 */
    634 idStr ConvertCG2GLSL( const idStr & in, const char * name, bool isVertexProgram, idStr & uniforms ) {
    635 	idStr program;
    636 	program.ReAllocate( in.Length() * 2, false );
    637 
    638 	idList< inOutVariable_t, TAG_RENDERPROG > varsIn;
    639 	idList< inOutVariable_t, TAG_RENDERPROG > varsOut;
    640 	idList< idStr > uniformList;
    641 
    642 	idLexer src( LEXFL_NOFATALERRORS );
    643 	src.LoadMemory( in.c_str(), in.Length(), name );
    644 
    645 	bool inMain = false;
    646 	const char * uniformArrayName = isVertexProgram ? VERTEX_UNIFORM_ARRAY_NAME : FRAGMENT_UNIFORM_ARRAY_NAME;
    647 	char newline[128] = { "\n" };
    648 
    649 	idToken token;
    650 	while ( src.ReadToken( &token ) ) {
    651 
    652 		// check for uniforms
    653 		while ( token == "uniform" && src.CheckTokenString( "float4" ) ) {
    654 			src.ReadToken( &token );
    655 			uniformList.Append( token );
    656 
    657 			// strip ': register()' from uniforms
    658 			if ( src.CheckTokenString( ":" ) ) {
    659 				if ( src.CheckTokenString( "register" ) ) {
    660 					src.SkipUntilString( ";" );
    661 				}
    662 			}
    663 
    664 			src.ReadToken( & token );
    665 		}
    666 
    667 		// convert the in/out structs
    668 		if ( token == "struct" ) {
    669 			if ( src.CheckTokenString( "VS_IN" ) ) {
    670 				ParseInOutStruct( src, AT_VS_IN, varsIn );
    671 				program += "\n\n";
    672 				for ( int i = 0; i < varsIn.Num(); i++ ) {
    673 					if ( varsIn[i].declareInOut ) {
    674 						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
    675 					}
    676 				}
    677 				continue;
    678 			} else if ( src.CheckTokenString( "VS_OUT" ) ) {
    679 				ParseInOutStruct( src, AT_VS_OUT, varsOut );
    680 				program += "\n";
    681 				for ( int i = 0; i < varsOut.Num(); i++ ) {
    682 					if ( varsOut[i].declareInOut ) {
    683 						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
    684 					}
    685 				}
    686 				continue;
    687 			} else if ( src.CheckTokenString( "PS_IN" ) ) {
    688 				ParseInOutStruct( src, AT_PS_IN, varsIn );
    689 				program += "\n\n";
    690 				for ( int i = 0; i < varsIn.Num(); i++ ) {
    691 					if ( varsIn[i].declareInOut ) {
    692 						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
    693 					}
    694 				}
    695 				inOutVariable_t var;
    696 				var.type = "vec4";
    697 				var.nameCg = "position";
    698 				var.nameGLSL = "gl_FragCoord";
    699 				varsIn.Append( var );
    700 				continue;
    701 			} else if ( src.CheckTokenString( "PS_OUT" ) ) {
    702 				ParseInOutStruct( src, AT_PS_OUT, varsOut );
    703 				program += "\n";
    704 				for ( int i = 0; i < varsOut.Num(); i++ ) {
    705 					if ( varsOut[i].declareInOut ) {
    706 						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
    707 					}
    708 				}
    709 				continue;
    710 			}
    711 		}
    712 
    713 		// strip 'static'
    714 		if ( token == "static" ) {
    715 			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    716 			src.SkipWhiteSpace( true ); // remove white space between 'static' and the next token
    717 			continue;
    718 		}
    719 
    720 		// strip ': register()' from uniforms
    721 		if ( token == ":" ) {
    722 			if ( src.CheckTokenString( "register" ) ) {
    723 				src.SkipUntilString( ";" );
    724 				program += ";";
    725 				continue;
    726 			}
    727 		}
    728 
    729 		// strip in/program parameters from main
    730 		if ( token == "void" && src.CheckTokenString( "main" ) ) {
    731 			src.ExpectTokenString( "(" );
    732 			while( src.ReadToken( &token ) ) {
    733 				if ( token == ")" ) {
    734 					break;
    735 				}
    736 			}
    737 
    738 			program += "\nvoid main()";
    739 			inMain = true;
    740 			continue;
    741 		}
    742 
    743 		// strip 'const' from local variables in main()
    744 		if ( token == "const" && inMain ) {
    745 			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    746 			src.SkipWhiteSpace( true ); // remove white space between 'const' and the next token
    747 			continue;
    748 		}
    749 
    750 		// maintain indentation
    751 		if ( token == "{" ) {
    752 			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    753 			program += "{";
    754 
    755 			int len = Min( idStr::Length( newline ) + 1, (int)sizeof( newline ) - 1 );
    756 			newline[len - 1] = '\t';
    757 			newline[len - 0] = '\0';
    758 			continue;
    759 		}
    760 		if ( token == "}" ) {
    761 			int len = Max( idStr::Length( newline ) - 1, 0 );
    762 			newline[len] = '\0';
    763 
    764 			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    765 			program += "}";
    766 			continue;
    767 		}
    768 
    769 		// check for a type conversion
    770 		bool foundType = false;
    771 		for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) {
    772 			if ( token.Cmp( typeConversion[i].typeCG ) == 0 ) {
    773 				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    774 				program += typeConversion[i].typeGLSL;
    775 				foundType = true;
    776 				break;
    777 			}
    778 		}
    779 		if ( foundType ) {
    780 			continue;
    781 		}
    782 
    783 		if ( r_useUniformArrays.GetBool() ) {
    784 			// check for uniforms that need to be converted to the array
    785 			bool isUniform = false;
    786 			for ( int i = 0; i < uniformList.Num(); i++ ) {
    787 				if ( token == uniformList[i] ) {
    788 					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    789 					program += va( "%s[%d /* %s */]", uniformArrayName, i, uniformList[i].c_str() );
    790 					isUniform = true;
    791 					break;
    792 				}
    793 			}
    794 			if ( isUniform ) {
    795 				continue;
    796 			}
    797 		}
    798 
    799 		// check for input/output parameters
    800 		if ( src.CheckTokenString( "." ) ) {
    801 
    802 			if ( token == "vertex" || token == "fragment" ) {
    803 				idToken member;
    804 				src.ReadToken( &member );
    805 
    806 				bool foundInOut = false;
    807 				for ( int i = 0; i < varsIn.Num(); i++ ) {
    808 					if ( member.Cmp( varsIn[i].nameCg ) == 0 ) {
    809 						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    810 						program += varsIn[i].nameGLSL;
    811 						foundInOut = true;
    812 						break;
    813 					}
    814 				}
    815 				if ( !foundInOut ) {
    816 					src.Error( "invalid input parameter %s.%s", token.c_str(), member.c_str() );
    817 					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    818 					program += token;
    819 					program += ".";
    820 					program += member;
    821 				}
    822 				continue;
    823 			}
    824 
    825 			if ( token == "result" ) {
    826 				idToken member;
    827 				src.ReadToken( &member );
    828 
    829 				bool foundInOut = false;
    830 				for ( int i = 0; i < varsOut.Num(); i++ ) {
    831 					if ( member.Cmp( varsOut[i].nameCg ) == 0 ) {
    832 						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    833 						program += varsOut[i].nameGLSL;
    834 						foundInOut = true;
    835 						break;
    836 					}
    837 				}
    838 				if ( !foundInOut ) {
    839 					src.Error( "invalid output parameter %s.%s", token.c_str(), member.c_str() );
    840 					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    841 					program += token;
    842 					program += ".";
    843 					program += member;
    844 				}
    845 				continue;
    846 			}
    847 
    848 			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    849 			program += token;
    850 			program += ".";
    851 			continue;
    852 		}
    853 
    854 		// check for a function conversion
    855 		bool foundFunction = false;
    856 		for ( int i = 0; builtinConversion[i].nameCG != NULL; i++ ) {
    857 			if ( token.Cmp( builtinConversion[i].nameCG ) == 0 ) {
    858 				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    859 				program += builtinConversion[i].nameGLSL;
    860 				foundFunction = true;
    861 				break;
    862 			}
    863 		}
    864 		if ( foundFunction ) {
    865 			continue;
    866 		}
    867 
    868 		program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
    869 		program += token;
    870 	}
    871 
    872 	idStr out;
    873 
    874 	if ( isVertexProgram ) {
    875 		out.ReAllocate( idStr::Length( vertexInsert ) + in.Length() * 2, false );
    876 		out += vertexInsert;
    877 	} else {
    878 		out.ReAllocate( idStr::Length( fragmentInsert ) + in.Length() * 2, false );
    879 		out += fragmentInsert;
    880 	}
    881 
    882 	if ( uniformList.Num() > 0 ) {
    883 		if ( r_useUniformArrays.GetBool() ) {
    884 			out += va( "\nuniform vec4 %s[%d];\n", uniformArrayName, uniformList.Num() );
    885 		} else {
    886 			out += "\n";
    887 			for ( int i = 0; i < uniformList.Num(); i++ ) {
    888 				out += "uniform vec4 ";
    889 				out += uniformList[i];
    890 				out += ";\n";
    891 			}
    892 		}
    893 	}
    894 
    895 	out += program;
    896 
    897 	for ( int i = 0; i < uniformList.Num(); i++ ) {
    898 		uniforms += uniformList[i];
    899 		uniforms += "\n";
    900 	}
    901 	uniforms += "\n";
    902 
    903 	return out;
    904 }
    905 
    906 /*
    907 ================================================================================================
    908 idRenderProgManager::LoadGLSLShader
    909 ================================================================================================
    910 */
    911 GLuint idRenderProgManager::LoadGLSLShader( GLenum target, const char * name, idList<int> & uniforms ) {
    912 
    913 	idStr inFile;
    914 	idStr outFileHLSL;
    915 	idStr outFileGLSL;
    916 	idStr outFileUniforms;
    917 	inFile.Format( "renderprogs\\%s", name );
    918 	inFile.StripFileExtension();
    919 	outFileHLSL.Format( "renderprogs\\glsl\\%s", name );
    920 	outFileHLSL.StripFileExtension();
    921 	outFileGLSL.Format( "renderprogs\\glsl\\%s", name );
    922 	outFileGLSL.StripFileExtension();
    923 	outFileUniforms.Format( "renderprogs\\glsl\\%s", name );
    924 	outFileUniforms.StripFileExtension();
    925 	if ( target == GL_FRAGMENT_SHADER ) {
    926 		inFile += ".pixel";
    927 		outFileHLSL += "_fragment.hlsl";
    928 		outFileGLSL += "_fragment.glsl";
    929 		outFileUniforms += "_fragment.uniforms";
    930 	} else {
    931 		inFile += ".vertex";
    932 		outFileHLSL += "_vertex.hlsl";
    933 		outFileGLSL += "_vertex.glsl";
    934 		outFileUniforms += "_vertex.uniforms";
    935 	}
    936 
    937 	// first check whether we already have a valid GLSL file and compare it to the hlsl timestamp;
    938 	ID_TIME_T hlslTimeStamp;
    939 	int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp );
    940 
    941 	ID_TIME_T glslTimeStamp;
    942 	int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp );
    943 
    944 	// if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file.
    945 	idStr programGLSL;
    946 	idStr programUniforms;
    947 	if ( ( glslFileLength <= 0 ) || ( hlslTimeStamp > glslTimeStamp ) ) {
    948 		if ( hlslFileLength <= 0 ) {
    949 			// hlsl file doesn't even exist bail out
    950 			return false;
    951 		}
    952 
    953 		void * hlslFileBuffer = NULL;
    954 		int len = fileSystem->ReadFile( inFile.c_str(), &hlslFileBuffer );
    955 		if ( len <= 0 ) {
    956 			return false;
    957 		}
    958 		idStr hlslCode( ( const char* ) hlslFileBuffer );
    959 		idStr programHLSL = StripDeadCode( hlslCode, inFile );
    960 		programGLSL = ConvertCG2GLSL( programHLSL, inFile, target == GL_VERTEX_SHADER, programUniforms );
    961 
    962 		fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_basepath" );
    963 		fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_basepath" );
    964 		if ( r_useUniformArrays.GetBool() ) {
    965 			fileSystem->WriteFile( outFileUniforms, programUniforms.c_str(), programUniforms.Length(), "fs_basepath" );
    966 		}
    967 	} else {
    968 		// read in the glsl file
    969 		void * fileBufferGLSL = NULL;
    970 		int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL );
    971 		if ( lengthGLSL <= 0 ) {
    972 			idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() );
    973 		}
    974 		programGLSL = ( const char * ) fileBufferGLSL;
    975 		Mem_Free( fileBufferGLSL );
    976 
    977 		if ( r_useUniformArrays.GetBool() ) {
    978 			// read in the uniform file
    979 			void * fileBufferUniforms = NULL;
    980 			int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms );
    981 			if ( lengthUniforms <= 0 ) {
    982 				idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() );
    983 			}
    984 			programUniforms = ( const char* ) fileBufferUniforms;
    985 			Mem_Free( fileBufferUniforms );
    986 		}
    987 	}
    988 
    989 	// find the uniforms locations in either the vertex or fragment uniform array
    990 	if ( r_useUniformArrays.GetBool() ) {
    991 		uniforms.Clear();
    992 
    993 		idLexer src( programUniforms, programUniforms.Length(), "uniforms" );
    994 		idToken token;
    995 		while ( src.ReadToken( &token ) ) {
    996 			int index = -1;
    997 			for ( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ ) {
    998 				const char * parmName = GetGLSLParmName( i );
    999 				if ( token == parmName ) {
   1000 					index = i;
   1001 				}
   1002 			}
   1003 			for ( int i = 0; i < MAX_GLSL_USER_PARMS && index == -1; i++ ) {
   1004 				const char * parmName = GetGLSLParmName( RENDERPARM_USER + i );
   1005 				if ( token == parmName ) {
   1006 					index = RENDERPARM_USER + i;
   1007 				}
   1008 			}
   1009 			if ( index == -1 ) {
   1010 				idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() );
   1011 			}
   1012 			uniforms.Append( index );
   1013 		}
   1014 	}
   1015 
   1016 	// create and compile the shader
   1017 	const GLuint shader = qglCreateShader( target );
   1018 	if ( shader ) {
   1019 		const char * source[1] = { programGLSL.c_str() };
   1020 
   1021 		qglShaderSource( shader, 1, source, NULL );
   1022 		qglCompileShader( shader );
   1023 
   1024 		int infologLength = 0;
   1025 		qglGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infologLength );
   1026 		if ( infologLength > 1 ) {
   1027 			idTempArray<char> infoLog( infologLength );
   1028 			int charsWritten = 0;
   1029 			qglGetShaderInfoLog( shader, infologLength, &charsWritten, infoLog.Ptr() );
   1030 
   1031 			// catch the strings the ATI and Intel drivers output on success
   1032 			if ( strstr( infoLog.Ptr(), "successfully compiled to run on hardware" ) != NULL || 
   1033 					strstr( infoLog.Ptr(), "No errors." ) != NULL ) {
   1034 				//idLib::Printf( "%s program %s from %s compiled to run on hardware\n", typeName, GetName(), GetFileName() );
   1035 			} else {
   1036 				idLib::Printf( "While compiling %s program %s\n", ( target == GL_FRAGMENT_SHADER ) ? "fragment" : "vertex" , inFile.c_str() );
   1037 
   1038 				const char separator = '\n';
   1039 				idList<idStr> lines;
   1040 				lines.Clear();
   1041 				idStr source( programGLSL );
   1042 				lines.Append( source );
   1043 				for ( int index = 0, ofs = lines[index].Find( separator ); ofs != -1; index++, ofs = lines[index].Find( separator ) ) {
   1044 					lines.Append( lines[index].c_str() + ofs + 1 );
   1045 					lines[index].CapLength( ofs );
   1046 				}
   1047 
   1048 				idLib::Printf( "-----------------\n" );
   1049 				for ( int i = 0; i < lines.Num(); i++ ) {
   1050 					idLib::Printf( "%3d: %s\n", i+1, lines[i].c_str() );
   1051 				}
   1052 				idLib::Printf( "-----------------\n" );
   1053 
   1054 				idLib::Printf( "%s\n", infoLog.Ptr() );
   1055 			}
   1056 		}
   1057 
   1058 		GLint compiled = GL_FALSE;
   1059 		qglGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
   1060 		if ( compiled == GL_FALSE ) {
   1061 			qglDeleteShader( shader );
   1062 			return INVALID_PROGID;
   1063 		}
   1064 	}
   1065 
   1066 	return shader;
   1067 }
   1068 /*
   1069 ================================================================================================
   1070 idRenderProgManager::FindGLSLProgram
   1071 ================================================================================================
   1072 */
   1073 int	 idRenderProgManager::FindGLSLProgram( const char * name, int vIndex, int fIndex ) {
   1074 
   1075 	for ( int i = 0; i < glslPrograms.Num(); ++i ) {
   1076 		if ( ( glslPrograms[i].vertexShaderIndex == vIndex ) && ( glslPrograms[i].fragmentShaderIndex == fIndex ) ) {
   1077 			LoadGLSLProgram( i, vIndex, fIndex );
   1078 			return i;
   1079 		}
   1080 	}
   1081 
   1082 	glslProgram_t program;
   1083 	program.name = name;
   1084 	int index = glslPrograms.Append( program );
   1085 	LoadGLSLProgram( index, vIndex, fIndex );
   1086 	return index;
   1087 }
   1088 
   1089 /*
   1090 ================================================================================================
   1091 idRenderProgManager::GetGLSLParmName
   1092 ================================================================================================
   1093 */
   1094 const char* idRenderProgManager::GetGLSLParmName( int rp ) const {
   1095 	if ( rp >= RENDERPARM_USER ) {
   1096 		int userParmIndex = rp - RENDERPARM_USER;
   1097 		return va("rpUser%d", userParmIndex );
   1098 	}
   1099 	assert( rp < RENDERPARM_TOTAL );
   1100 	return GLSLParmNames[ rp ];
   1101 }
   1102 
   1103 /*
   1104 ================================================================================================
   1105 idRenderProgManager::SetUniformValue
   1106 ================================================================================================
   1107 */
   1108 void idRenderProgManager::SetUniformValue( const renderParm_t rp, const float * value ) {
   1109 	for ( int i = 0; i < 4; i++ ) {
   1110 		glslUniforms[rp][i] = value[i];
   1111 	}
   1112 }
   1113 
   1114 /*
   1115 ================================================================================================
   1116 idRenderProgManager::CommitUnforms
   1117 ================================================================================================
   1118 */
   1119 void idRenderProgManager::CommitUniforms() {
   1120 	const int progID = GetGLSLCurrentProgram();
   1121 	const glslProgram_t & prog = glslPrograms[progID];
   1122 
   1123 	if ( r_useUniformArrays.GetBool() ) {
   1124 		ALIGNTYPE16 idVec4 localVectors[RENDERPARM_USER + MAX_GLSL_USER_PARMS];
   1125 
   1126 		if ( prog.vertexShaderIndex >= 0 ) {
   1127 			const idList<int> & vertexUniforms = vertexShaders[prog.vertexShaderIndex].uniforms;
   1128 			if ( prog.vertexUniformArray != -1 && vertexUniforms.Num() > 0 ) {
   1129 				for ( int i = 0; i < vertexUniforms.Num(); i++ ) {
   1130 					localVectors[i] = glslUniforms[vertexUniforms[i]];
   1131 				}
   1132 				qglUniform4fv( prog.vertexUniformArray, vertexUniforms.Num(), localVectors->ToFloatPtr() );
   1133 			}
   1134 		}
   1135 
   1136 		if ( prog.fragmentShaderIndex >= 0 ) {
   1137 			const idList<int> & fragmentUniforms = fragmentShaders[prog.fragmentShaderIndex].uniforms;
   1138 			if ( prog.fragmentUniformArray != -1 && fragmentUniforms.Num() > 0 ) {
   1139 				for ( int i = 0; i < fragmentUniforms.Num(); i++ ) {
   1140 					localVectors[i] = glslUniforms[fragmentUniforms[i]];
   1141 				}
   1142 				qglUniform4fv( prog.fragmentUniformArray, fragmentUniforms.Num(), localVectors->ToFloatPtr() );
   1143 			}
   1144 		}
   1145 	} else {
   1146 		for ( int i = 0; i < prog.uniformLocations.Num(); i++ ) {
   1147 			const glslUniformLocation_t & uniformLocation = prog.uniformLocations[i];
   1148 			qglUniform4fv( uniformLocation.uniformIndex, 1, glslUniforms[uniformLocation.parmIndex].ToFloatPtr() );
   1149 		}
   1150 	}
   1151 }
   1152 
   1153 class idSort_QuickUniforms : public idSort_Quick< glslUniformLocation_t, idSort_QuickUniforms > {
   1154 public:
   1155 	int Compare( const glslUniformLocation_t & a, const glslUniformLocation_t & b ) const { return a.uniformIndex - b.uniformIndex; }
   1156 };
   1157 
   1158 /*
   1159 ================================================================================================
   1160 idRenderProgManager::LoadGLSLProgram
   1161 ================================================================================================
   1162 */
   1163 void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex ) {
   1164 	glslProgram_t & prog = glslPrograms[programIndex];
   1165 
   1166 	if ( prog.progId != INVALID_PROGID ) {
   1167 		return; // Already loaded
   1168 	}
   1169 
   1170 	GLuint vertexProgID = ( vertexShaderIndex != -1 ) ? vertexShaders[ vertexShaderIndex ].progId : INVALID_PROGID;
   1171 	GLuint fragmentProgID = ( fragmentShaderIndex != -1 ) ? fragmentShaders[ fragmentShaderIndex ].progId : INVALID_PROGID;
   1172 
   1173 	const GLuint program = qglCreateProgram();
   1174 	if ( program ) {
   1175 
   1176 		if ( vertexProgID != INVALID_PROGID ) {
   1177 			qglAttachShader( program, vertexProgID );
   1178 		}
   1179 
   1180 		if ( fragmentProgID != INVALID_PROGID ) {
   1181 			qglAttachShader( program, fragmentProgID );
   1182 		}
   1183 
   1184 		// bind vertex attribute locations
   1185 		for ( int i = 0; attribsPC[i].glsl != NULL; i++ ) {
   1186 			if ( ( attribsPC[i].flags & AT_VS_IN ) != 0 ) {
   1187 				qglBindAttribLocation( program, attribsPC[i].bind, attribsPC[i].glsl );
   1188 			}
   1189 		}
   1190 
   1191 		qglLinkProgram( program );
   1192 
   1193 		int infologLength = 0;
   1194 		qglGetProgramiv( program, GL_INFO_LOG_LENGTH, &infologLength );
   1195 		if ( infologLength > 1 ) {
   1196 			char * infoLog = (char *)malloc( infologLength );
   1197 			int charsWritten = 0;
   1198 			qglGetProgramInfoLog( program, infologLength, &charsWritten, infoLog );
   1199 
   1200 			// catch the strings the ATI and Intel drivers output on success
   1201 			if ( strstr( infoLog, "Vertex shader(s) linked, fragment shader(s) linked." ) != NULL || strstr( infoLog, "No errors." ) != NULL ) {
   1202 				//idLib::Printf( "render prog %s from %s linked\n", GetName(), GetFileName() );
   1203 			} else {
   1204 				idLib::Printf( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 
   1205 					programIndex, 
   1206 					( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 
   1207 					( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
   1208 				idLib::Printf( "%s\n", infoLog );
   1209 			}
   1210 
   1211 			free( infoLog );
   1212 		}
   1213 	}
   1214 
   1215 	int linked = GL_FALSE;
   1216 	qglGetProgramiv( program, GL_LINK_STATUS, &linked );
   1217 	if ( linked == GL_FALSE ) {
   1218 		qglDeleteProgram( program );
   1219 		idLib::Error( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 
   1220 			programIndex, 
   1221 			( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 
   1222 			( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
   1223 		return;
   1224 	}
   1225 
   1226 	if ( r_useUniformArrays.GetBool() ) {
   1227 		prog.vertexUniformArray = qglGetUniformLocation( program, VERTEX_UNIFORM_ARRAY_NAME );
   1228 		prog.fragmentUniformArray = qglGetUniformLocation( program, FRAGMENT_UNIFORM_ARRAY_NAME );
   1229 
   1230 		assert( prog.vertexUniformArray != -1 || vertexShaderIndex < 0 || vertexShaders[vertexShaderIndex].uniforms.Num() == 0 );
   1231 		assert( prog.fragmentUniformArray != -1 || fragmentShaderIndex < 0 || fragmentShaders[fragmentShaderIndex].uniforms.Num() == 0 );
   1232 	} else {
   1233 		// store the uniform locations after we have linked the GLSL program
   1234 		prog.uniformLocations.Clear();
   1235 		for ( int i = 0; i < RENDERPARM_TOTAL; i++ ) {
   1236 			const char * parmName = GetGLSLParmName( i );
   1237 			GLint loc = qglGetUniformLocation( program, parmName );
   1238 			if ( loc != -1 ) {
   1239 				glslUniformLocation_t uniformLocation;
   1240 				uniformLocation.parmIndex = i;
   1241 				uniformLocation.uniformIndex = loc;
   1242 				prog.uniformLocations.Append( uniformLocation );
   1243 			}
   1244 		}
   1245 
   1246 		// store the USER uniform locations
   1247 		for ( int i = 0; i < MAX_GLSL_USER_PARMS; i++ ) {
   1248 			const char * parmName = GetGLSLParmName( RENDERPARM_USER + i );
   1249 			GLint loc = qglGetUniformLocation( program, parmName );
   1250 			if ( loc != -1 ) {
   1251 				glslUniformLocation_t uniformLocation;
   1252 				uniformLocation.parmIndex = RENDERPARM_USER + i;
   1253 				uniformLocation.uniformIndex = loc;
   1254 				prog.uniformLocations.Append( uniformLocation );
   1255 			}
   1256 		}
   1257 
   1258 		// sort the uniforms based on index
   1259 		prog.uniformLocations.SortWithTemplate( idSort_QuickUniforms() );
   1260 	}
   1261 
   1262 	// get the uniform buffer binding for skinning joint matrices
   1263 	GLint blockIndex = qglGetUniformBlockIndex( program, "matrices_ubo" );
   1264 	if ( blockIndex != -1 ) {
   1265 		qglUniformBlockBinding( program, blockIndex, 0 );
   1266 	}
   1267 
   1268 	// set the texture unit locations once for the render program. We only need to do this once since we only link the program once
   1269 	qglUseProgram( program );
   1270 	for ( int i = 0; i < MAX_PROG_TEXTURE_PARMS; ++i ) {
   1271 		GLint loc = qglGetUniformLocation( program, va( "samp%d", i ) );
   1272 		if ( loc != -1 ) {
   1273 			qglUniform1i( loc, i );
   1274 		}
   1275 	}
   1276 
   1277 	idStr programName = vertexShaders[ vertexShaderIndex ].name;
   1278 	programName.StripFileExtension();
   1279 	prog.name = programName;
   1280 	prog.progId = program;
   1281 	prog.fragmentShaderIndex = fragmentShaderIndex;
   1282 	prog.vertexShaderIndex = vertexShaderIndex;
   1283 }
   1284 
   1285 /*
   1286 ================================================================================================
   1287 idRenderProgManager::ZeroUniforms
   1288 ================================================================================================
   1289 */
   1290 void idRenderProgManager::ZeroUniforms() {
   1291 	memset( glslUniforms.Ptr(), 0, glslUniforms.Allocated() );
   1292 }
   1293