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"
38 FPROG_DIFFUSE_AND_SPECULAR_COLOR,
40 FPROG_NUM_FRAGMENT_PROGRAMS
43 GLuint fragmentDisplayListBase; // FPROG_NUM_FRAGMENT_PROGRAMS lists
45 void RB_NV20_DependentSpecularPass( const drawInteraction_t *din );
46 void RB_NV20_DependentAmbientPass( void );
49 =========================================================================================
51 GENERAL INTERACTION RENDERING
53 =========================================================================================
58 GL_SelectTextureNoClient
61 void GL_SelectTextureNoClient( int unit ) {
62 backEnd.glState.currenttmu = unit;
63 qglActiveTextureARB( GL_TEXTURE0_ARB + unit );
64 RB_LogComment( "glActiveTextureARB( %i )\n", unit );
69 RB_NV20_BumpAndLightFragment
72 static void RB_NV20_BumpAndLightFragment( void ) {
73 if ( r_useCombinerDisplayLists.GetBool() ) {
74 qglCallList( fragmentDisplayListBase + FPROG_BUMP_AND_LIGHT );
78 // program the nvidia register combiners
79 qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 3 );
81 // stage 0 rgb performs the dot product
82 // SPARE0 = TEXTURE0 dot TEXTURE1
83 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
84 GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
85 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
86 GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
87 qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
88 GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
89 GL_NONE, GL_NONE, GL_TRUE, GL_FALSE, GL_FALSE );
92 // stage 1 rgb multiplies texture 2 and 3 together
93 // SPARE1 = TEXTURE2 * TEXTURE3
94 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
95 GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
96 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
97 GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
98 qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
99 GL_SPARE1_NV, GL_DISCARD_NV, GL_DISCARD_NV,
100 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
102 // stage 1 alpha does nohing
104 // stage 2 color multiplies spare0 * spare 1 just for debugging
105 // SPARE0 = SPARE0 * SPARE1
106 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
107 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
108 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
109 GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
110 qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
111 GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
112 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
114 // stage 2 alpha multiples spare0 * spare 1
115 // SPARE0 = SPARE0 * SPARE1
116 qglCombinerInputNV( GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_A_NV,
117 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
118 qglCombinerInputNV( GL_COMBINER2_NV, GL_ALPHA, GL_VARIABLE_B_NV,
119 GL_SPARE1_NV, GL_UNSIGNED_IDENTITY_NV, GL_BLUE );
120 qglCombinerOutputNV( GL_COMBINER2_NV, GL_ALPHA,
121 GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
122 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
125 qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_SPARE0_NV,
126 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
127 qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_ZERO,
128 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
129 qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_ZERO,
130 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
131 qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
132 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
133 qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_SPARE0_NV,
134 GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
139 RB_NV20_DI_BumpAndLightPass
141 We are going to write alpha as light falloff * ( bump dot light ) * lightProjection
142 If the light isn't a monoLightShader, the lightProjection will be skipped, because
143 it will have to be done on an itterated basis
146 static void RB_NV20_DI_BumpAndLightPass( const drawInteraction_t *din, bool monoLightShader ) {
147 RB_LogComment( "---------- RB_NV_BumpAndLightPass ----------\n" );
149 GL_State( GLS_COLORMASK | GLS_DEPTHMASK | backEnd.depthFunc );
151 // texture 0 is the normalization cube map
152 // GL_TEXTURE0_ARB will be the normalized vector
153 // towards the light source
155 GL_SelectTexture( 0 );
156 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
158 GL_SelectTextureNoClient( 0 );
160 if ( din->ambientLight ) {
161 globalImages->ambientNormalMap->Bind();
163 globalImages->normalCubeMapImage->Bind();
166 // texture 1 will be the per-surface bump map
168 GL_SelectTexture( 1 );
169 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
171 GL_SelectTextureNoClient( 1 );
173 din->bumpImage->Bind();
175 // texture 2 will be the light falloff texture
177 GL_SelectTexture( 2 );
178 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
180 GL_SelectTextureNoClient( 2 );
182 din->lightFalloffImage->Bind();
184 // texture 3 will be the light projection texture
186 GL_SelectTexture( 3 );
187 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
189 GL_SelectTextureNoClient( 3 );
191 if ( monoLightShader ) {
192 din->lightImage->Bind();
194 // if the projected texture is multi-colored, we
195 // will need to do it in subsequent passes
196 globalImages->whiteImage->Bind();
199 // bind our "fragment program"
200 RB_NV20_BumpAndLightFragment();
203 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_BUMP_AND_LIGHT );
204 RB_DrawElementsWithCounters( din->surf->geo );
210 RB_NV20_DiffuseColorFragment
213 static void RB_NV20_DiffuseColorFragment( void ) {
214 if ( r_useCombinerDisplayLists.GetBool() ) {
215 qglCallList( fragmentDisplayListBase + FPROG_DIFFUSE_COLOR );
219 // program the nvidia register combiners
220 qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 1 );
222 // stage 0 is free, so we always do the multiply of the vertex color
223 // when the vertex color is inverted, qglCombinerInputNV(GL_VARIABLE_B_NV) will be changed
224 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
225 GL_TEXTURE0_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
226 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
227 GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
228 qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
229 GL_TEXTURE0_ARB, GL_DISCARD_NV, GL_DISCARD_NV,
230 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
232 qglCombinerOutputNV( GL_COMBINER0_NV, GL_ALPHA,
233 GL_DISCARD_NV, GL_DISCARD_NV, GL_DISCARD_NV,
234 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
237 // for GL_CONSTANT_COLOR0_NV * TEXTURE0 * TEXTURE1
238 qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_CONSTANT_COLOR0_NV,
239 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
240 qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_E_TIMES_F_NV,
241 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
242 qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
243 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
244 qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_ZERO,
245 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
246 qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_TEXTURE0_ARB,
247 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
248 qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_TEXTURE1_ARB,
249 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
250 qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
251 GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
257 RB_NV20_DI_DiffuseColorPass
261 static void RB_NV20_DI_DiffuseColorPass( const drawInteraction_t *din ) {
262 RB_LogComment( "---------- RB_NV20_DiffuseColorPass ----------\n" );
264 GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_ALPHAMASK
265 | backEnd.depthFunc );
267 // texture 0 will be the per-surface diffuse map
269 GL_SelectTexture( 0 );
270 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
272 GL_SelectTextureNoClient( 0 );
274 din->diffuseImage->Bind();
276 // texture 1 will be the light projected texture
278 GL_SelectTexture( 1 );
279 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
281 GL_SelectTextureNoClient( 1 );
283 din->lightImage->Bind();
285 // texture 2 is disabled
287 GL_SelectTexture( 2 );
288 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
290 GL_SelectTextureNoClient( 2 );
292 globalImages->BindNull();
294 // texture 3 is disabled
296 GL_SelectTexture( 3 );
297 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
299 GL_SelectTextureNoClient( 3 );
301 globalImages->BindNull();
303 // bind our "fragment program"
304 RB_NV20_DiffuseColorFragment();
306 // override one parameter for inverted vertex color
307 if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
308 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
309 GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
313 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_COLOR );
314 RB_DrawElementsWithCounters( din->surf->geo );
320 RB_NV20_SpecularColorFragment
323 static void RB_NV20_SpecularColorFragment( void ) {
324 if ( r_useCombinerDisplayLists.GetBool() ) {
325 qglCallList( fragmentDisplayListBase + FPROG_SPECULAR_COLOR );
329 // program the nvidia register combiners
330 qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 4 );
332 // we want GL_CONSTANT_COLOR1_NV * PRIMARY_COLOR * TEXTURE2 * TEXTURE3 * specular( TEXTURE0 * TEXTURE1 )
334 // stage 0 rgb performs the dot product
335 // GL_SPARE0_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
336 // TEXTURE2 = TEXTURE2 * PRIMARY_COLOR
337 // the scale and bias steepen the specular curve
338 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
339 GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
340 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
341 GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
342 qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
343 GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
344 GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );
346 // stage 0 alpha does nothing
348 // stage 1 color takes bump * bump
349 // GL_SPARE0_NV = ( GL_SPARE0_NV * GL_SPARE0_NV - 0.5 ) * 2
350 // the scale and bias steepen the specular curve
351 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
352 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
353 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
354 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
355 qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
356 GL_SPARE0_NV, GL_DISCARD_NV, GL_DISCARD_NV,
357 GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );
359 // stage 1 alpha does nothing
362 // GL_SPARE0_NV = GL_SPARE0_NV * TEXTURE3
363 // SECONDARY_COLOR = CONSTANT_COLOR * TEXTURE2
364 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
365 GL_SPARE0_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
366 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
367 GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
368 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_C_NV,
369 GL_CONSTANT_COLOR1_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
370 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_D_NV,
371 GL_TEXTURE2_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
372 qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
373 GL_SPARE0_NV, GL_SECONDARY_COLOR_NV, GL_DISCARD_NV,
374 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
376 // stage 2 alpha does nothing
379 // stage 3 scales the texture by the vertex color
380 qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_A_NV,
381 GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
382 qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV,
383 GL_PRIMARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
384 qglCombinerOutputNV( GL_COMBINER3_NV, GL_RGB,
385 GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
386 GL_NONE, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
388 // stage 3 alpha does nothing
390 // final combiner = GL_SPARE0_NV * SECONDARY_COLOR + PRIMARY_COLOR * SECONDARY_COLOR
391 qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_SPARE0_NV,
392 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
393 qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SECONDARY_COLOR_NV,
394 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
395 qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
396 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
397 qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_E_TIMES_F_NV,
398 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
399 qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_SPARE0_NV,
400 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
401 qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_SECONDARY_COLOR_NV,
402 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
403 qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
404 GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
410 RB_NV20_DI_SpecularColorPass
414 static void RB_NV20_DI_SpecularColorPass( const drawInteraction_t *din ) {
415 RB_LogComment( "---------- RB_NV20_SpecularColorPass ----------\n" );
417 GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | GLS_ALPHAMASK
418 | backEnd.depthFunc );
420 // texture 0 is the normalization cube map for the half angle
422 GL_SelectTexture( 0 );
423 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
425 GL_SelectTextureNoClient( 0 );
427 globalImages->normalCubeMapImage->Bind();
429 // texture 1 will be the per-surface bump map
431 GL_SelectTexture( 1 );
432 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
434 GL_SelectTextureNoClient( 1 );
436 din->bumpImage->Bind();
438 // texture 2 will be the per-surface specular map
440 GL_SelectTexture( 2 );
441 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
443 GL_SelectTextureNoClient( 2 );
445 din->specularImage->Bind();
447 // texture 3 will be the light projected texture
449 GL_SelectTexture( 3 );
450 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
452 GL_SelectTextureNoClient( 3 );
454 din->lightImage->Bind();
456 // bind our "fragment program"
457 RB_NV20_SpecularColorFragment();
459 // override one parameter for inverted vertex color
460 if ( din->vertexColor == SVC_INVERSE_MODULATE ) {
461 qglCombinerInputNV( GL_COMBINER3_NV, GL_RGB, GL_VARIABLE_B_NV,
462 GL_PRIMARY_COLOR_NV, GL_UNSIGNED_INVERT_NV, GL_RGB );
466 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_SPECULAR_COLOR );
467 RB_DrawElementsWithCounters( din->surf->geo );
474 RB_NV20_DiffuseAndSpecularColorFragment
477 static void RB_NV20_DiffuseAndSpecularColorFragment( void ) {
478 if ( r_useCombinerDisplayLists.GetBool() ) {
479 qglCallList( fragmentDisplayListBase + FPROG_DIFFUSE_AND_SPECULAR_COLOR );
483 // program the nvidia register combiners
484 qglCombinerParameteriNV( GL_NUM_GENERAL_COMBINERS_NV, 3 );
486 // GL_CONSTANT_COLOR0_NV will be the diffuse color
487 // GL_CONSTANT_COLOR1_NV will be the specular color
489 // stage 0 rgb performs the dot product
490 // GL_SECONDARY_COLOR_NV = ( TEXTURE0 dot TEXTURE1 - 0.5 ) * 2
491 // the scale and bias steepen the specular curve
492 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV,
493 GL_TEXTURE1_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
494 qglCombinerInputNV( GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV,
495 GL_TEXTURE0_ARB, GL_EXPAND_NORMAL_NV, GL_RGB );
496 qglCombinerOutputNV( GL_COMBINER0_NV, GL_RGB,
497 GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
498 GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_TRUE, GL_FALSE, GL_FALSE );
500 // stage 0 alpha does nothing
502 // stage 1 color takes bump * bump
503 // PRIMARY_COLOR = ( GL_SECONDARY_COLOR_NV * GL_SECONDARY_COLOR_NV - 0.5 ) * 2
504 // the scale and bias steepen the specular curve
505 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV,
506 GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
507 qglCombinerInputNV( GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV,
508 GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
509 qglCombinerOutputNV( GL_COMBINER1_NV, GL_RGB,
510 GL_SECONDARY_COLOR_NV, GL_DISCARD_NV, GL_DISCARD_NV,
511 GL_SCALE_BY_TWO_NV, GL_BIAS_BY_NEGATIVE_ONE_HALF_NV, GL_FALSE, GL_FALSE, GL_FALSE );
513 // stage 1 alpha does nothing
516 // PRIMARY_COLOR = ( PRIMARY_COLOR * TEXTURE3 ) * 2
517 // SPARE0 = 1.0 * 1.0 (needed for final combiner)
518 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_A_NV,
519 GL_SECONDARY_COLOR_NV, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
520 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_B_NV,
521 GL_TEXTURE3_ARB, GL_UNSIGNED_IDENTITY_NV, GL_RGB );
522 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_C_NV,
523 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
524 qglCombinerInputNV( GL_COMBINER2_NV, GL_RGB, GL_VARIABLE_D_NV,
525 GL_ZERO, GL_UNSIGNED_INVERT_NV, GL_RGB );
526 qglCombinerOutputNV( GL_COMBINER2_NV, GL_RGB,
527 GL_SECONDARY_COLOR_NV, GL_SPARE0_NV, GL_DISCARD_NV,
528 GL_SCALE_BY_TWO_NV, GL_NONE, GL_FALSE, GL_FALSE, GL_FALSE );
530 // stage 2 alpha does nothing
532 // final combiner = TEXTURE2_ARB * CONSTANT_COLOR0_NV + PRIMARY_COLOR_NV * CONSTANT_COLOR1_NV
534 qglFinalCombinerInputNV( GL_VARIABLE_A_NV, GL_CONSTANT_COLOR1_NV,
535 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
536 qglFinalCombinerInputNV( GL_VARIABLE_B_NV, GL_SECONDARY_COLOR_NV,
537 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
538 qglFinalCombinerInputNV( GL_VARIABLE_C_NV, GL_ZERO,
539 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
540 qglFinalCombinerInputNV( GL_VARIABLE_D_NV, GL_E_TIMES_F_NV,
541 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
542 qglFinalCombinerInputNV( GL_VARIABLE_E_NV, GL_TEXTURE2_ARB,
543 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
544 qglFinalCombinerInputNV( GL_VARIABLE_F_NV, GL_CONSTANT_COLOR0_NV,
545 GL_UNSIGNED_IDENTITY_NV, GL_RGB );
546 qglFinalCombinerInputNV( GL_VARIABLE_G_NV, GL_ZERO,
547 GL_UNSIGNED_IDENTITY_NV, GL_ALPHA );
553 RB_NV20_DI_DiffuseAndSpecularColorPass
557 static void RB_NV20_DI_DiffuseAndSpecularColorPass( const drawInteraction_t *din ) {
558 RB_LogComment( "---------- RB_NV20_DI_DiffuseAndSpecularColorPass ----------\n" );
560 GL_State( GLS_SRCBLEND_DST_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHMASK | backEnd.depthFunc );
562 // texture 0 is the normalization cube map for the half angle
563 // still bound from RB_NV_BumpAndLightPass
564 // GL_SelectTextureNoClient( 0 );
565 // GL_Bind( tr.normalCubeMapImage );
567 // texture 1 is the per-surface bump map
568 // still bound from RB_NV_BumpAndLightPass
569 // GL_SelectTextureNoClient( 1 );
570 // GL_Bind( din->bumpImage );
572 // texture 2 is the per-surface diffuse map
574 GL_SelectTexture( 2 );
575 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
577 GL_SelectTextureNoClient( 2 );
579 din->diffuseImage->Bind();
581 // texture 3 is the per-surface specular map
583 GL_SelectTexture( 3 );
584 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
586 GL_SelectTextureNoClient( 3 );
588 din->specularImage->Bind();
590 // bind our "fragment program"
591 RB_NV20_DiffuseAndSpecularColorFragment();
594 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_NV20_DIFFUSE_AND_SPECULAR_COLOR );
595 RB_DrawElementsWithCounters( din->surf->geo );
601 RB_NV20_DrawInteraction
604 static void RB_NV20_DrawInteraction( const drawInteraction_t *din ) {
605 const drawSurf_t *surf = din->surf;
607 // load all the vertex program parameters
608 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_ORIGIN, din->localLightOrigin.ToFloatPtr() );
609 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_VIEW_ORIGIN, din->localViewOrigin.ToFloatPtr() );
610 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_S, din->lightProjection[0].ToFloatPtr() );
611 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_T, din->lightProjection[1].ToFloatPtr() );
612 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_PROJECT_Q, din->lightProjection[2].ToFloatPtr() );
613 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_LIGHT_FALLOFF_S, din->lightProjection[3].ToFloatPtr() );
614 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_S, din->bumpMatrix[0].ToFloatPtr() );
615 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_BUMP_MATRIX_T, din->bumpMatrix[1].ToFloatPtr() );
616 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_S, din->diffuseMatrix[0].ToFloatPtr() );
617 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_DIFFUSE_MATRIX_T, din->diffuseMatrix[1].ToFloatPtr() );
618 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_S, din->specularMatrix[0].ToFloatPtr() );
619 qglProgramEnvParameter4fvARB( GL_VERTEX_PROGRAM_ARB, PP_SPECULAR_MATRIX_T, din->specularMatrix[1].ToFloatPtr() );
621 // set the constant colors
622 qglCombinerParameterfvNV( GL_CONSTANT_COLOR0_NV, din->diffuseColor.ToFloatPtr() );
623 qglCombinerParameterfvNV( GL_CONSTANT_COLOR1_NV, din->specularColor.ToFloatPtr() );
625 // vertex color passes should be pretty rare (cross-faded bump map surfaces), so always
626 // run them down as three-passes
627 if ( din->vertexColor != SVC_IGNORE ) {
628 qglEnableClientState( GL_COLOR_ARRAY );
629 RB_NV20_DI_BumpAndLightPass( din, false );
630 RB_NV20_DI_DiffuseColorPass( din );
631 RB_NV20_DI_SpecularColorPass( din );
632 qglDisableClientState( GL_COLOR_ARRAY );
636 qglColor3f( 1, 1, 1 );
638 // on an ideal card, we would now just bind the textures and call a
639 // single pass vertex / fragment program, but
640 // on NV20, we need to decide which single / dual / tripple pass set of programs to use
642 // ambient light could be done as a single pass if we want to optimize for it
644 // monochrome light is two passes
645 int internalFormat = din->lightImage->internalFormat;
646 if ( ( r_useNV20MonoLights.GetInteger() == 2 ) ||
647 ( din->lightImage->isMonochrome && r_useNV20MonoLights.GetInteger() ) ) {
648 // do a two-pass rendering
649 RB_NV20_DI_BumpAndLightPass( din, true );
650 RB_NV20_DI_DiffuseAndSpecularColorPass( din );
652 // general case is three passes
653 // ( bump dot lightDir ) * lightFalloff
654 // diffuse * lightProject
655 // specular * ( bump dot halfAngle extended ) * lightProject
656 RB_NV20_DI_BumpAndLightPass( din, false );
657 RB_NV20_DI_DiffuseColorPass( din );
658 RB_NV20_DI_SpecularColorPass( din );
665 RB_NV20_CreateDrawInteractions
669 static void RB_NV20_CreateDrawInteractions( const drawSurf_t *surf ) {
674 qglEnable( GL_VERTEX_PROGRAM_ARB );
675 qglEnable( GL_REGISTER_COMBINERS_NV );
679 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
681 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
683 qglEnableVertexAttribArrayARB( 8 );
684 qglEnableVertexAttribArrayARB( 9 );
685 qglEnableVertexAttribArrayARB( 10 );
686 qglEnableVertexAttribArrayARB( 11 );
689 for ( ; surf ; surf=surf->nextOnLight ) {
690 // set the vertex pointers
691 idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache );
692 qglColorPointer( 4, GL_UNSIGNED_BYTE, sizeof( idDrawVert ), ac->color );
694 GL_SelectTexture( 0 );
695 qglTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
696 GL_SelectTexture( 1 );
697 qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
698 GL_SelectTexture( 2 );
699 qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
700 GL_SelectTexture( 3 );
701 qglTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
702 GL_SelectTexture( 0 );
704 qglVertexAttribPointerARB( 11, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->normal.ToFloatPtr() );
705 qglVertexAttribPointerARB( 10, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[1].ToFloatPtr() );
706 qglVertexAttribPointerARB( 9, 3, GL_FLOAT, false, sizeof( idDrawVert ), ac->tangents[0].ToFloatPtr() );
707 qglVertexAttribPointerARB( 8, 2, GL_FLOAT, false, sizeof( idDrawVert ), ac->st.ToFloatPtr() );
709 qglVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() );
711 RB_CreateSingleDrawInteractions( surf, RB_NV20_DrawInteraction );
715 qglDisableVertexAttribArrayARB( 8 );
716 qglDisableVertexAttribArrayARB( 9 );
717 qglDisableVertexAttribArrayARB( 10 );
718 qglDisableVertexAttribArrayARB( 11 );
723 GL_SelectTexture( 3 );
724 globalImages->BindNull();
725 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
727 GL_SelectTexture( 2 );
728 globalImages->BindNull();
729 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
731 GL_SelectTexture( 1 );
732 globalImages->BindNull();
733 qglDisableClientState( GL_TEXTURE_COORD_ARRAY );
735 GL_SelectTextureNoClient( 3 );
736 globalImages->BindNull();
738 GL_SelectTextureNoClient( 2 );
739 globalImages->BindNull();
741 GL_SelectTextureNoClient( 1 );
742 globalImages->BindNull();
745 backEnd.glState.currenttmu = -1;
746 GL_SelectTexture( 0 );
748 qglEnableClientState( GL_TEXTURE_COORD_ARRAY );
750 qglDisable( GL_VERTEX_PROGRAM_ARB );
751 qglDisable( GL_REGISTER_COMBINERS_NV );
755 //======================================================================================
760 RB_NV20_DrawInteractions
763 void RB_NV20_DrawInteractions( void ) {
767 // for each light, perform adding and shadowing
769 for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) {
771 if ( vLight->lightShader->IsFogLight() ) {
774 if ( vLight->lightShader->IsBlendLight() ) {
777 if ( !vLight->localInteractions && !vLight->globalInteractions
778 && !vLight->translucentInteractions ) {
782 backEnd.vLight = vLight;
784 RB_LogComment( "---------- RB_RenderViewLight 0x%p ----------\n", vLight );
786 // clear the stencil buffer if needed
787 if ( vLight->globalShadows || vLight->localShadows ) {
788 backEnd.currentScissor = vLight->scissorRect;
789 if ( r_useScissor.GetBool() ) {
790 qglScissor( backEnd.viewDef->viewport.x1 + backEnd.currentScissor.x1,
791 backEnd.viewDef->viewport.y1 + backEnd.currentScissor.y1,
792 backEnd.currentScissor.x2 + 1 - backEnd.currentScissor.x1,
793 backEnd.currentScissor.y2 + 1 - backEnd.currentScissor.y1 );
795 qglClear( GL_STENCIL_BUFFER_BIT );
797 // no shadows, so no need to read or write the stencil buffer
798 // we might in theory want to use GL_ALWAYS instead of disabling
799 // completely, to satisfy the invarience rules
800 qglStencilFunc( GL_ALWAYS, 128, 255 );
803 backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
805 if ( r_useShadowVertexProgram.GetBool() ) {
806 qglEnable( GL_VERTEX_PROGRAM_ARB );
807 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
808 RB_StencilShadowPass( vLight->globalShadows );
809 RB_NV20_CreateDrawInteractions( vLight->localInteractions );
810 qglEnable( GL_VERTEX_PROGRAM_ARB );
811 qglBindProgramARB( GL_VERTEX_PROGRAM_ARB, VPROG_STENCIL_SHADOW );
812 RB_StencilShadowPass( vLight->localShadows );
813 RB_NV20_CreateDrawInteractions( vLight->globalInteractions );
814 qglDisable( GL_VERTEX_PROGRAM_ARB ); // if there weren't any globalInteractions, it would have stayed on
816 RB_StencilShadowPass( vLight->globalShadows );
817 RB_NV20_CreateDrawInteractions( vLight->localInteractions );
818 RB_StencilShadowPass( vLight->localShadows );
819 RB_NV20_CreateDrawInteractions( vLight->globalInteractions );
822 // translucent surfaces never get stencil shadowed
823 if ( r_skipTranslucent.GetBool() ) {
827 qglStencilFunc( GL_ALWAYS, 128, 255 );
829 backEnd.depthFunc = GLS_DEPTHFUNC_LESS;
830 RB_NV20_CreateDrawInteractions( vLight->translucentInteractions );
832 backEnd.depthFunc = GLS_DEPTHFUNC_EQUAL;
836 //=======================================================================
844 void R_NV20_Init( void ) {
845 glConfig.allowNV20Path = false;
847 common->Printf( "---------- R_NV20_Init ----------\n" );
849 if ( !glConfig.registerCombinersAvailable || !glConfig.ARBVertexProgramAvailable || glConfig.maxTextureUnits < 4 ) {
850 common->Printf( "Not available.\n" );
856 // create our "fragment program" display lists
857 fragmentDisplayListBase = qglGenLists( FPROG_NUM_FRAGMENT_PROGRAMS );
859 // force them to issue commands to build the list
860 bool temp = r_useCombinerDisplayLists.GetBool();
861 r_useCombinerDisplayLists.SetBool( false );
863 qglNewList( fragmentDisplayListBase + FPROG_BUMP_AND_LIGHT, GL_COMPILE );
864 RB_NV20_BumpAndLightFragment();
867 qglNewList( fragmentDisplayListBase + FPROG_DIFFUSE_COLOR, GL_COMPILE );
868 RB_NV20_DiffuseColorFragment();
871 qglNewList( fragmentDisplayListBase + FPROG_SPECULAR_COLOR, GL_COMPILE );
872 RB_NV20_SpecularColorFragment();
875 qglNewList( fragmentDisplayListBase + FPROG_DIFFUSE_AND_SPECULAR_COLOR, GL_COMPILE );
876 RB_NV20_DiffuseAndSpecularColorFragment();
879 r_useCombinerDisplayLists.SetBool( temp );
881 common->Printf( "---------------------------------\n" );
883 glConfig.allowNV20Path = true;