804eb5647717e30a57cfb1d7eafed17cc780c434
[summerhack] / src / 3dengfx / src / 3dengfx / 3denginefx.cpp
1 /*
2 This file is part of the 3dengfx, realtime visualization system.
3 Copyright (c) 2004, 2005 John Tsiombikas <nuclear@siggraph.org>
4
5 3dengfx is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 3dengfx is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with 3dengfx; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19
20 /* main 3dengfx state control, and low level OpenGL interaction
21  *
22  * Author: John Tsiombikas 2004
23  */
24
25 #include "3dengfx_config.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <signal.h>
30 #include <iostream>
31 #include <list>
32 #include "opengl.h"
33 #include "fxwt/fxwt.hpp"
34 #include "fxwt/init.hpp"
35 #include "fxwt/gfx_library.h"
36 #include "3denginefx.hpp"
37 #include "texman.hpp"
38 #include "sdrman.hpp"
39 #include "camera.hpp"
40 #include "gfx/3dgeom.hpp"
41 #include "gfxprog.hpp"
42 #include "gfx/image.h"
43 #include "common/config_parser.h"
44 #include "common/err_msg.h"
45 #include "dsys/dsys.hpp"
46
47 using std::cout;
48 using std::cerr;
49 using std::endl;
50 using std::string;
51
52 #ifdef SINGLE_PRECISION_MATH
53 #define GL_SCALAR_TYPE  GL_FLOAT
54 #else
55 #define GL_SCALAR_TYPE  GL_DOUBLE
56 #endif  // SINGLE_PRECISION_MATH
57
58
59 void (*load_matrix_gl)(const Matrix4x4 &mat);
60
61 namespace glext {
62 #ifdef SINGLE_PRECISION_MATH
63         PFNGLLOADTRANSPOSEMATRIXFARBPROC glLoadTransposeMatrix;
64 #else
65         PFNGLLOADTRANSPOSEMATRIXDARBPROC glLoadTransposeMatrix;
66 #endif  // SINGLE_PRECISION_MATH
67
68         PFNGLACTIVETEXTUREARBPROC glActiveTexture;
69         PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTexture;
70
71         PFNGLBINDBUFFERARBPROC glBindBuffer;
72         PFNGLBUFFERDATAARBPROC glBufferData;
73         PFNGLDELETEBUFFERSARBPROC glDeleteBuffers;
74         PFNGLISBUFFERARBPROC glIsBuffer;
75         PFNGLMAPBUFFERARBPROC glMapBuffer;
76         PFNGLUNMAPBUFFERARBPROC glUnmapBuffer;
77         PFNGLGENBUFFERSARBPROC glGenBuffers;
78
79         // fragment/vertex program extensions
80         PFNGLBINDPROGRAMARBPROC glBindProgram;
81         PFNGLGENPROGRAMSARBPROC glGenPrograms;
82         PFNGLDELETEPROGRAMSARBPROC glDeletePrograms;
83         PFNGLPROGRAMSTRINGARBPROC glProgramString;
84
85         // point parameters
86         PFNGLPOINTPARAMETERFARBPROC glPointParameterf;
87         PFNGLPOINTPARAMETERFVARBPROC glPointParameterfv;
88
89         // --- OpenGL 2.0 Shading Language ---
90         
91         // - objects
92         PFNGLDELETEOBJECTARBPROC glDeleteObject;
93         PFNGLATTACHOBJECTARBPROC glAttachObject;
94         PFNGLDETACHOBJECTARBPROC glDetachObject;
95         PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameteriv;
96         PFNGLGETINFOLOGARBPROC glGetInfoLog;
97
98         // - program objects
99         PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObject;
100         PFNGLLINKPROGRAMARBPROC glLinkProgram;
101         PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObject;
102
103         // - shader objects
104         PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObject;
105         PFNGLSHADERSOURCEARBPROC glShaderSource;
106         PFNGLCOMPILESHADERARBPROC glCompileShader;
107
108         // - uniforms
109         PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation;
110         PFNGLGETACTIVEUNIFORMARBPROC glGetActiveUniform;
111         PFNGLUNIFORM1IARBPROC glUniform1i;
112         PFNGLUNIFORM1FARBPROC glUniform1f;
113         PFNGLUNIFORM2FARBPROC glUniform2f;
114         PFNGLUNIFORM3FARBPROC glUniform3f;
115         PFNGLUNIFORM4FARBPROC glUniform4f;
116         PFNGLUNIFORMMATRIX3FVARBPROC glUniformMatrix3fv;
117         PFNGLUNIFORMMATRIX4FVARBPROC glUniformMatrix4fv;
118 }
119
120 using namespace glext;
121
122 static const char *gl_error_string[] = {
123         "GL_INVALID_ENUM",              // 0x500
124         "GL_INVALID_VALUE",             // 0x501
125         "GL_INVALID_OPERATION", // 0x502
126         "GL_STACK_OVERFLOW",    // 0x503
127         "GL_STACK_UNDERFLOW",   // 0x504
128         "GL_OUT_OF_MEMORY",             // 0x505
129         "GL_NO_ERROR",                  // 0x0
130         "[INVALID ERROR NUMBER]"
131 };
132
133 ///////////////// local 3d engine state block ///////////////////
134 static bool gc_valid;
135 static GraphicsInitParameters gparams;
136 static Matrix4x4 tex_matrix[8];
137 static int coord_index[MAX_TEXTURES];
138 static PrimitiveType primitive_type;
139 static StencilOp stencil_fail, stencil_pass, stencil_pzfail;
140 static int stencil_ref;
141 static bool mipmapping = true;
142 static TextureDim ttype[8];     // the type of each texture bound to each texunit (1D/2D/3D/CUBE)
143
144 namespace engfx_state {
145         SysCaps sys_caps;
146         Matrix4x4 world_matrix;
147         Matrix4x4 view_matrix, inv_view_matrix;
148         const Camera *view_mat_camera;
149         Matrix4x4 proj_matrix;
150         const Light *bump_light;
151         int light_count;
152 }
153
154 using namespace engfx_state;
155
156 GraphicsInitParameters *load_graphics_context_config(const char *fname) {
157         static GraphicsInitParameters gip;
158         gip.x = 640;
159         gip.y = 480;
160         gip.bpp = 16;
161         gip.depth_bits = 16;
162         gip.stencil_bits = 8;
163         gip.dont_care_flags = 0;
164
165         if(load_config_file(fname) == -1) {
166                 error("%s: could not load config file", __func__);
167                 return 0;
168         }
169
170         const ConfigOption *cfgopt;
171         while((cfgopt = get_next_option())) {
172
173                 if(!strcmp(cfgopt->option, "fullscreen")) {
174                         if(!strcmp(cfgopt->str_value, "true")) {
175                                 gip.fullscreen = true;
176                         } else if(!strcmp(cfgopt->str_value, "false")) {
177                                 gip.fullscreen = false;
178                         } else {
179                                 error("%s: error parsing config file %s", __func__, fname);
180                                 return 0;
181                         }
182                 } else if(!strcmp(cfgopt->option, "resolution")) {
183                         if(!strcmp(cfgopt->str_value, "dontcare")) {
184                                 gip.x = 1024;
185                                 gip.y = 768;
186                                 gip.dont_care_flags |= DONT_CARE_SIZE;
187                         } else {
188                                 if(!isdigit(cfgopt->str_value[0])) {
189                                         error("%s: error parsing config file %s", __func__, fname);
190                                         return 0;
191                                 }
192                                 gip.x = atoi(cfgopt->str_value);
193
194                                 char *ptr = cfgopt->str_value;
195                                 while(*ptr && *ptr != 'x') ptr++;
196                                 if(!*ptr || !*(ptr+1) || !isdigit(*(ptr+1))) {
197                                         error("%s: error parsing config file %s", __func__, fname);
198                                         return 0;
199                                 }
200
201                                 gip.y = atoi(ptr + 1);
202                         }
203                 } else if(!strcmp(cfgopt->option, "bpp")) {
204                         if(cfgopt->flags & CFGOPT_INT) {
205                                 gip.bpp = cfgopt->int_value;
206                         } else if(!strcmp(cfgopt->str_value, "dontcare")) {
207                                 gip.bpp = 32;
208                                 gip.dont_care_flags |= DONT_CARE_BPP;
209                         } else {
210                                 error("%s: error parsing config file %s", __func__, fname);
211                                 return 0;
212                         }
213                 } else if(!strcmp(cfgopt->option, "zbuffer")) {
214                         if(cfgopt->flags & CFGOPT_INT) {
215                                 gip.depth_bits = cfgopt->int_value;
216                         } else if(!strcmp(cfgopt->str_value, "dontcare")) {
217                                 gip.depth_bits = 32;
218                                 gip.dont_care_flags |= DONT_CARE_DEPTH;
219                         } else {
220                                 error("%s: error parsing config file %s", __func__, fname);
221                                 return 0;
222                         }
223                 } else if(!strcmp(cfgopt->option, "stencil")) {
224                         if(cfgopt->flags & CFGOPT_INT) {
225                                 gip.stencil_bits = cfgopt->int_value;
226                         } else if(!strcmp(cfgopt->str_value, "dontcare")) {
227                                 gip.stencil_bits = 8;
228                                 gip.dont_care_flags |= DONT_CARE_STENCIL;
229                         } else {
230                                 error("%s: error parsing config file %s", __func__, fname);
231                                 return 0;
232                         }
233                 }
234         }
235         
236         destroy_config_parser();
237         
238         return &gip;            
239 }
240
241 /* ---- get_system_capabilities() ----
242  * Retrieves information on the graphics subsystem capabilities
243  * and returns a SysCaps structure describing them
244  */
245 SysCaps get_system_capabilities() {
246         static bool first_call = true;
247         
248         if(!first_call) {
249                 return sys_caps;
250         }
251         first_call = false;
252         
253         // get extensions & vendor strings
254         const char *tmp_str = (const char*)glGetString(GL_EXTENSIONS);
255         if(!tmp_str) {
256                 error("%s: glGetString() failed, possibly no valid GL context", __func__);
257                 exit(-1);
258         }
259         char *ext_str = new char[strlen(tmp_str) + 1];
260         strcpy(ext_str, tmp_str);
261         
262         char *cptr = ext_str;   
263         while(*cptr) {
264                 if(*cptr == ' ') *cptr = '\n';
265                 cptr++;
266         }
267
268         set_verbosity(2);
269         info("Supported extensions:\n-------------\n%s", ext_str);
270         set_verbosity(3);
271                 
272         info("Rendering System Information:");
273
274         const char *vendor = (const char*)glGetString(GL_VENDOR);
275         info("  Vendor: %s", vendor);
276         info("Renderer: %s", glGetString(GL_RENDERER));
277         info(" Version: %s", glGetString(GL_VERSION));
278         info("(note: the list of extensions is logged seperately at \"gl_ext.log\")");
279
280         // fill the SysCaps structure
281         //SysCaps sys_caps;
282         sys_caps.multitex = (bool)strstr(ext_str, "GL_ARB_multitexture");
283         sys_caps.load_transpose = (bool)strstr(ext_str, "GL_ARB_transpose_matrix");
284         sys_caps.gen_mipmaps = (bool)strstr(ext_str, "GL_SGIS_generate_mipmap");
285         sys_caps.tex_combine_ops = (bool)strstr(ext_str, "GL_ARB_texture_env_combine");
286         sys_caps.bump_dot3 = (bool)strstr(ext_str, "GL_ARB_texture_env_dot3");
287         sys_caps.bump_env = (bool)strstr(ext_str, "GL_ATI_envmap_bumpmap");
288         sys_caps.vertex_buffers = (bool)strstr(ext_str, "GL_ARB_vertex_buffer_object");
289         sys_caps.depth_texture = (bool)strstr(ext_str, "GL_ARB_depth_texture");
290         sys_caps.shadow_mapping = (bool)strstr(ext_str, "GL_ARB_shadow");
291         sys_caps.point_sprites = (bool)strstr(ext_str, "GL_ARB_point_sprite");
292         sys_caps.point_params = (bool)strstr(ext_str, "GL_ARB_point_parameters");
293         glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &sys_caps.max_texture_units);
294         sys_caps.non_power_of_two_textures = (bool)strstr(ext_str, "GL_ARB_texture_non_power_of_two");
295         glGetIntegerv(GL_MAX_LIGHTS, &sys_caps.max_lights);
296         
297         sys_caps.prog.asm_vertex = (bool)strstr(ext_str, "GL_ARB_vertex_program");
298         sys_caps.prog.asm_pixel = (bool)strstr(ext_str, "GL_ARB_fragment_program");
299         sys_caps.prog.glslang = (bool)strstr(ext_str, "GL_ARB_shading_language_100");
300         sys_caps.prog.shader_obj = (bool)strstr(ext_str, "GL_ARB_shader_objects");
301         sys_caps.prog.glsl_vertex = (bool)strstr(ext_str, "GL_ARB_vertex_shader");
302         sys_caps.prog.glsl_pixel = (bool)strstr(ext_str, "GL_ARB_fragment_shader");
303
304         delete [] ext_str;
305         
306         // also log these things
307         info("-------------------");
308         info("System Capabilities");
309         info("-------------------");
310         info("Load transposed matrices: %s", sys_caps.load_transpose ? "yes" : "no");
311         info("Auto-generate mipmaps (SGIS): %s", sys_caps.gen_mipmaps ? "yes" : "no");
312         info("Custom texture combination operations: %s", sys_caps.tex_combine_ops ? "yes" : "no");
313         info("Diffuse bump mapping (dot3): %s", sys_caps.bump_dot3 ? "yes" : "no");
314         info("Specular bump mapping (env-bump): %s", sys_caps.bump_env ? "yes" : "no");
315         info("Video memory vertex/index buffers: %s", sys_caps.vertex_buffers ? "yes" : "no");
316         info("Depth texture: %s", sys_caps.depth_texture ? "yes" : "no");
317         info("Shadow mapping: %s", sys_caps.shadow_mapping ? "yes" : "no");
318         info("Programmable vertex processing (asm): %s", sys_caps.prog.asm_vertex ? "yes" : "no");
319         info("Programmable pixel processing (asm): %s", sys_caps.prog.asm_pixel ? "yes" : "no");
320         info("OpenGL 2.0 shading language: %s", sys_caps.prog.glslang ? "yes" : "no");
321         info("Programmable vertex processing (glsl): %s", sys_caps.prog.glsl_vertex ? "yes" : "no");
322         info("Programmable pixel processing (glsl): %s", sys_caps.prog.glsl_pixel ? "yes" : "no");
323         info("Point sprites: %s", sys_caps.point_sprites ? "yes" : "no");
324         info("Point parameters: %s", sys_caps.point_params ? "yes" : "no");
325         info("Non power of 2 textures: %s", sys_caps.non_power_of_two_textures ? "yes" : "no");
326         info("Texture units: %d", sys_caps.max_texture_units);
327         info("Max lights: %d", sys_caps.max_lights);
328
329         if(!sys_caps.point_sprites && !sys_caps.point_params) {
330                 warning("no point sprites support, falling back to billboards which *may* degrade particle system performance");
331         }
332
333         return sys_caps;
334 }
335
336 const char *get_glerror_string(GLenum error) {
337         if(!error) return gl_error_string[0x506];
338         if(error < 0x500 || error > 0x505) error = 0x507;
339         return gl_error_string[error - 0x500];
340 }
341
342 /* load_matrix_transpose_arb() & load_matrix_transpose_manual()
343  * --------------------------------------------------------
344  * two functions to handle the transformation matrix loading
345  * to OpenGL by either transposing the Matrix4x4 data or using
346  * the transposed-loading extension (use through function pointer
347  * LoadMatrixGL which is set during initialization to the correct one)
348  */
349 void load_matrix_transpose_arb(const Matrix4x4 &mat) {
350         glLoadTransposeMatrix(mat.opengl_matrix());
351 }
352
353 void load_matrix_transpose_manual(const Matrix4x4 &mat) {
354 #ifdef SINGLE_PRECISION_MATH
355         glLoadMatrixf(mat.transposed().opengl_matrix());
356 #else
357         glLoadMatrixd(mat.transposed().opengl_matrix());
358 #endif  // SINGLE_PRECISION_MATH
359 }
360
361
362 //////////////// 3D Engine Initialization ////////////////
363
364 static const char *signame(int sig) {
365         switch(sig) {
366         case SIGSEGV:
367                 return "segmentation fault (SIGSEGV)";
368         case SIGILL:
369                 return "illegal instruction (SIGILL)";
370         case SIGTERM:
371                 return "termination signal (SIGTERM)";
372         case SIGFPE:
373                 return "floating point exception (SIGFPE)";
374         case SIGINT:
375                 return "interrupt signal (SIGINT)";
376         default:
377                 return "unknown";
378         }
379         return "can't happen";
380 }
381
382 static void signal_handler(int sig) {
383         error("It seems this is the end... caught %s, exiting...", signame(sig));
384         destroy_graphics_context();
385         exit(EXIT_FAILURE);
386 }
387
388 /* ---- create_graphics_context() ----
389  * initializes the graphics subsystem according to the init parameters
390  */
391 bool create_graphics_context(const GraphicsInitParameters &gip) {
392         
393         gparams = gip;
394
395         remove(get_log_filename());
396
397         if(!fxwt::init_graphics(&gparams)) {
398                 return false;
399         }
400
401         signal(SIGSEGV, signal_handler);
402         signal(SIGILL, signal_handler);
403         signal(SIGTERM, signal_handler);
404         signal(SIGFPE, signal_handler);
405         signal(SIGINT, signal_handler);
406
407 #if GFX_LIBRARY == GTK
408         fxwt::init();
409         dsys::init();
410         return true;
411 #else
412         if(!start_gl()) return false;
413         fxwt::init();
414         dsys::init();
415         return true;
416 #endif  // GTK
417 }
418
419 /*
420  * short graphics context creation
421  * creates a graphics context (windowed or fullscreen)
422  * given only the wanted resolution and a fullscreen flag.
423  */
424 bool create_graphics_context(int x, int y, bool fullscreen)
425 {
426         GraphicsInitParameters gip;
427         gip.x = x;
428         gip.y = y;
429         gip.bpp = 32;
430         gip.depth_bits = 32;
431         gip.fullscreen = fullscreen;
432         gip.stencil_bits = 8;
433         gip.dont_care_flags = DONT_CARE_DEPTH | DONT_CARE_STENCIL | DONT_CARE_BPP;
434
435         return create_graphics_context(gip);
436 }
437
438 /* OpenGL startup after initialization */
439 bool start_gl() {
440         SysCaps sys_caps = get_system_capabilities();
441
442         glext::glActiveTexture = (PFNGLACTIVETEXTUREARBPROC)glGetProcAddress("glActiveTextureARB");
443         glext::glClientActiveTexture = (PFNGLCLIENTACTIVETEXTUREARBPROC)glGetProcAddress("glClientActiveTextureARB");
444         
445         if(!glext::glActiveTexture || !glext::glClientActiveTexture) {
446                 warning("No multitexturing support.");
447                 sys_caps.multitex = false;
448         }
449
450         if(sys_caps.load_transpose) {
451 #ifdef SINGLE_PRECISION_MATH
452                 glLoadTransposeMatrix = (PFNGLLOADTRANSPOSEMATRIXFARBPROC)glGetProcAddress("glLoadTransposeMatrixfARB");
453 #else
454                 glLoadTransposeMatrix = (PFNGLLOADTRANSPOSEMATRIXDARBPROC)glGetProcAddress("glLoadTransposeMatrixdARB");
455 #endif  // SINGLE_PRECISION_MATH
456                 
457                 load_matrix_gl = load_matrix_transpose_arb;
458         } else {
459                 load_matrix_gl = load_matrix_transpose_manual;
460         }
461
462         if(sys_caps.vertex_buffers) {
463                 glBindBuffer = (PFNGLBINDBUFFERARBPROC)glGetProcAddress("glBindBufferARB");
464                 glBufferData = (PFNGLBUFFERDATAARBPROC)glGetProcAddress("glBufferDataARB");
465                 glDeleteBuffers = (PFNGLDELETEBUFFERSARBPROC)glGetProcAddress("glDeleteBuffersARB");
466                 glIsBuffer = (PFNGLISBUFFERARBPROC)glGetProcAddress("glIsBufferARB");
467                 glMapBuffer = (PFNGLMAPBUFFERARBPROC)glGetProcAddress("glMapBufferARB");
468                 glUnmapBuffer = (PFNGLUNMAPBUFFERARBPROC)glGetProcAddress("glUnmapBufferARB");
469                 glGenBuffers = (PFNGLGENBUFFERSARBPROC)glGetProcAddress("glGenBuffersARB");
470         }
471
472         if(sys_caps.prog.asm_vertex || sys_caps.prog.asm_pixel) {
473                 glBindProgram = (PFNGLBINDPROGRAMARBPROC)glGetProcAddress("glBindProgramARB");
474                 glGenPrograms = (PFNGLGENPROGRAMSARBPROC)glGetProcAddress("glGenProgramsARB");
475                 glDeletePrograms = (PFNGLDELETEPROGRAMSARBPROC)glGetProcAddress("glDeleteProgramsARB");
476                 glProgramString = (PFNGLPROGRAMSTRINGARBPROC)glGetProcAddress("glProgramStringARB");
477         }
478
479         if(sys_caps.prog.shader_obj) {
480                 glDeleteObject = (PFNGLDELETEOBJECTARBPROC)glGetProcAddress("glDeleteObjectARB");
481                 glAttachObject = (PFNGLATTACHOBJECTARBPROC)glGetProcAddress("glAttachObjectARB");
482                 glDetachObject = (PFNGLDETACHOBJECTARBPROC)glGetProcAddress("glDetachObjectARB");
483                 glGetObjectParameteriv = (PFNGLGETOBJECTPARAMETERIVARBPROC)glGetProcAddress("glGetObjectParameterivARB");
484                 glGetInfoLog = (PFNGLGETINFOLOGARBPROC)glGetProcAddress("glGetInfoLogARB");
485                 
486                 glCreateProgramObject = (PFNGLCREATEPROGRAMOBJECTARBPROC)glGetProcAddress("glCreateProgramObjectARB");
487                 glLinkProgram = (PFNGLLINKPROGRAMARBPROC)glGetProcAddress("glLinkProgramARB");
488                 glUseProgramObject = (PFNGLUSEPROGRAMOBJECTARBPROC)glGetProcAddress("glUseProgramObjectARB");
489
490                 glCreateShaderObject = (PFNGLCREATESHADEROBJECTARBPROC)glGetProcAddress("glCreateShaderObjectARB");
491                 glShaderSource = (PFNGLSHADERSOURCEARBPROC)glGetProcAddress("glShaderSourceARB");
492                 glCompileShader = (PFNGLCOMPILESHADERARBPROC)glGetProcAddress("glCompileShaderARB");
493                 
494                 glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONARBPROC)glGetProcAddress("glGetUniformLocationARB");
495                 glGetActiveUniform = (PFNGLGETACTIVEUNIFORMARBPROC)glGetProcAddress("glGetActiveUniformARB");
496                 glUniform1i = (PFNGLUNIFORM1IARBPROC)glGetProcAddress("glUniform1iARB");
497                 glUniform1f = (PFNGLUNIFORM1FARBPROC)glGetProcAddress("glUniform1fARB");
498                 glUniform2f = (PFNGLUNIFORM2FARBPROC)glGetProcAddress("glUniform2fARB");
499                 glUniform3f = (PFNGLUNIFORM3FARBPROC)glGetProcAddress("glUniform3fARB");
500                 glUniform4f = (PFNGLUNIFORM4FARBPROC)glGetProcAddress("glUniform4fARB");
501                 glUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVARBPROC)glGetProcAddress("glUniformMatrix3fvARB");
502                 glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVARBPROC)glGetProcAddress("glUniformMatrix4fvARB");
503         }
504         
505         if(sys_caps.point_params) {
506                 glext::glPointParameterf = (PFNGLPOINTPARAMETERFARBPROC)glGetProcAddress("glPointParameterfARB");
507                 glext::glPointParameterfv = (PFNGLPOINTPARAMETERFVARBPROC)glGetProcAddress("glPointParameterfvARB");
508
509                 if(!glext::glPointParameterfv) {
510                         error("error loading glPointParameterfv");
511                         return false;
512                 }
513                 if(!glext::glPointParameterf) {
514                         error("error loading glPointParameterf");
515                         return false;
516                 }
517         }
518
519         gc_valid = true;
520         
521         set_default_states();
522         return true;
523 }
524
525 void destroy_graphics_context() {
526         static bool destroy_called_again = false;
527
528         if(destroy_called_again) {
529                 warning("Multiple destroy_graphics_context() calls");
530                 return;
531         } else {
532                 destroy_called_again = true;
533         }
534
535         dsys::clean_up();
536         if(!gc_valid) return;
537         gc_valid = false;
538         info("3d engine shutting down...");
539         destroy_textures();
540         destroy_shaders();
541         fxwt::destroy_graphics();
542 }
543
544 void set_default_states() {
545         set_primitive_type(TRIANGLE_LIST);
546         set_front_face(ORDER_CW);
547         set_backface_culling(true);
548         set_zbuffering(true);
549         set_lighting(true);
550         set_auto_normalize(false);
551         
552         glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
553         glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
554         
555         set_matrix(XFORM_WORLD, Matrix4x4());
556         set_matrix(XFORM_VIEW, Matrix4x4());
557         set_matrix(XFORM_PROJECTION, create_projection_matrix(quarter_pi, 1.333333f, 1.0f, 1000.0f));
558         
559         memset(coord_index, 0, MAX_TEXTURES * sizeof(int));
560
561         for(int i=0; i<8; i++) {
562                 ttype[i] = TEX_2D;
563         }
564
565         if(sys_caps.point_params) {
566                 glext::glPointParameterf(GL_POINT_SIZE_MIN_ARB, 1.0);
567                 glext::glPointParameterf(GL_POINT_SIZE_MAX_ARB, 256.0);
568
569                 float quadratic[] = {0.0f, 0.0f, 0.01f};
570                 glext::glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic);
571         }
572 }
573
574 const GraphicsInitParameters *get_graphics_init_parameters() {
575         return &gparams;
576 }
577
578 void clear(const Color &color) {
579         glClearColor(color.r, color.g, color.b, color.a);
580         glClear(GL_COLOR_BUFFER_BIT);
581 }
582
583 void clear_zbuffer(scalar_t zval) {
584         glClearDepth(zval);
585         glClear(GL_DEPTH_BUFFER_BIT);
586 }
587
588 void clear_stencil(unsigned char sval) {
589         glClearStencil(sval);
590         glClear(GL_STENCIL_BUFFER_BIT);
591 }
592
593 void clear_zbuffer_stencil(scalar_t zval, unsigned char sval) {
594         glClearDepth(zval);
595         glClearStencil(sval);
596         glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
597 }
598
599 void flip() {
600         glFlush();
601         glFinish();
602         fxwt::swap_buffers();
603 }
604
605 void load_xform_matrices() {
606         for(int i=0; i<sys_caps.max_texture_units; i++) {
607                 select_texture_unit(i);
608                 glMatrixMode(GL_TEXTURE);
609                 load_matrix_gl(tex_matrix[i]);
610         }
611         
612         glMatrixMode(GL_PROJECTION);
613         load_matrix_gl(proj_matrix);
614         
615         Matrix4x4 modelview = view_matrix * world_matrix;
616         glMatrixMode(GL_MODELVIEW);
617         load_matrix_gl(modelview);
618 }
619
620 #define BUFFER_OFFSET(i) ((char *)NULL + (i))
621
622 void draw(const VertexArray &varray) {
623         load_xform_matrices();
624
625         bool use_vbo = !varray.get_dynamic() && sys_caps.vertex_buffers;
626         
627         glEnableClientState(GL_VERTEX_ARRAY);
628         glEnableClientState(GL_COLOR_ARRAY);
629         glEnableClientState(GL_NORMAL_ARRAY);
630         
631         if(use_vbo) {
632                 Vertex v;
633                 glBindBuffer(GL_ARRAY_BUFFER_ARB, varray.get_buffer_object());
634                 glVertexPointer(3, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.pos - (char*)&v));
635                 glNormalPointer(GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.normal - (char*)&v));
636                 glColorPointer(4, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.color - (char*)&v));
637
638                 for(int i=0; i<MAX_TEXTURES; i++) {
639                         select_texture_unit(i);
640                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
641
642                         int dim = ttype[i] == TEX_1D ? 1 : (ttype[i] == TEX_3D || ttype[i] == TEX_CUBE ? 3 : 2);
643                         glTexCoordPointer(dim, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.tex[coord_index[i]] - (char*)&v));
644                 }
645
646                 glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
647         } else {
648                 glVertexPointer(3, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->pos);
649                 glNormalPointer(GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->normal);
650                 glColorPointer(4, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->color);
651
652                 for(int i=0; i<MAX_TEXTURES; i++) {
653                         select_texture_unit(i);
654                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
655
656                         int dim = ttype[i] == TEX_1D ? 1 : (ttype[i] == TEX_3D || ttype[i] == TEX_CUBE ? 3 : 2);
657                         glTexCoordPointer(dim, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->tex[coord_index[i]]);
658                 }
659         }
660         
661         glDrawArrays(primitive_type, 0, varray.get_count());
662         
663         glDisableClientState(GL_VERTEX_ARRAY);
664         glDisableClientState(GL_COLOR_ARRAY);
665         glDisableClientState(GL_NORMAL_ARRAY);
666         
667         for(int i=0; i<MAX_TEXTURES; i++) {
668                 select_texture_unit(i);
669                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
670         }
671 }
672
673 void draw(const VertexArray &varray, const IndexArray &iarray) {
674         load_xform_matrices();
675         
676         bool use_vbo = !varray.get_dynamic() && sys_caps.vertex_buffers;
677         bool use_ibo = false;//!iarray.get_dynamic() && sys_caps.vertex_buffers;
678         
679         glEnableClientState(GL_VERTEX_ARRAY);
680         glEnableClientState(GL_COLOR_ARRAY);
681         glEnableClientState(GL_NORMAL_ARRAY);
682         
683         if(use_vbo) {
684                 Vertex v;
685                 glBindBuffer(GL_ARRAY_BUFFER_ARB, varray.get_buffer_object());
686                 glVertexPointer(3, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.pos - (char*)&v));
687                 glNormalPointer(GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.normal - (char*)&v));
688                 glColorPointer(4, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.color - (char*)&v));
689
690                 for(int i=0; i<MAX_TEXTURES; i++) {
691                         select_texture_unit(i);
692                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
693
694                         int dim = ttype[i] == TEX_1D ? 1 : (ttype[i] == TEX_3D || ttype[i] == TEX_CUBE ? 3 : 2);
695                         glTexCoordPointer(dim, GL_SCALAR_TYPE, sizeof(Vertex), (void*)((char*)&v.tex[coord_index[i]] - (char*)&v));
696                 }
697
698                 glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
699         } else {
700                 glVertexPointer(3, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->pos);
701                 glNormalPointer(GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->normal);
702                 glColorPointer(4, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->color);
703
704                 for(int i=0; i<MAX_TEXTURES; i++) {
705                         select_texture_unit(i);
706                         glEnableClientState(GL_TEXTURE_COORD_ARRAY);
707
708                         int dim = ttype[i] == TEX_1D ? 1 : (ttype[i] == TEX_3D || ttype[i] == TEX_CUBE ? 3 : 2);
709                         glTexCoordPointer(dim, GL_SCALAR_TYPE, sizeof(Vertex), &varray.get_data()->tex[coord_index[i]]);
710                 }
711         }
712
713         if(use_ibo) {
714                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, iarray.get_buffer_object());
715                 glDrawElements(primitive_type, iarray.get_count(), GL_UNSIGNED_INT, 0);
716                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
717         } else {
718                 glDrawElements(primitive_type, iarray.get_count(), GL_UNSIGNED_INT, iarray.get_data());
719         }
720         
721         glDisableClientState(GL_VERTEX_ARRAY);
722         glDisableClientState(GL_COLOR_ARRAY);
723         glDisableClientState(GL_NORMAL_ARRAY);
724         
725         for(int i=0; i<MAX_TEXTURES; i++) {
726                 select_texture_unit(i);
727                 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
728         }
729 }
730
731
732 /* draw_line(start_vertex, end_vertex, start_width, end_width)
733  * Draws a line as a cylindrically billboarded elongated quad.
734  */
735 void draw_line(const Vertex &v1, const Vertex &v2, scalar_t w1, scalar_t w2, const Color &col) {
736         if(w2 < 0.0) w2 = w1;
737
738         Vector3 p1 = v1.pos;
739         Vector3 p2 = v2.pos;
740
741         Vector3 cam_pos = Vector3(0,0,0).transformed(inv_view_matrix);
742         
743         Vector3 vec = p2 - p1;
744         scalar_t len = vec.length();
745         
746         Basis basis;
747         basis.k = -(cam_pos - ((p2 + p1) / 2)).normalized();
748         basis.j = vec / len;
749         basis.i = cross_product(basis.j, basis.k).normalized();
750         basis.k = cross_product(basis.i, basis.j).normalized();
751
752         world_matrix.set_translation(p1);
753         world_matrix = world_matrix * Matrix4x4(basis.create_rotation_matrix());
754         load_xform_matrices();
755
756         Vertex quad[] = {
757                 Vertex(Vector3(-w1, 0, 0), v1.tex[0].u, 0.0, col),
758                 Vertex(Vector3(-w2, len, 0), v2.tex[0].u, 0.0, col),
759                 Vertex(Vector3(w2, len, 0), v2.tex[0].u, 1.0, col),
760                 Vertex(Vector3(w1, 0, 0), v1.tex[0].u, 1.0, col)
761         };
762
763         set_lighting(false);
764         set_primitive_type(QUAD_LIST);
765         draw(VertexArray(quad, 4));
766         set_primitive_type(TRIANGLE_LIST);
767         set_lighting(true);
768 }
769
770 void draw_point(const Vertex &pt, scalar_t size) {
771
772         Vector3 p = pt.pos;
773         
774         Vector3 cam_pos = Vector3(0,0,0).transformed(inv_view_matrix);
775
776         Basis basis;
777         basis.k = -(cam_pos - p).normalized();
778         basis.j = Vector3(0, 1, 0);
779         basis.i = cross_product(basis.j, basis.k);
780         basis.j = cross_product(basis.k, basis.i);
781
782         world_matrix.set_translation(p);
783         world_matrix = world_matrix * Matrix4x4(basis.create_rotation_matrix());
784         load_xform_matrices();
785
786         Vertex quad[] = {
787                 Vertex(Vector3(-size, -size, 0), 0.0, 0.0, pt.color),
788                 Vertex(Vector3(-size, size, 0), 0.0, 1.0, pt.color),
789                 Vertex(Vector3(size, size, 0), 1.0, 1.0, pt.color),
790                 Vertex(Vector3(size, -size, 0), 1.0, 0.0, pt.color)
791         };
792
793         set_lighting(false);
794         set_primitive_type(QUAD_LIST);
795         draw(VertexArray(quad, 4));
796         set_primitive_type(TRIANGLE_LIST);
797         set_lighting(true);
798
799 }
800
801
802 void draw_scr_quad(const Vector2 &corner1, const Vector2 &corner2, const Color &color, bool reset_xform) {
803         if(reset_xform) {
804                 glMatrixMode(GL_MODELVIEW);
805                 glPushMatrix();
806                 glLoadIdentity();
807         }
808
809         glMatrixMode(GL_PROJECTION);
810         glPushMatrix();
811         glLoadIdentity();
812         glOrtho(0.0, 1.0, 1.0, 0.0, 0.0, 1.0);
813
814         glDisable(GL_LIGHTING);
815
816         glBegin(GL_QUADS);
817         glColor4f(color.r, color.g, color.b, color.a);
818         glTexCoord2f(0.0f, 1.0f);
819         glVertex3f(corner1.x, corner1.y, -0.5);
820         glTexCoord2f(1.0f, 1.0f);
821         glVertex3f(corner2.x, corner1.y, -0.5);
822         glTexCoord2f(1.0f, 0.0f);
823         glVertex3f(corner2.x, corner2.y, -0.5);
824         glTexCoord2f(0.0f, 0.0f);
825         glVertex3f(corner1.x, corner2.y, -0.5);
826         glEnd();
827
828         glEnable(GL_LIGHTING);
829
830         glPopMatrix();
831
832         if(reset_xform) {
833                 glMatrixMode(GL_MODELVIEW);
834                 glPopMatrix();
835         }
836 }
837
838 int get_texture_unit_count() {
839         return sys_caps.max_texture_units;
840 }
841
842 //////////////////// render states /////////////////////
843
844 void set_primitive_type(PrimitiveType pt) {
845         primitive_type = pt;
846 }
847
848 void set_backface_culling(bool enable) {
849         if(enable) {
850                 glEnable(GL_CULL_FACE);
851         } else {
852                 glDisable(GL_CULL_FACE);
853         }
854 }
855
856 void set_front_face(FaceOrder order) {
857         glFrontFace(order);
858 }
859
860 void set_auto_normalize(bool enable) {
861         if(enable) {
862                 glEnable(GL_NORMALIZE);
863         } else {
864                 glDisable(GL_NORMALIZE);
865         }
866 }
867
868 void set_color_write(bool red, bool green, bool blue, bool alpha) {
869         glColorMask(red, green, blue, alpha);
870 }
871
872 void set_wireframe(bool enable) {
873         //set_primitive_type(enable ? LINE_LIST : TRIANGLE_LIST);
874         glPolygonMode(GL_FRONT_AND_BACK, enable ? GL_LINE : GL_FILL);
875 }
876         
877
878 ///////////////// blending states ///////////////
879
880 void set_alpha_blending(bool enable) {
881         if(enable) {
882                 glEnable(GL_BLEND);
883         } else {
884                 glDisable(GL_BLEND);
885         }
886 }
887
888 void set_blend_func(BlendingFactor src, BlendingFactor dest) {
889         glBlendFunc(src, dest);
890 }
891
892 ///////////////// zbuffer states ////////////////
893
894 void set_zbuffering(bool enable) {
895         if(enable) {
896                 glEnable(GL_DEPTH_TEST);
897         } else {
898                 glDisable(GL_DEPTH_TEST);
899         }
900 }
901
902 void set_zwrite(bool enable) {
903         glDepthMask(enable);
904 }
905
906 void set_zfunc(CmpFunc func) {
907         glDepthFunc(func);
908 }
909
910 /////////////// stencil states //////////////////
911 void set_stencil_buffering(bool enable) {
912         if(enable) {
913                 glEnable(GL_STENCIL_TEST);
914         } else {
915                 glDisable(GL_STENCIL_TEST);
916         }
917 }
918
919 void set_stencil_pass_op(StencilOp sop) {
920         stencil_pass = sop;
921         glStencilOp(stencil_fail, stencil_pzfail, stencil_pass);
922 }
923
924 void set_stencil_fail_op(StencilOp sop) {
925         stencil_fail = sop;
926         glStencilOp(stencil_fail, stencil_pzfail, stencil_pass);
927 }
928
929 void set_stencil_pass_zfail_op(StencilOp sop) {
930         stencil_pzfail = sop;
931         glStencilOp(stencil_fail, stencil_pzfail, stencil_pass);
932 }
933
934 void set_stencil_op(StencilOp fail, StencilOp spass_zfail, StencilOp pass) {
935         stencil_fail = fail;
936         stencil_pzfail = spass_zfail;
937         stencil_pass = pass;
938         glStencilOp(stencil_fail, stencil_pzfail, stencil_pass);
939 }
940
941 void set_stencil_func(CmpFunc func) {
942         glStencilFunc(func, stencil_ref, 0xffffffff);
943 }
944
945 void set_stencil_reference(unsigned int ref) {
946         stencil_ref = ref;
947 }
948
949 ///////////// texture & material states //////////////
950
951 void set_point_sprites(bool enable) {
952         if(sys_caps.point_sprites) {
953                 if(enable) {
954                         glEnable(GL_POINT_SPRITE_ARB);
955                 } else {
956                         glDisable(GL_POINT_SPRITE_ARB);
957                 }
958         }
959 }
960
961 void set_texture_filtering(int tex_unit, TextureFilteringType tex_filter) {
962         
963         int min_filter;
964         
965         switch(tex_filter) {
966         case POINT_SAMPLING:
967                 min_filter = mipmapping ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
968                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MIN_FILTER, min_filter);
969                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
970                 break;
971                 
972         case BILINEAR_FILTERING:
973                 min_filter = mipmapping ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR;
974                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MIN_FILTER, min_filter);
975                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
976                 break;
977                 
978         case TRILINEAR_FILTERING:
979         default:
980                 min_filter = mipmapping ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
981                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MIN_FILTER, min_filter);
982                 glTexParameteri(ttype[tex_unit], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
983                 break;
984         }
985 }
986
987 void set_texture_addressing(int tex_unit, TextureAddressing uaddr, TextureAddressing vaddr) {
988         glTexParameteri(ttype[tex_unit], GL_TEXTURE_WRAP_S, uaddr);
989         glTexParameteri(ttype[tex_unit], GL_TEXTURE_WRAP_T, vaddr);
990 }
991
992 void set_texture_border_color(int tex_unit, const Color &color) {
993         float col[] = {color.r, color.g, color.b, color.a};
994         glTexParameterfv(ttype[tex_unit], GL_TEXTURE_BORDER_COLOR, col);
995 }
996
997 void set_texture(int tex_unit, const Texture *tex) {
998         select_texture_unit(tex_unit);
999         glBindTexture(tex->get_type(), tex->tex_id);
1000         ttype[tex_unit] = tex->get_type();
1001 }
1002
1003 void set_mip_mapping(bool enable) {
1004         mipmapping = enable;
1005 }
1006
1007 void set_material(const Material &mat) {
1008         mat.set_glmaterial();
1009 }
1010
1011 void use_vertex_colors(bool enable) {
1012         if(enable) {
1013                 glEnable(GL_COLOR_MATERIAL);
1014         } else {
1015                 glDisable(GL_COLOR_MATERIAL);
1016         }
1017 }
1018
1019
1020 void set_render_target(Texture *tex, CubeMapFace cube_map_face) {
1021         static std::stack<Texture*> rt_stack;
1022         static std::stack<CubeMapFace> face_stack;
1023
1024         Texture *prev = rt_stack.empty() ? 0 : rt_stack.top();
1025         CubeMapFace prev_face = CUBE_MAP_PX; // just to get rid of the uninitialized var warning
1026         if(!face_stack.empty()) prev_face = face_stack.top();
1027
1028         if(tex == prev) return;
1029
1030         if(prev) {
1031                 set_texture(0, prev);
1032                 glCopyTexSubImage2D(prev->get_type() == TEX_CUBE ? prev_face : GL_TEXTURE_2D, 0, 0, 0, 0, 0, prev->width, prev->height);
1033         }
1034
1035         if(!tex) {
1036                 rt_stack.pop();
1037                 if(prev->get_type() == TEX_CUBE) {
1038                         face_stack.pop();
1039
1040                         if(rt_stack.empty()) {
1041                                 set_viewport(0, 0, gparams.x, gparams.y);
1042                         } else {
1043                                 set_viewport(0, 0, rt_stack.top()->width, rt_stack.top()->height);
1044                         }
1045                 }
1046         } else {
1047                 if(tex->get_type() == TEX_CUBE) {
1048                         set_viewport(0, 0, tex->width, tex->height);
1049                 }
1050
1051                 rt_stack.push(tex);
1052                 if(tex->get_type() == TEX_CUBE) face_stack.push(cube_map_face);
1053         }
1054 }
1055
1056 void copy_texture(Texture *tex, bool full_screen) {
1057         if(!tex) return;
1058
1059         int width = full_screen ? get_graphics_init_parameters()->x : tex->width;
1060         int height = full_screen ? get_graphics_init_parameters()->y : tex->height;
1061
1062         set_texture(0, tex);
1063         glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, width, height);
1064 }
1065
1066 // multitexturing interface
1067
1068 void select_texture_unit(int tex_unit) {
1069         if(sys_caps.multitex) {
1070                 glext::glActiveTexture(GL_TEXTURE0 + tex_unit);
1071                 glext::glClientActiveTexture(GL_TEXTURE0 + tex_unit);
1072         }
1073 }
1074
1075 void enable_texture_unit(int tex_unit) {
1076         if(!tex_unit || (sys_caps.multitex && tex_unit < sys_caps.max_texture_units)) {
1077                 select_texture_unit(tex_unit);
1078                 glEnable(ttype[tex_unit]);
1079         }
1080 }
1081
1082 void disable_texture_unit(int tex_unit) {
1083         if(!tex_unit || (sys_caps.multitex && tex_unit < sys_caps.max_texture_units)) {
1084                 select_texture_unit(tex_unit);
1085                 glDisable(ttype[tex_unit]);
1086         }
1087 }
1088
1089 void set_texture_unit_color(int tex_unit, TextureBlendFunction op, TextureBlendArgument arg1, TextureBlendArgument arg2, TextureBlendArgument arg3) {
1090         
1091         select_texture_unit(tex_unit);
1092         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1093         glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, op);
1094         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, arg1);
1095         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB, arg2);
1096         if(arg3 != TARG_NONE) {
1097                 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB, arg3);
1098         }
1099 }
1100
1101 void set_texture_unit_alpha(int tex_unit, TextureBlendFunction op, TextureBlendArgument arg1, TextureBlendArgument arg2, TextureBlendArgument arg3) {
1102         
1103         select_texture_unit(tex_unit);
1104         glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
1105         glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, op);
1106         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, arg1);
1107         glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, arg2);
1108         if(arg3 != TARG_NONE) {
1109                 glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA, arg3);
1110         }
1111 }
1112
1113 void set_texture_coord_index(int tex_unit, int index) {
1114         coord_index[tex_unit] = index;
1115 }
1116
1117 void set_texture_constant(int tex_unit, const Color &col) {
1118         float color[] = {col.r, col.g, col.b, col.a};
1119         select_texture_unit(tex_unit);
1120         glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color);
1121 }
1122
1123 //void set_texture_transform_state(int sttex_unitage, TexTransformState TexXForm);
1124 //void set_texture_coord_generator(int stage, TexGen tgen);
1125
1126 void set_point_sprite_coords(int tex_unit, bool enable) {
1127         if(sys_caps.point_params) {
1128                 select_texture_unit(tex_unit);
1129                 glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, enable ? GL_TRUE : GL_FALSE);
1130         }
1131 }
1132
1133
1134 // programmable interface
1135 void set_gfx_program(GfxProg *prog) {
1136         if(!sys_caps.prog.glslang) return;
1137         if(prog) {
1138                 if(!prog->linked) {
1139                         prog->link();
1140                         if(!prog->linked) return;
1141                 }
1142                 glUseProgramObject(prog->prog);
1143                 
1144                 // call any registered update handlers
1145                 if(prog->update_handler) {
1146                         prog->update_handler(prog);
1147                 }
1148         } else {
1149                 glUseProgramObject(0);
1150         }
1151 }
1152
1153 // lighting states
1154 void set_lighting(bool enable) {
1155         if(enable) {
1156                 glEnable(GL_LIGHTING);
1157         } else {
1158                 glDisable(GL_LIGHTING);
1159         }
1160 }
1161
1162 void set_ambient_light(const Color &ambient_color) {
1163         float col[] = {ambient_color.r, ambient_color.g, ambient_color.b, ambient_color.a};
1164         glLightModelfv(GL_LIGHT_MODEL_AMBIENT, col);
1165 }
1166
1167 void set_shading_mode(ShadeMode mode) {
1168         glShadeModel(mode);
1169 }
1170
1171 void set_bump_light(const Light *light) {
1172         bump_light = light;
1173 }
1174
1175 // transformation matrices
1176 void set_matrix(TransformType xform_type, const Matrix4x4 &mat, int num) {
1177         switch(xform_type) {
1178         case XFORM_WORLD:
1179                 world_matrix = mat;
1180                 break;
1181                 
1182         case XFORM_VIEW:
1183                 view_matrix = mat;
1184                 inv_view_matrix = view_matrix.inverse();
1185                 view_mat_camera = 0;
1186                 break;
1187                 
1188         case XFORM_PROJECTION:
1189                 proj_matrix = mat;
1190                 break;
1191                 
1192         case XFORM_TEXTURE:
1193                 tex_matrix[num] = mat;
1194                 break;
1195         }
1196 }
1197
1198 Matrix4x4 get_matrix(TransformType xform_type, int num) {
1199         switch(xform_type) {
1200         case XFORM_WORLD:
1201                 return world_matrix;
1202
1203         case XFORM_VIEW:
1204                 return view_matrix;
1205
1206         case XFORM_TEXTURE:
1207                 return tex_matrix[num];
1208
1209         case XFORM_PROJECTION:
1210         default:
1211                 return proj_matrix;
1212         }
1213 }
1214
1215 void set_viewport(unsigned int x, unsigned int y, unsigned int xsize, unsigned int ysize)
1216 {
1217         glViewport(x, y, xsize, ysize);
1218 }
1219
1220 // normalized set_viewport()
1221 void set_viewport_norm(float x, float y, float xsize, float ysize)
1222 {
1223         glViewport((int) (x * gparams.x), (int)(y * gparams.y), 
1224                 (int) (xsize * gparams.x), int (ysize * gparams.y));
1225 }
1226
1227 Matrix4x4 create_projection_matrix(scalar_t vfov, scalar_t aspect, scalar_t near_clip, scalar_t far_clip) {
1228 #ifdef COORD_LHS
1229         scalar_t f = 1.0f / (scalar_t)tan(vfov * 0.5f);
1230         scalar_t q = far_clip / (far_clip - near_clip);
1231
1232         Matrix4x4 mat;
1233         mat[0][0] = f / aspect;
1234         mat[1][1] = f;
1235         mat[2][2] = q;
1236         mat[3][2] = 1.0f;
1237         mat[2][3] = -q * near_clip;
1238 #else
1239         scalar_t f = 1.0f / (scalar_t)tan(vfov * 0.5f);
1240
1241         Matrix4x4 mat;
1242         mat[0][0] = f / aspect;
1243         mat[1][1] = f;
1244         mat[2][2] = (far_clip + near_clip) / (near_clip - far_clip);
1245         mat[3][2] = -1.0f;
1246         mat[2][3] = (2.0f * far_clip * near_clip) / (near_clip - far_clip);
1247         mat[3][3] = 0;
1248 #endif
1249         
1250         return mat;
1251 }
1252
1253
1254 // ---- misc ----
1255
1256 bool screen_capture(char *fname, enum image_file_format fmt) {
1257         static int scr_num;
1258         static const char *suffix[] = {"png", "jpg", "tga", "oug1", "oug2"};
1259         int x = gparams.x;
1260         int y = gparams.y;
1261
1262         uint32_t *pixels = new uint32_t[x * y];
1263         glReadPixels(0, 0, x, y, GL_BGRA, GL_UNSIGNED_BYTE, pixels);
1264         
1265         if(!fname) {
1266                 static char fname_buf[50];
1267                 fname = fname_buf;
1268                 sprintf(fname, "3dengfx_shot%04d.%s", scr_num++, suffix[fmt]);
1269         }
1270
1271         unsigned int flags = get_image_save_flags();
1272         set_image_save_flags(flags | IMG_SAVE_INVERT);
1273         int res = save_image(fname, pixels, x, y, fmt);
1274         set_image_save_flags(flags);
1275         
1276         delete [] pixels;
1277         return res != -1;
1278 }