1 /*
2 * OpenBOR - http://www.LavaLit.com
3 * -----------------------------------------------------------------------
4 * All rights reserved, see LICENSE in OpenBOR root for details.
5 *
6 * Copyright (c) 2004 - 2011 OpenBOR Team
7 */
8
9 /************************ OpenGL video backend *************************/
10 /*
11 * New video backend that uses OpenGL as a cross-platform API for optimized
12 * hardware blitting and scaling. Supports both OpenGL 1.2+ and OpenGL ES 1.x.
13
14 * It also offers better fullscreen than software SDL because the picture is
15 * guaranteed to display at the correct aspect ratio and not have black bars on
16 * all 4 sides.
17 */
18
19 #include <SDL.h>
20 #include <SDL_framerate.h>
21 #include <math.h>
22 #include "openbor.h"
23 #include "opengl.h"
24 #include "video.h"
25 #include "sdlport.h"
26 #include "loadgl.h"
27
28 #define nextpowerof2(x) pow(2,ceil(log(x)/log(2)))
29 #define abs(x) ((x<0)?-(x):(x))
30
31 static SDL_Surface* screen = NULL;
32 static SDL_Surface* bscreen = NULL; // FIXME: unnecessary SDL dependency; this should be an s_screen
33
34 static int textureDepths[4] = {16,16,24,32};
35 static const char* glExtensions;
36
37 static float ycompress = 1.0;
38
39 static GLushort glpalette[256];
40 static GLuint gltexture;
41
42 static GLint textureTarget;
43 static GLint textureInternalColorFormat;
44 static GLint textureColorFormat;
45 static GLint texturePixelFormat;
46
47 static int viewportWidth, viewportHeight; // dimensions of display area
48 static int scaledWidth, scaledHeight; // dimensions of game screen after scaling
49 static int textureWidth, textureHeight; // dimensions of game screen and GL texture
50 static int xOffset, yOffset; // offset of game screen on display area
51 static int bytesPerPixel;
52
53 #ifdef GLES
54 static GLfixed tcx, tcy; // maximum x and y texture coords in fixed-point form
55 #else
56 static GLfloat tcx, tcy; // maximum x and y texture coords in floating-point form
57 #endif
58
59 // use some variables declared in video.c that are common to both backends
60 extern s_videomodes stored_videomodes;
61 extern FPSmanager framerate_manager;
62 extern int stretch;
63 extern int nativeWidth, nativeHeight;
64
65 // Macros for compatibility between OpenGL ES and normal OpenGL
66 #ifdef GLES
67 #define glOrtho(l,r,b,t,n,f) glOrthox((l)<<16,(r)<<16,(b)<<16,(t)<<16,(n)<<16,(f)<<16)
68 #define glTexParameter glTexParameterx
69 #define BOR_UNSIGNED_SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5
70 #define BOR_CLAMP GL_CLAMP_TO_EDGE
71 #else
72 #define glTexParameter glTexParameteri
73 #define BOR_UNSIGNED_SHORT_5_6_5 GL_UNSIGNED_SHORT_5_6_5_REV
74 #define BOR_CLAMP GL_CLAMP
75 #endif
76
77 // calculates scale factors and offsets based on viewport and texture sizes
video_gl_setup_screen()78 void video_gl_setup_screen()
79 {
80 // set up the viewport
81 glViewport(0, 0, viewportWidth, viewportHeight);
82
83 // set up orthographic projection
84 glMatrixMode(GL_PROJECTION);
85 glLoadIdentity();
86 glOrtho(0, viewportWidth-1, 0, viewportHeight-1, -1, 1);
87
88 // reset the model view
89 glMatrixMode(GL_MODELVIEW);
90 glLoadIdentity();
91 }
92
93 // initializes the OpenGL texture for the game screen
video_gl_init_textures()94 void video_gl_init_textures()
95 {
96 int allocTextureWidth, allocTextureHeight;
97 bool npotTexturesSupported;
98
99 // set texture target, format, etc.
100 textureTarget = GL_TEXTURE_2D;
101 textureColorFormat = (bytesPerPixel == 4) ? GL_RGBA : GL_RGB;
102 textureInternalColorFormat = textureColorFormat;
103 texturePixelFormat = (bytesPerPixel <= 2) ? BOR_UNSIGNED_SHORT_5_6_5 : GL_UNSIGNED_BYTE;
104
105 // enable 2D textures
106 glEnable(textureTarget);
107
108 // detect support for non-power-of-two texture dimensions
109 #ifdef GLES
110 npotTexturesSupported = strstr(glExtensions, "GL_IMG_texture_npot") || strstr(glExtensions, "GL_OES_texture_npot");
111 #else
112 npotTexturesSupported = strstr(glExtensions, "GL_ARB_texture_non_power_of_two") ? true : false;
113 #endif
114
115 /*
116 * FIXME: NPOT textures are disabled for now because they cause a software fallback with
117 * a few drivers for older Nvidia cards. But POT textures eat up more memory than
118 * is necessary on computers with NPOT texture support. There has to be a
119 * better solution to this problem than just disabling them for everyone...the
120 * "GL_*_texture_rectangle" extensions might solve the problem in some cases.
121 */
122 npotTexturesSupported = false;
123
124 if(npotTexturesSupported)
125 {
126 allocTextureWidth = textureWidth;
127 allocTextureHeight = textureHeight;
128 }
129 else
130 {
131 allocTextureWidth = nextpowerof2(textureWidth);
132 allocTextureHeight = nextpowerof2(textureHeight);
133 }
134
135 // calculate maximum texture coordinates
136 #ifdef GLES
137 tcx = (textureWidth == 0) ? 0 : (textureWidth<<16) / allocTextureWidth;
138 tcy = (textureHeight == 0) ? 0 : (textureHeight<<16) / allocTextureHeight;
139 #else
140 tcx = (textureWidth == 0) ? 0 : (float)textureWidth / (float)allocTextureWidth;
141 tcy = (textureHeight == 0) ? 0 : (float)textureHeight / (float)allocTextureHeight;
142 #endif
143
144 // allocate a surface to initialize the texture with
145 bscreen = SDL_AllocSurface(SDL_SWSURFACE, allocTextureWidth, allocTextureHeight, textureDepths[bytesPerPixel-1], 0,0,0,0);
146
147 // create texture object
148 glDeleteTextures(1, &gltexture);
149 glGenTextures(1, &gltexture);
150 glBindTexture(textureTarget, gltexture);
151 glTexImage2D(textureTarget, 0, textureInternalColorFormat, allocTextureWidth,
152 allocTextureHeight, 0, textureColorFormat, texturePixelFormat, bscreen->pixels);
153 glTexParameter(textureTarget, GL_TEXTURE_WRAP_S, BOR_CLAMP);
154 glTexParameter(textureTarget, GL_TEXTURE_WRAP_T, BOR_CLAMP);
155
156 // we don't need bscreen anymore, except in 8-bit mode
157 SDL_FreeSurface(bscreen);
158 bscreen = NULL;
159
160 if(bytesPerPixel == 1) bscreen = SDL_AllocSurface(SDL_SWSURFACE, textureWidth, textureHeight, 16, 0,0,0,0);
161 }
162
video_gl_set_mode(s_videomodes videomodes)163 int video_gl_set_mode(s_videomodes videomodes)
164 {
165 GLint maxTextureSize;
166
167 bytesPerPixel = videomodes.pixel;
168 textureWidth = videomodes.hRes;
169 textureHeight = videomodes.vRes;
170
171 // use the current monitor resolution in fullscreen mode to prevent aspect ratio distortion
172 viewportWidth = savedata.fullscreen ? nativeWidth : (int)(videomodes.hRes * MAX(0.25,savedata.glscale));
173 viewportHeight = savedata.fullscreen ? nativeHeight : (int)(videomodes.vRes * MAX(0.25,savedata.glscale));
174
175 // zero width/height means close the window, not make it enormous!
176 if((viewportWidth == 0) || (viewportHeight == 0)) return 0;
177
178 // set up OpenGL double buffering
179 if(SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0)
180 {
181 printf("Can't set up OpenGL double buffering (%s)...", SDL_GetError());
182 goto error;
183 }
184
185 // try to disable vertical retrace syncing (VSync)
186 #ifdef SDL13
187 if(SDL_GL_SetSwapInterval(0) < 0)
188 #else
189 if(SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 0) != 0)
190 #endif
191 {
192 printf("Warning: can't disable vertical retrace sync (%s)\n", SDL_GetError());
193 }
194
195 // free existing surfaces
196 // if(screen) SDL_FreeAndNullVideoSurface(screen);
197 if(bscreen) { SDL_FreeSurface(bscreen); bscreen=NULL; }
198
199 // create main video surface and initialize OpenGL context
200 screen = SDL_SetVideoMode(viewportWidth, viewportHeight, 0, savedata.fullscreen ? (SDL_OPENGL|SDL_FULLSCREEN) : SDL_OPENGL);
201
202 // make sure the surface was created successfully
203 if(!screen)
204 {
205 printf("OpenGL initialization failed (%s)...", SDL_GetError());
206 goto error;
207 }
208
209 // update viewport size based on actual dimensions
210 viewportWidth = screen->w;
211 viewportHeight = screen->h;
212
213 #ifdef LOADGL
214 // load OpenGL functions dynamically in Linux/Windows/OSX
215 if(LoadGLFunctions() == 0) goto error;
216 #endif
217
218 // reject the MesaGL software renderer (swrast) because it's much slower than SDL software blitting
219 if(stricmp((const char*)glGetString(GL_RENDERER), "Software Rasterizer") == 0)
220 {
221 printf("Not going to use the Mesa software renderer...");
222 goto error;
223 }
224
225 // get the list of available extensions
226 glExtensions = (const char*)glGetString(GL_EXTENSIONS);
227
228 // don't try to create a texture larger than the maximum allowed dimensions
229 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
230 if((textureWidth > maxTextureSize) || (textureHeight > maxTextureSize))
231 {
232 printf("Unable to create a %ix%i OpenGL texture (max texture size %i)...", textureWidth, textureHeight, maxTextureSize);
233 goto error;
234 }
235
236 // set background to black
237 glClearColor(0.0, 0.0, 0.0, 0.0);
238 glClear(GL_COLOR_BUFFER_BIT);
239
240 // disable unneeded features
241 glDisable(GL_BLEND);
242 glDisable(GL_LIGHTING);
243 glDisable(GL_DEPTH_TEST);
244 glDisable(GL_DITHER);
245
246 #ifdef GLES
247 // enable vertex/texcoord arrays in OpenGL ES since we can't use glBegin()/glEnd()
248 glEnableClientState(GL_VERTEX_ARRAY);
249 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
250 #endif
251
252 // initialize texture object
253 video_gl_init_textures();
254
255 // set up offsets, scale factors, and viewport
256 video_gl_setup_screen();
257
258 // FIXME: don't use savedata for this; use the previous brightness and gamma (will need to coordinate with software SDL)
259 video_gl_set_color_correction(savedata.gamma, savedata.brightness);
260
261 opengl = 1;
262 return 1;
263 error:
264 printf("falling back on SDL video backend\n");
265 if(screen) SDL_FreeAndNullVideoSurface(screen);
266 if(bscreen) { SDL_FreeSurface(bscreen); bscreen=NULL; }
267 opengl = 0;
268 savedata.usegl[savedata.fullscreen] = 0;
269 return 0;
270 }
271
video_gl_clearscreen()272 void video_gl_clearscreen()
273 {
274 // clear both buffers in a double-buffered setup
275 glClear(GL_COLOR_BUFFER_BIT);
276 SDL_GL_SwapBuffers();
277 glClear(GL_COLOR_BUFFER_BIT);
278 SDL_GL_SwapBuffers();
279 }
280
video_gl_fullscreen_flip()281 void video_gl_fullscreen_flip()
282 {
283 savedata.fullscreen ^= 1;
284 video_set_mode(stored_videomodes);
285 }
286
video_gl_draw_quad(int x,int y,int width,int height)287 void video_gl_draw_quad(int x, int y, int width, int height)
288 {
289 #ifdef GLES
290 // OpenGL ES supports neither quads nor glBegin()/glEnd() :(
291 GLshort box[] = {x,y, x+width-1,y, x+width-1,y+height-1, x,y+height-1};
292 GLfixed tex[] = {0,tcy, tcx,tcy, tcx,0, 0,0, };
293 GLubyte indices[] = {0,1,3,2};
294
295 glActiveTexture(GL_TEXTURE0);
296 glTexCoordPointer(2, GL_FIXED, 0, tex);
297 glActiveTexture(GL_TEXTURE1);
298 glTexCoordPointer(2, GL_FIXED, 0, tex);
299 glVertexPointer(2, GL_SHORT, 0, box);
300 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices);
301 #else
302 glBegin(GL_QUADS);
303 // Top left
304 glMultiTexCoord2f(GL_TEXTURE0, 0.0, tcy);
305 glMultiTexCoord2f(GL_TEXTURE1, 0.0, tcy);
306 glVertex2i(x, y);
307
308 // Top right
309 glMultiTexCoord2f(GL_TEXTURE0, tcx, tcy);
310 glMultiTexCoord2f(GL_TEXTURE1, tcx, tcy);
311 glVertex2i(x+width-1, y);
312
313 // Bottom right
314 glMultiTexCoord2f(GL_TEXTURE0, tcx, 0.0);
315 glMultiTexCoord2f(GL_TEXTURE1, tcx, 0.0);
316 glVertex2i(x+width-1, y+height-1);
317
318 // Bottom left
319 glMultiTexCoord2f(GL_TEXTURE0, 0.0, 0.0);
320 glMultiTexCoord2f(GL_TEXTURE1, 0.0, 0.0);
321 glVertex2i(x, y+height-1);
322 glEnd();
323 #endif
324 }
325
video_gl_copy_screen(s_screen * src)326 int video_gl_copy_screen(s_screen* src)
327 {
328 unsigned char *sp;
329 unsigned char *dp;
330 int width, height, linew, slinew;
331 int h, i;
332 float texScale;
333 SDL_Surface* ds = NULL;
334
335 // Convert 8-bit s_screen to a 16-bit SDL_Surface
336 if(bscreen)
337 {
338 if(SDL_MUSTLOCK(bscreen)) SDL_LockSurface(bscreen);
339
340 width = bscreen->w;
341 if(width > src->width) width = src->width;
342 height = bscreen->h;
343 if(height > src->height) height = src->height;
344 if(!width || !height) return 0;
345 h = height;
346
347 sp = (unsigned char*)src->data;
348 ds = bscreen;
349 dp = ds->pixels;
350
351 linew = width*bytesPerPixel;
352 slinew = src->width*bytesPerPixel;
353
354 do{
355 //u16pcpy((unsigned short*)dp, sp, glpalette, linew);
356 i=linew-1;
357 do
358 {
359 ((unsigned short*)dp)[i] = glpalette[sp[i]];
360 }while(i--);
361 sp += slinew;
362 dp += ds->pitch;
363 }while(--h);
364
365 if(SDL_MUSTLOCK(bscreen)) SDL_UnlockSurface(bscreen);
366 }
367
368 // update texture contents with new surface contents
369 glActiveTexture(GL_TEXTURE0);
370 glBindTexture(textureTarget, gltexture);
371 glTexSubImage2D(textureTarget, 0, 0, 0, textureWidth, textureHeight, textureColorFormat,
372 texturePixelFormat, bscreen ? bscreen->pixels : src->data);
373
374 // determine x and y scale factors
375 texScale = MIN((float)viewportWidth/(float)textureWidth, (float)viewportHeight/(float)textureHeight);
376
377 // determine on-screen dimensions
378 scaledWidth = (int)(textureWidth * texScale);
379 scaledHeight = (int)(textureHeight * texScale) * ycompress;
380
381 // determine offsets
382 xOffset = (viewportWidth - scaledWidth) / 2;
383 yOffset = (viewportHeight - scaledHeight) / 2;
384
385 // set texture scaling type
386 if((savedata.glfilter[savedata.fullscreen]) || (textureWidth == (stretch ? viewportWidth : scaledWidth)))
387 {
388 glTexParameter(textureTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
389 glTexParameter(textureTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
390 }
391 else
392 {
393 glTexParameter(textureTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
394 glTexParameter(textureTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
395 }
396
397 // render the quad
398 if(stretch)
399 video_gl_draw_quad(0, 0, viewportWidth, viewportHeight);
400 else
401 video_gl_draw_quad(xOffset, yOffset, scaledWidth, scaledHeight);
402
403 // blit the image to the screen
404 SDL_GL_SwapBuffers();
405
406 #if WIN || LINUX
407 // limit framerate to 200 fps
408 SDL_framerateDelay(&framerate_manager);
409 #endif
410
411 return 1;
412 }
413
video_gl_setpalette(unsigned char * pal)414 void video_gl_setpalette(unsigned char* pal)
415 {
416 int i;
417 for(i=0; i<256; i++)
418 {
419 glpalette[i] = colour16(pal[0], pal[1], pal[2]);
420 pal += 3;
421 }
422 }
423
424 // Set the brightness and gamma values
video_gl_set_color_correction(int gamma,int brightness)425 void video_gl_set_color_correction(int gamma, int brightness)
426 {
427 int numTexEnvStages = 0;
428
429 if(gamma < -256) gamma = -256;
430 if(gamma > 256) gamma = 256;
431 if(brightness < -256) brightness = -256;
432 if(brightness > 256) brightness = 256;
433 glColor4f(abs(gamma / 256.0), abs(gamma / 256.0), abs(gamma / 256.0), abs(brightness / 256.0));
434
435 #if GLES
436 if(gamma)
437 #else
438 if(strstr(glExtensions, "GL_ARB_texture_env_combine"))
439 #endif
440 {
441 if(gamma > 0)
442 {
443 numTexEnvStages = 3;
444
445 // first stage: c*g
446 glActiveTexture(GL_TEXTURE0);
447 glBindTexture(textureTarget, gltexture);
448 glEnable(textureTarget);
449 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
450 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
451 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
452 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
453 glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR_ARB);
454 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
455
456 // second stage: (1.0-c)*(1.0-prev) = (1.0-c)*(1.0-(c*g))
457 glActiveTexture(GL_TEXTURE1);
458 glBindTexture(textureTarget, gltexture);
459 glEnable(textureTarget);
460 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
461 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
462 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
463 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
464 glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
465 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
466
467 // third stage: 1.0-prev = 1.0-((1.0-c)*(1.0-(c*g)))
468 glActiveTexture(GL_TEXTURE2);
469 glBindTexture(textureTarget, gltexture);
470 glEnable(textureTarget);
471 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
472 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_REPLACE);
473 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS);
474 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
475 }
476 else if(gamma < 0)
477 {
478 numTexEnvStages = 2;
479
480 // first stage: (1.0-c)*g
481 glActiveTexture(GL_TEXTURE0);
482 glBindTexture(textureTarget, gltexture);
483 glEnable(textureTarget);
484 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
485 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
486 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
487 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_ONE_MINUS_SRC_COLOR);
488 glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR_ARB);
489 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
490
491 // second stage: c*(1.0-prev) = c*(1.0-((1.0-c)*g))
492 glActiveTexture(GL_TEXTURE1);
493 glBindTexture(textureTarget, gltexture);
494 glEnable(textureTarget);
495 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
496 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE);
497 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE);
498 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
499 glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PREVIOUS);
500 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_ONE_MINUS_SRC_COLOR);
501
502 // third stage: disabled
503 glActiveTexture(GL_TEXTURE2);
504 glBindTexture(textureTarget, 0);
505 glDisable(textureTarget);
506 }
507 else // gamma == 0 -> no gamma correction
508 {
509 numTexEnvStages = 0;
510
511 // first stage: sampled color from texture
512 glActiveTexture(GL_TEXTURE0);
513 glBindTexture(textureTarget, gltexture);
514 glEnable(textureTarget);
515 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
516
517 // second stage: disabled
518 glActiveTexture(GL_TEXTURE1);
519 glBindTexture(textureTarget, 0);
520 glDisable(textureTarget);
521
522 // third stage: disabled
523 glActiveTexture(GL_TEXTURE2);
524 glBindTexture(textureTarget, 0);
525 glDisable(textureTarget);
526 }
527 }
528
529 if(brightness)
530 {
531 // brightness correction
532 GLfloat blendColor = brightness > 0 ? 1.0 : 0.0; // white (positive brightess) or black (0 or negative)
533 GLfloat rgba[4] = {blendColor, blendColor, blendColor, 1.0};
534
535 glActiveTexture(GL_TEXTURE0 + numTexEnvStages);
536 glBindTexture(textureTarget, gltexture);
537 glEnable(textureTarget);
538 glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, rgba);
539 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
540 glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE);
541 glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, numTexEnvStages > 0 ? GL_PREVIOUS : GL_TEXTURE);
542 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, GL_SRC_COLOR);
543 glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT);
544 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB, GL_SRC_COLOR);
545 glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PRIMARY_COLOR);
546 glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_ALPHA);
547 }
548
549 //fprintf(stderr, "set brightness=%d and gamma=%d in %d texenv stages\n", brightness, gamma, numTexEnvStages + 1);
550 }
551
552
553