2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 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.
14 Doom 3 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.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
34 #include "cg_explicit.h"
38 static void cg_error_callback( void ) {
39 CGerror i = cgGetError();
40 common->Printf( "Cg error (%d): %s\n", i, cgGetErrorString(i) );
44 =========================================================================================
46 GENERAL INTERACTION RENDERING
48 =========================================================================================
53 GL_SelectTextureNoClient
56 static void GL_SelectTextureNoClient( int unit ) {
57 backEnd.glState.currenttmu = unit;
58 qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
59 RB_LogComment( "glActiveTextureARB( %i )\n", unit );
64 RB_ARB2_DrawInteraction
67 void RB_ARB2_DrawInteraction( const drawInteraction_t *din ) {
68 // load all the vertex program parameters
69 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
70 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
71 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
72 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
73 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
74 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
75 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
76 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
77 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
78 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
79 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
80 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
82 // testing fragment based normal mapping
83 if ( r_testARBProgram.GetBool() ) {
84 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 2, din->localLightOrigin.ToFloatPtr() );
85 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 3, din->localViewOrigin.ToFloatPtr() );
88 static const float zero[4] = { 0, 0, 0, 0 };
89 static const float one[4] = { 1, 1, 1, 1 };
90 static const float negOne[4] = { -1, -1, -1, -1 };
92 switch ( din->vertexColor ) {
94 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, zero );
95 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
98 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, one );
99 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, zero );
101 case SVC_INVERSE_MODULATE:
102 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_MODULATE, negOne );
103 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_COLOR_ADD, one );
107 // set the constant colors
108 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 0, din->diffuseColor.ToFloatPtr() );
109 qglProgramEnvParameter4fvARB( GL_FRAGMENT_PROGRAM_ARB, 1, din->specularColor.ToFloatPtr() );
113 // texture 1 will be the per-surface bump map
114 GL_SelectTextureNoClient( 1 );
115 din->bumpImage->Bind();
117 // texture 2 will be the light falloff texture
118 GL_SelectTextureNoClient( 2 );
119 din->lightFalloffImage->Bind();
121 // texture 3 will be the light projection texture
122 GL_SelectTextureNoClient( 3 );
123 din->lightImage->Bind();
125 // texture 4 is the per-surface diffuse map
126 GL_SelectTextureNoClient( 4 );
127 din->diffuseImage->Bind();
129 // texture 5 is the per-surface specular map
130 GL_SelectTextureNoClient( 5 );
131 din->specularImage->Bind();
134 RB_DrawElementsWithCounters( din->surf->geo );
140 RB_ARB2_CreateDrawInteractions
144 void RB_ARB2_CreateDrawInteractions( const drawSurf_t *surf ) {
149 // perform setup here that will be constant for all interactions
150 GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
152 // bind the vertex program
153 if ( r_testARBProgram.GetBool() ) {
154 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_TEST );
155 qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST );
157 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION );
158 qglBindProgramARB( GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION );
161 qglEnable(GL_VERTEX_PROGRAM_ARB);
162 qglEnable(GL_FRAGMENT_PROGRAM_ARB);
164 // enable the vertex arrays
165 qglEnableVertexAttribArrayARB( 8 );
166 qglEnableVertexAttribArrayARB( 9 );
167 qglEnableVertexAttribArrayARB( 10 );
168 qglEnableVertexAttribArrayARB( 11 );
169 qglEnableClientState( GL_COLOR_ARRAY );
171 // texture 0 is the normalization cube map for the vector towards the light
172 GL_SelectTextureNoClient( 0 );
173 if ( backEnd.vLight->lightShader->IsAmbientLight() ) {
174 globalImages->ambientNormalMap->Bind();
176 globalImages->normalCubeMapImage->Bind();
179 // texture 6 is the specular lookup table
180 GL_SelectTextureNoClient( 6 );
181 if ( r_testARBProgram.GetBool() ) {
182 globalImages->specular2DTableImage->Bind(); // variable specularity in alpha channel
184 globalImages->specularTableImage->Bind();
188 for ( ; surf ; surf=surf->nextOnLight ) {
189 // perform setup here that will not change over multiple interaction passes
191 // set the vertex pointers
192 idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
193 qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
194 qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
195 qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
196 qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
197 qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
198 qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
200 // this may cause RB_ARB2_DrawInteraction to be exacuted multiple
201 // times with different colors and images if the surface or light have multiple layers
202 RB_CreateSingleDrawInteractions( surf, RB_ARB2_DrawInteraction );
205 qglDisableVertexAttribArrayARB( 8 );
206 qglDisableVertexAttribArrayARB( 9 );
207 qglDisableVertexAttribArrayARB( 10 );
208 qglDisableVertexAttribArrayARB( 11 );
209 qglDisableClientState( GL_COLOR_ARRAY );
212 GL_SelectTextureNoClient( 6 );
213 globalImages->BindNull();
215 GL_SelectTextureNoClient( 5 );
216 globalImages->BindNull();
218 GL_SelectTextureNoClient( 4 );
219 globalImages->BindNull();
221 GL_SelectTextureNoClient( 3 );
222 globalImages->BindNull();
224 GL_SelectTextureNoClient( 2 );
225 globalImages->BindNull();
227 GL_SelectTextureNoClient( 1 );
228 globalImages->BindNull();
230 backEnd.glState.currenttmu = -1;
231 GL_SelectTexture( 0 );
233 qglDisable(GL_VERTEX_PROGRAM_ARB);
234 qglDisable(GL_FRAGMENT_PROGRAM_ARB);
240 RB_ARB2_DrawInteractions
243 void RB_ARB2_DrawInteractions( void ) {
245 const idMaterial *lightShader;
247 GL_SelectTexture( 0 );
248 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
251 // for each light, perform adding and shadowing
253 for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
254 backEnd.vLight = vLight;
257 if ( vLight->lightShader->IsFogLight() ) {
260 if ( vLight->lightShader->IsBlendLight() ) {
264 if ( !vLight->localInteractions && !vLight->globalInteractions
265 && !vLight->translucentInteractions ) {
269 lightShader = vLight->lightShader;
271 // clear the stencil buffer if needed
272 if ( vLight->globalShadows || vLight->localShadows ) {
273 backEnd.currentScissor = vLight->scissorRect;
274 if ( r_useScissor.GetBool() ) {
275 qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
276 backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
277 backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
278 backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
280 qglClear( GL_STENCIL_BUFFER_BIT );
282 // no shadows, so no need to read or write the stencil buffer
283 // we might in theory want to use GL_ALWAYS instead of disabling
284 // completely, to satisfy the invarience rules
285 qglStencilFunc( GL_ALWAYS, 128, 255 );
288 if ( r_useShadowVertexProgram.GetBool() ) {
289 qglEnable( GL_VERTEX_PROGRAM_ARB );
290 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
291 RB_StencilShadowPass( vLight->globalShadows );
292 RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
293 qglEnable( GL_VERTEX_PROGRAM_ARB );
294 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
295 RB_StencilShadowPass( vLight->localShadows );
296 RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
297 qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
299 RB_StencilShadowPass( vLight->globalShadows );
300 RB_ARB2_CreateDrawInteractions( vLight->localInteractions );
301 RB_StencilShadowPass( vLight->localShadows );
302 RB_ARB2_CreateDrawInteractions( vLight->globalInteractions );
305 // translucent surfaces never get stencil shadowed
306 if ( r_skipTranslucent.GetBool() ) {
310 qglStencilFunc( GL_ALWAYS, 128, 255 );
312 backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
313 RB_ARB2_CreateDrawInteractions( vLight->translucentInteractions );
315 backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
318 // disable stencil shadow test
319 qglStencilFunc( GL_ALWAYS, 128, 255 );
321 GL_SelectTexture( 0 );
322 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
325 //===================================================================================
334 static const int MAX_GLPROGS = 200;
336 // a single file can have both a vertex program and a fragment program
337 static progDef_t progs[MAX_GLPROGS] = {
338 { GL_VERTEX_PROGRAM_ARB, VPROG_TEST, "test.vfp" },
339 { GL_FRAGMENT_PROGRAM_ARB, FPROG_TEST, "test.vfp" },
340 { GL_VERTEX_PROGRAM_ARB, VPROG_INTERACTION, "interaction.vfp" },
341 { GL_FRAGMENT_PROGRAM_ARB, FPROG_INTERACTION, "interaction.vfp" },
342 { GL_VERTEX_PROGRAM_ARB, VPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
343 { GL_FRAGMENT_PROGRAM_ARB, FPROG_BUMPY_ENVIRONMENT, "bumpyEnvironment.vfp" },
344 { GL_VERTEX_PROGRAM_ARB, VPROG_AMBIENT, "ambientLight.vfp" },
345 { GL_FRAGMENT_PROGRAM_ARB, FPROG_AMBIENT, "ambientLight.vfp" },
346 { GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW, "shadow.vp" },
347 { GL_VERTEX_PROGRAM_ARB, VPROG_R200_INTERACTION, "R200_interaction.vp" },
348 { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT, "nv20_bumpAndLight.vp" },
349 { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_COLOR, "nv20_diffuseColor.vp" },
350 { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR, "nv20_specularColor.vp" },
351 { GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR, "nv20_diffuseAndSpecularColor.vp" },
352 { GL_VERTEX_PROGRAM_ARB, VPROG_ENVIRONMENT, "environment.vfp" },
353 { GL_FRAGMENT_PROGRAM_ARB, FPROG_ENVIRONMENT, "environment.vfp" },
354 { GL_VERTEX_PROGRAM_ARB, VPROG_GLASSWARP, "arbVP_glasswarp.txt" },
355 { GL_FRAGMENT_PROGRAM_ARB, FPROG_GLASSWARP, "arbFP_glasswarp.txt" },
357 // additional programs can be dynamically specified in materials
365 void R_LoadARBProgram( int progIndex ) {
368 idStr fullPath = "glprogs/";
369 fullPath += progs[progIndex].name;
374 common->Printf( "%s", fullPath.c_str() );
376 // load the program even if we don't support it, so
377 // fs_copyfiles can generate cross-platform data dumps
378 fileSystem->ReadFile( fullPath.c_str(), (void **)&fileBuffer, NULL );
380 common->Printf( ": File not found\n" );
384 // copy to stack memory and free
385 buffer = (char *)_alloca( strlen( fileBuffer ) + 1 );
386 strcpy( buffer, fileBuffer );
387 fileSystem->FreeFile( fileBuffer );
389 if ( !glConfig.isInitialized ) {
394 // submit the program string at start to GL
396 if ( progs[progIndex].ident == 0 ) {
397 // allocate a new identifier for this program
398 progs[progIndex].ident = PROG_USER + progIndex;
401 // vertex and fragment programs can both be present in a single file, so
402 // scan for the proper header to be the start point, and stamp a 0 in after the end
404 if ( progs[progIndex].target == GL_VERTEX_PROGRAM_ARB ) {
405 if ( !glConfig.ARBVertexProgramAvailable ) {
406 common->Printf( ": GL_VERTEX_PROGRAM_ARB not available\n" );
409 start = strstr( (char *)buffer, "!!ARBvp" );
411 if ( progs[progIndex].target == GL_FRAGMENT_PROGRAM_ARB ) {
412 if ( !glConfig.ARBFragmentProgramAvailable ) {
413 common->Printf( ": GL_FRAGMENT_PROGRAM_ARB not available\n" );
416 start = strstr( (char *)buffer, "!!ARBfp" );
419 common->Printf( ": !!ARB not found\n" );
422 end = strstr( start, "END" );
425 common->Printf( ": END not found\n" );
430 qglBindProgramARB( progs[progIndex].target, progs[progIndex].ident );
433 qglProgramStringARB( progs[progIndex].target, GL_PROGRAM_FORMAT_ASCII_ARB,
434 strlen( start ), (unsigned char *)start );
437 qglGetIntegerv( GL_PROGRAM_ERROR_POSITION_ARB, (GLint *)&ofs );
438 if ( err == GL_INVALID_OPERATION ) {
439 const GLubyte *str = qglGetString( GL_PROGRAM_ERROR_STRING_ARB );
440 common->Printf( "\nGL_PROGRAM_ERROR_STRING_ARB: %s\n", str );
442 common->Printf( "GL_PROGRAM_ERROR_POSITION_ARB < 0 with error\n" );
443 } else if ( ofs >= (int)strlen( (char *)start ) ) {
444 common->Printf( "error at end of program\n" );
446 common->Printf( "error at %i:\n%s", ofs, start + ofs );
451 common->Printf( "\nGL_PROGRAM_ERROR_POSITION_ARB != -1 without error\n" );
455 common->Printf( "\n" );
462 Returns a GL identifier that can be bound to the given target, parsing
463 a text file if it hasn't already been loaded.
466 int R_FindARBProgram( GLenum target, const char *program ) {
468 idStr stripped = program;
470 stripped.StripFileExtension();
472 // see if it is already loaded
473 for ( i = 0 ; progs[i].name[0] ; i++ ) {
474 if ( progs[i].target != target ) {
478 idStr compare = progs[i].name;
479 compare.StripFileExtension();
481 if ( !idStr::Icmp( stripped.c_str(), compare.c_str() ) ) {
482 return progs[i].ident;
486 if ( i == MAX_GLPROGS ) {
487 common->Error( "R_FindARBProgram: MAX_GLPROGS" );
490 // add it to the list and load it
491 progs[i].ident = (program_t)0; // will be gen'd by R_LoadARBProgram
492 progs[i].target = target;
493 strncpy( progs[i].name, program, sizeof( progs[i].name ) - 1 );
495 R_LoadARBProgram( i );
497 return progs[i].ident;
502 R_ReloadARBPrograms_f
505 void R_ReloadARBPrograms_f( const idCmdArgs &args ) {
508 common->Printf( "----- R_ReloadARBPrograms -----\n" );
509 for ( i = 0 ; progs[i].name[0] ; i++ ) {
510 R_LoadARBProgram( i );
512 common->Printf( "-------------------------------\n" );
521 void R_ARB2_Init( void ) {
522 glConfig.allowARB2Path = false;
524 common->Printf( "---------- R_ARB2_Init ----------\n" );
526 if ( !glConfig.ARBVertexProgramAvailable || !glConfig.ARBFragmentProgramAvailable ) {
527 common->Printf( "Not available.\n" );
531 common->Printf( "Available.\n" );
533 common->Printf( "---------------------------------\n" );
535 glConfig.allowARB2Path = true;