]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_backend.c
*** empty log message ***
[divverent/darkplaces.git] / gl_backend.c
1
2 #include "quakedef.h"
3 #include "image.h"
4 #include "jpeg.h"
5 #include "cl_collision.h"
6
7 cvar_t gl_mesh_drawrangeelements = {0, "gl_mesh_drawrangeelements", "1"};
8 cvar_t gl_mesh_testarrayelement = {0, "gl_mesh_testarrayelement", "0"};
9 cvar_t gl_mesh_testmanualfeeding = {0, "gl_mesh_testmanualfeeding", "0"};
10 cvar_t gl_delayfinish = {CVAR_SAVE, "gl_delayfinish", "0"};
11 cvar_t gl_paranoid = {0, "gl_paranoid", "0"};
12 cvar_t gl_printcheckerror = {0, "gl_printcheckerror", "0"};
13
14 cvar_t r_render = {0, "r_render", "1"};
15 cvar_t r_waterwarp = {CVAR_SAVE, "r_waterwarp", "1"};
16 cvar_t gl_polyblend = {CVAR_SAVE, "gl_polyblend", "1"};
17 cvar_t gl_dither = {CVAR_SAVE, "gl_dither", "1"}; // whether or not to use dithering
18 cvar_t gl_lockarrays = {0, "gl_lockarrays", "1"};
19
20 int gl_maxdrawrangeelementsvertices;
21 int gl_maxdrawrangeelementsindices;
22
23 #ifdef DEBUGGL
24 int errornumber = 0;
25
26 void GL_PrintError(int errornumber, char *filename, int linenumber)
27 {
28         switch(errornumber)
29         {
30 #ifdef GL_INVALID_ENUM
31         case GL_INVALID_ENUM:
32                 Con_Printf("GL_INVALID_ENUM at %s:%i\n", filename, linenumber);
33                 break;
34 #endif
35 #ifdef GL_INVALID_VALUE
36         case GL_INVALID_VALUE:
37                 Con_Printf("GL_INVALID_VALUE at %s:%i\n", filename, linenumber);
38                 break;
39 #endif
40 #ifdef GL_INVALID_OPERATION
41         case GL_INVALID_OPERATION:
42                 Con_Printf("GL_INVALID_OPERATION at %s:%i\n", filename, linenumber);
43                 break;
44 #endif
45 #ifdef GL_STACK_OVERFLOW
46         case GL_STACK_OVERFLOW:
47                 Con_Printf("GL_STACK_OVERFLOW at %s:%i\n", filename, linenumber);
48                 break;
49 #endif
50 #ifdef GL_STACK_UNDERFLOW
51         case GL_STACK_UNDERFLOW:
52                 Con_Printf("GL_STACK_UNDERFLOW at %s:%i\n", filename, linenumber);
53                 break;
54 #endif
55 #ifdef GL_OUT_OF_MEMORY
56         case GL_OUT_OF_MEMORY:
57                 Con_Printf("GL_OUT_OF_MEMORY at %s:%i\n", filename, linenumber);
58                 break;
59 #endif
60 #ifdef GL_TABLE_TOO_LARGE
61         case GL_TABLE_TOO_LARGE:
62                 Con_Printf("GL_TABLE_TOO_LARGE at %s:%i\n", filename, linenumber);
63                 break;
64 #endif
65         default:
66                 Con_Printf("GL UNKNOWN (%i) at %s:%i\n", errornumber, filename, linenumber);
67                 break;
68         }
69 }
70 #endif
71
72 #define BACKENDACTIVECHECK if (!backendactive) Sys_Error("GL backend function called when backend is not active\n");
73
74 int c_meshs, c_meshelements;
75
76 void SCR_ScreenShot_f (void);
77
78 static matrix4x4_t backend_viewmatrix;
79 static matrix4x4_t backend_modelmatrix;
80 static matrix4x4_t backend_modelviewmatrix;
81 static matrix4x4_t backend_glmodelviewmatrix;
82 static matrix4x4_t backend_projectmatrix;
83
84 static int backendunits, backendactive;
85 static mempool_t *gl_backend_mempool;
86
87 /*
88 note: here's strip order for a terrain row:
89 0--1--2--3--4
90 |\ |\ |\ |\ |
91 | \| \| \| \|
92 A--B--C--D--E
93 clockwise
94
95 A0B, 01B, B1C, 12C, C2D, 23D, D3E, 34E
96
97 *elements++ = i + row;
98 *elements++ = i;
99 *elements++ = i + row + 1;
100 *elements++ = i;
101 *elements++ = i + 1;
102 *elements++ = i + row + 1;
103
104
105 for (y = 0;y < rows - 1;y++)
106 {
107         for (x = 0;x < columns - 1;x++)
108         {
109                 i = y * rows + x;
110                 *elements++ = i + columns;
111                 *elements++ = i;
112                 *elements++ = i + columns + 1;
113                 *elements++ = i;
114                 *elements++ = i + 1;
115                 *elements++ = i + columns + 1;
116         }
117 }
118
119 alternative:
120 0--1--2--3--4
121 | /| /|\ | /|
122 |/ |/ | \|/ |
123 A--B--C--D--E
124 counterclockwise
125
126 for (y = 0;y < rows - 1;y++)
127 {
128         for (x = 0;x < columns - 1;x++)
129         {
130                 i = y * rows + x;
131                 *elements++ = i;
132                 *elements++ = i + columns;
133                 *elements++ = i + columns + 1;
134                 *elements++ = i + columns;
135                 *elements++ = i + columns + 1;
136                 *elements++ = i + 1;
137         }
138 }
139 */
140
141 int polygonelements[768];
142
143 static void R_Mesh_CacheArray_Startup(void);
144 static void R_Mesh_CacheArray_Shutdown(void);
145 void GL_Backend_AllocArrays(void)
146 {
147         if (!gl_backend_mempool)
148                 gl_backend_mempool = Mem_AllocPool("GL_Backend");
149         R_Mesh_CacheArray_Startup();
150 }
151
152 void GL_Backend_FreeArrays(void)
153 {
154         R_Mesh_CacheArray_Shutdown();
155         Mem_FreePool(&gl_backend_mempool);
156 }
157
158 static void gl_backend_start(void)
159 {
160         Con_DPrint("OpenGL Backend started\n");
161         if (qglDrawRangeElements != NULL)
162         {
163                 CHECKGLERROR
164                 qglGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &gl_maxdrawrangeelementsvertices);
165                 CHECKGLERROR
166                 qglGetIntegerv(GL_MAX_ELEMENTS_INDICES, &gl_maxdrawrangeelementsindices);
167                 CHECKGLERROR
168                 Con_DPrintf("glDrawRangeElements detected (max vertices %i, max indices %i)\n", gl_maxdrawrangeelementsvertices, gl_maxdrawrangeelementsindices);
169         }
170
171         backendunits = min(MAX_TEXTUREUNITS, gl_textureunits);
172
173         GL_Backend_AllocArrays();
174
175         backendactive = true;
176 }
177
178 static void gl_backend_shutdown(void)
179 {
180         backendunits = 0;
181         backendactive = false;
182
183         Con_DPrint("OpenGL Backend shutting down\n");
184
185         GL_Backend_FreeArrays();
186 }
187
188 static void gl_backend_newmap(void)
189 {
190 }
191
192 cvar_t scr_zoomwindow = {CVAR_SAVE, "scr_zoomwindow", "0"};
193 cvar_t scr_zoomwindow_viewsizex = {CVAR_SAVE, "scr_zoomwindow_viewsizex", "20"};
194 cvar_t scr_zoomwindow_viewsizey = {CVAR_SAVE, "scr_zoomwindow_viewsizey", "20"};
195 cvar_t scr_zoomwindow_fov = {CVAR_SAVE, "scr_zoomwindow_fov", "20"};
196
197 void gl_backend_init(void)
198 {
199         int i;
200
201         for (i = 0;i < POLYGONELEMENTS_MAXPOINTS - 2;i++)
202         {
203                 polygonelements[i * 3 + 0] = 0;
204                 polygonelements[i * 3 + 1] = i + 1;
205                 polygonelements[i * 3 + 2] = i + 2;
206         }
207
208         Cvar_RegisterVariable(&r_render);
209         Cvar_RegisterVariable(&r_waterwarp);
210         Cvar_RegisterVariable(&gl_polyblend);
211         Cvar_RegisterVariable(&gl_dither);
212         Cvar_RegisterVariable(&gl_lockarrays);
213         Cvar_RegisterVariable(&gl_delayfinish);
214         Cvar_RegisterVariable(&gl_paranoid);
215         Cvar_RegisterVariable(&gl_printcheckerror);
216 #ifdef NORENDER
217         Cvar_SetValue("r_render", 0);
218 #endif
219
220         Cvar_RegisterVariable(&gl_mesh_drawrangeelements);
221         Cvar_RegisterVariable(&gl_mesh_testarrayelement);
222         Cvar_RegisterVariable(&gl_mesh_testmanualfeeding);
223
224         Cvar_RegisterVariable(&scr_zoomwindow);
225         Cvar_RegisterVariable(&scr_zoomwindow_viewsizex);
226         Cvar_RegisterVariable(&scr_zoomwindow_viewsizey);
227         Cvar_RegisterVariable(&scr_zoomwindow_fov);
228
229         R_RegisterModule("GL_Backend", gl_backend_start, gl_backend_shutdown, gl_backend_newmap);
230 }
231
232 void GL_SetupView_Orientation_Identity (void)
233 {
234         Matrix4x4_CreateIdentity(&backend_viewmatrix);
235         memset(&backend_modelmatrix, 0, sizeof(backend_modelmatrix));
236 }
237
238 void GL_SetupView_Orientation_FromEntity(matrix4x4_t *matrix)
239 {
240         matrix4x4_t tempmatrix, basematrix;
241         Matrix4x4_Invert_Simple(&tempmatrix, matrix);
242         Matrix4x4_CreateRotate(&basematrix, -90, 1, 0, 0);
243         Matrix4x4_ConcatRotate(&basematrix, 90, 0, 0, 1);
244         Matrix4x4_Concat(&backend_viewmatrix, &basematrix, &tempmatrix);
245         //Matrix4x4_ConcatRotate(&backend_viewmatrix, -angles[2], 1, 0, 0);
246         //Matrix4x4_ConcatRotate(&backend_viewmatrix, -angles[0], 0, 1, 0);
247         //Matrix4x4_ConcatRotate(&backend_viewmatrix, -angles[1], 0, 0, 1);
248         //Matrix4x4_ConcatTranslate(&backend_viewmatrix, -origin[0], -origin[1], -origin[2]);
249         memset(&backend_modelmatrix, 0, sizeof(backend_modelmatrix));
250 }
251
252 void GL_SetupView_Mode_Perspective (double fovx, double fovy, double zNear, double zFar)
253 {
254         double xmax, ymax;
255         double m[16];
256
257         if (!r_render.integer)
258                 return;
259
260         // set up viewpoint
261         qglMatrixMode(GL_PROJECTION);CHECKGLERROR
262         qglLoadIdentity();CHECKGLERROR
263         // pyramid slopes
264         xmax = zNear * tan(fovx * M_PI / 360.0);
265         ymax = zNear * tan(fovy * M_PI / 360.0);
266         // set view pyramid
267         qglFrustum(-xmax, xmax, -ymax, ymax, zNear, zFar);CHECKGLERROR
268         qglGetDoublev(GL_PROJECTION_MATRIX, m);
269         backend_projectmatrix.m[0][0] = m[0];
270         backend_projectmatrix.m[1][0] = m[1];
271         backend_projectmatrix.m[2][0] = m[2];
272         backend_projectmatrix.m[3][0] = m[3];
273         backend_projectmatrix.m[0][1] = m[4];
274         backend_projectmatrix.m[1][1] = m[5];
275         backend_projectmatrix.m[2][1] = m[6];
276         backend_projectmatrix.m[3][1] = m[7];
277         backend_projectmatrix.m[0][2] = m[8];
278         backend_projectmatrix.m[1][2] = m[9];
279         backend_projectmatrix.m[2][2] = m[10];
280         backend_projectmatrix.m[3][2] = m[11];
281         backend_projectmatrix.m[0][3] = m[12];
282         backend_projectmatrix.m[1][3] = m[13];
283         backend_projectmatrix.m[2][3] = m[14];
284         backend_projectmatrix.m[3][3] = m[15];
285         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
286         GL_SetupView_Orientation_Identity();
287 }
288
289 void GL_SetupView_Mode_PerspectiveInfiniteFarClip (double fovx, double fovy, double zNear)
290 {
291         double nudge, m[16];
292
293         if (!r_render.integer)
294                 return;
295
296         // set up viewpoint
297         qglMatrixMode(GL_PROJECTION);CHECKGLERROR
298         qglLoadIdentity();CHECKGLERROR
299         // set view pyramid
300         nudge = 1.0 - 1.0 / (1<<23);
301         m[ 0] = 1.0 / tan(fovx * M_PI / 360.0);
302         m[ 1] = 0;
303         m[ 2] = 0;
304         m[ 3] = 0;
305         m[ 4] = 0;
306         m[ 5] = 1.0 / tan(fovy * M_PI / 360.0);
307         m[ 6] = 0;
308         m[ 7] = 0;
309         m[ 8] = 0;
310         m[ 9] = 0;
311         m[10] = -nudge;
312         m[11] = -1;
313         m[12] = 0;
314         m[13] = 0;
315         m[14] = -2 * zNear * nudge;
316         m[15] = 0;
317         qglLoadMatrixd(m);
318         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
319         GL_SetupView_Orientation_Identity();
320         backend_projectmatrix.m[0][0] = m[0];
321         backend_projectmatrix.m[1][0] = m[1];
322         backend_projectmatrix.m[2][0] = m[2];
323         backend_projectmatrix.m[3][0] = m[3];
324         backend_projectmatrix.m[0][1] = m[4];
325         backend_projectmatrix.m[1][1] = m[5];
326         backend_projectmatrix.m[2][1] = m[6];
327         backend_projectmatrix.m[3][1] = m[7];
328         backend_projectmatrix.m[0][2] = m[8];
329         backend_projectmatrix.m[1][2] = m[9];
330         backend_projectmatrix.m[2][2] = m[10];
331         backend_projectmatrix.m[3][2] = m[11];
332         backend_projectmatrix.m[0][3] = m[12];
333         backend_projectmatrix.m[1][3] = m[13];
334         backend_projectmatrix.m[2][3] = m[14];
335         backend_projectmatrix.m[3][3] = m[15];
336 }
337
338 void GL_SetupView_Mode_Ortho (double x1, double y1, double x2, double y2, double zNear, double zFar)
339 {
340         double m[16];
341
342         if (!r_render.integer)
343                 return;
344
345         // set up viewpoint
346         qglMatrixMode(GL_PROJECTION);CHECKGLERROR
347         qglLoadIdentity();CHECKGLERROR
348         qglOrtho(x1, x2, y2, y1, zNear, zFar);
349         qglGetDoublev(GL_PROJECTION_MATRIX, m);
350         backend_projectmatrix.m[0][0] = m[0];
351         backend_projectmatrix.m[1][0] = m[1];
352         backend_projectmatrix.m[2][0] = m[2];
353         backend_projectmatrix.m[3][0] = m[3];
354         backend_projectmatrix.m[0][1] = m[4];
355         backend_projectmatrix.m[1][1] = m[5];
356         backend_projectmatrix.m[2][1] = m[6];
357         backend_projectmatrix.m[3][1] = m[7];
358         backend_projectmatrix.m[0][2] = m[8];
359         backend_projectmatrix.m[1][2] = m[9];
360         backend_projectmatrix.m[2][2] = m[10];
361         backend_projectmatrix.m[3][2] = m[11];
362         backend_projectmatrix.m[0][3] = m[12];
363         backend_projectmatrix.m[1][3] = m[13];
364         backend_projectmatrix.m[2][3] = m[14];
365         backend_projectmatrix.m[3][3] = m[15];
366         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
367         GL_SetupView_Orientation_Identity();
368 }
369
370 typedef struct gltextureunit_s
371 {
372         int t1d, t2d, t3d, tcubemap;
373         int arrayenabled;
374         int arrayis3d;
375         const void *pointer_texcoord;
376         float rgbscale, alphascale;
377         int combinergb, combinealpha;
378         // FIXME: add more combine stuff
379         // texmatrixenabled exists only to avoid unnecessary texmatrix compares
380         int texmatrixenabled;
381         matrix4x4_t matrix;
382 }
383 gltextureunit_t;
384
385 static struct
386 {
387         int blendfunc1;
388         int blendfunc2;
389         int blend;
390         GLboolean depthmask;
391         int colormask; // stored as bottom 4 bits: r g b a (3 2 1 0 order)
392         int depthtest;
393         int scissortest;
394         int unit;
395         int clientunit;
396         gltextureunit_t units[MAX_TEXTUREUNITS];
397         float color4f[4];
398         int lockrange_first;
399         int lockrange_count;
400         const void *pointer_vertex;
401         const void *pointer_color;
402 }
403 gl_state;
404
405 void GL_SetupTextureState(void)
406 {
407         int i;
408         gltextureunit_t *unit;
409         CHECKGLERROR
410         gl_state.unit = -1;
411         gl_state.clientunit = -1;
412         for (i = 0;i < backendunits;i++)
413         {
414                 GL_ActiveTexture(i);
415                 GL_ClientActiveTexture(i);
416                 unit = gl_state.units + i;
417                 unit->t1d = 0;
418                 unit->t2d = 0;
419                 unit->t3d = 0;
420                 unit->tcubemap = 0;
421                 unit->arrayenabled = false;
422                 unit->arrayis3d = false;
423                 unit->pointer_texcoord = NULL;
424                 unit->rgbscale = 1;
425                 unit->alphascale = 1;
426                 unit->combinergb = GL_MODULATE;
427                 unit->combinealpha = GL_MODULATE;
428                 unit->texmatrixenabled = false;
429                 unit->matrix = r_identitymatrix;
430                 qglMatrixMode(GL_TEXTURE);
431                 qglLoadIdentity();
432                 qglMatrixMode(GL_MODELVIEW);
433
434                 qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), NULL);CHECKGLERROR
435                 qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
436
437                 qglDisable(GL_TEXTURE_1D);CHECKGLERROR
438                 qglDisable(GL_TEXTURE_2D);CHECKGLERROR
439                 if (gl_texture3d)
440                 {
441                         qglDisable(GL_TEXTURE_3D);CHECKGLERROR
442                 }
443                 if (gl_texturecubemap)
444                 {
445                         qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
446                 }
447                 if (gl_combine.integer)
448                 {
449                         qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);CHECKGLERROR
450                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);CHECKGLERROR
451                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);CHECKGLERROR
452                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);CHECKGLERROR
453                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);CHECKGLERROR // for GL_INTERPOLATE_ARB mode
454                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);CHECKGLERROR
455                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);CHECKGLERROR
456                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);CHECKGLERROR
457                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);CHECKGLERROR
458                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);CHECKGLERROR
459                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PREVIOUS_ARB);CHECKGLERROR
460                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_ALPHA_ARB, GL_CONSTANT_ARB);CHECKGLERROR
461                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);CHECKGLERROR
462                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);CHECKGLERROR
463                         qglTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_ALPHA_ARB, GL_SRC_ALPHA);CHECKGLERROR
464                         qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);CHECKGLERROR
465                         qglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1);CHECKGLERROR
466                 }
467                 else
468                 {
469                         qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);CHECKGLERROR
470                 }
471         }
472         CHECKGLERROR
473 }
474
475 void GL_Backend_ResetState(void)
476 {
477         memset(&gl_state, 0, sizeof(gl_state));
478         gl_state.depthtest = true;
479         gl_state.blendfunc1 = GL_ONE;
480         gl_state.blendfunc2 = GL_ZERO;
481         gl_state.blend = false;
482         gl_state.depthmask = GL_TRUE;
483         gl_state.colormask = 15;
484         gl_state.color4f[0] = gl_state.color4f[1] = gl_state.color4f[2] = gl_state.color4f[3] = 1;
485         gl_state.lockrange_first = 0;
486         gl_state.lockrange_count = 0;
487         gl_state.pointer_vertex = NULL;
488         gl_state.pointer_color = NULL;
489
490         CHECKGLERROR
491
492         qglColorMask(1, 1, 1, 1);
493         qglEnable(GL_CULL_FACE);CHECKGLERROR
494         qglCullFace(GL_FRONT);CHECKGLERROR
495         qglEnable(GL_DEPTH_TEST);CHECKGLERROR
496         qglBlendFunc(gl_state.blendfunc1, gl_state.blendfunc2);CHECKGLERROR
497         qglDisable(GL_BLEND);CHECKGLERROR
498         qglDepthMask(gl_state.depthmask);CHECKGLERROR
499
500         qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), NULL);CHECKGLERROR
501         qglEnableClientState(GL_VERTEX_ARRAY);CHECKGLERROR
502
503         qglColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL);CHECKGLERROR
504         qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR
505
506         GL_Color(0, 0, 0, 0);
507         GL_Color(1, 1, 1, 1);
508
509         GL_SetupTextureState();
510 }
511
512 void GL_ActiveTexture(int num)
513 {
514         if (gl_state.unit != num)
515         {
516                 gl_state.unit = num;
517                 if (qglActiveTexture)
518                 {
519                         qglActiveTexture(GL_TEXTURE0_ARB + gl_state.unit);
520                         CHECKGLERROR
521                 }
522         }
523 }
524
525 void GL_ClientActiveTexture(int num)
526 {
527         if (gl_state.clientunit != num)
528         {
529                 gl_state.clientunit = num;
530                 if (qglActiveTexture)
531                 {
532                         qglClientActiveTexture(GL_TEXTURE0_ARB + gl_state.clientunit);
533                         CHECKGLERROR
534                 }
535         }
536 }
537
538 void GL_BlendFunc(int blendfunc1, int blendfunc2)
539 {
540         if (gl_state.blendfunc1 != blendfunc1 || gl_state.blendfunc2 != blendfunc2)
541         {
542                 if (r_showtrispass)
543                         return;
544                 qglBlendFunc(gl_state.blendfunc1 = blendfunc1, gl_state.blendfunc2 = blendfunc2);CHECKGLERROR
545                 if (gl_state.blendfunc2 == GL_ZERO)
546                 {
547                         if (gl_state.blendfunc1 == GL_ONE)
548                         {
549                                 if (gl_state.blend)
550                                 {
551                                         gl_state.blend = 0;
552                                         qglDisable(GL_BLEND);CHECKGLERROR
553                                 }
554                         }
555                         else
556                         {
557                                 if (!gl_state.blend)
558                                 {
559                                         gl_state.blend = 1;
560                                         qglEnable(GL_BLEND);CHECKGLERROR
561                                 }
562                         }
563                 }
564                 else
565                 {
566                         if (!gl_state.blend)
567                         {
568                                 gl_state.blend = 1;
569                                 qglEnable(GL_BLEND);CHECKGLERROR
570                         }
571                 }
572         }
573 }
574
575 void GL_DepthMask(int state)
576 {
577         if (gl_state.depthmask != state)
578         {
579                 if (r_showtrispass)
580                         return;
581                 qglDepthMask(gl_state.depthmask = state);CHECKGLERROR
582         }
583 }
584
585 void GL_DepthTest(int state)
586 {
587         if (gl_state.depthtest != state)
588         {
589                 if (r_showtrispass)
590                         return;
591                 gl_state.depthtest = state;
592                 if (gl_state.depthtest)
593                 {
594                         qglEnable(GL_DEPTH_TEST);CHECKGLERROR
595                 }
596                 else
597                 {
598                         qglDisable(GL_DEPTH_TEST);CHECKGLERROR
599                 }
600         }
601 }
602
603 void GL_ColorMask(int r, int g, int b, int a)
604 {
605         int state = r*8 + g*4 + b*2 + a*1;
606         if (gl_state.colormask != state)
607         {
608                 if (r_showtrispass)
609                         return;
610                 gl_state.colormask = state;
611                 qglColorMask((GLboolean)r, (GLboolean)g, (GLboolean)b, (GLboolean)a);CHECKGLERROR
612         }
613 }
614
615 void GL_Color(float cr, float cg, float cb, float ca)
616 {
617         if (gl_state.pointer_color || gl_state.color4f[0] != cr || gl_state.color4f[1] != cg || gl_state.color4f[2] != cb || gl_state.color4f[3] != ca)
618         {
619                 if (r_showtrispass)
620                         return;
621                 gl_state.color4f[0] = cr;
622                 gl_state.color4f[1] = cg;
623                 gl_state.color4f[2] = cb;
624                 gl_state.color4f[3] = ca;
625                 CHECKGLERROR
626                 qglColor4f(gl_state.color4f[0], gl_state.color4f[1], gl_state.color4f[2], gl_state.color4f[3]);
627                 CHECKGLERROR
628         }
629 }
630
631 void GL_ShowTrisColor(float cr, float cg, float cb, float ca)
632 {
633         if (!r_showtrispass)
634                 return;
635         r_showtrispass = false;
636         GL_Color(cr * r_showtris.value, cg * r_showtris.value, cb * r_showtris.value, ca);
637         r_showtrispass = true;
638 }
639
640
641 void GL_LockArrays(int first, int count)
642 {
643         if (gl_state.lockrange_count != count || gl_state.lockrange_first != first)
644         {
645                 if (gl_state.lockrange_count)
646                 {
647                         gl_state.lockrange_count = 0;
648                         CHECKGLERROR
649                         qglUnlockArraysEXT();
650                         CHECKGLERROR
651                 }
652                 if (count && gl_supportslockarrays && gl_lockarrays.integer && r_render.integer)
653                 {
654                         gl_state.lockrange_first = first;
655                         gl_state.lockrange_count = count;
656                         CHECKGLERROR
657                         qglLockArraysEXT(first, count);
658                         CHECKGLERROR
659                 }
660         }
661 }
662
663 void GL_Scissor (int x, int y, int width, int height)
664 {
665         CHECKGLERROR
666         qglScissor(x, vid.realheight - (y + height),width,height);
667         CHECKGLERROR
668 }
669
670 void GL_ScissorTest(int state)
671 {
672         if(gl_state.scissortest == state)
673                 return;
674
675         CHECKGLERROR
676         if((gl_state.scissortest = state))
677                 qglEnable(GL_SCISSOR_TEST);
678         else
679                 qglDisable(GL_SCISSOR_TEST);
680         CHECKGLERROR
681 }
682
683 void GL_Clear(int mask)
684 {
685         if (r_showtrispass)
686                 return;
687         qglClear(mask);CHECKGLERROR
688 }
689
690 void GL_TransformToScreen(const vec4_t in, vec4_t out)
691 {
692         vec4_t temp;
693         float iw;
694         Matrix4x4_Transform4 (&backend_viewmatrix, in, temp);
695         Matrix4x4_Transform4 (&backend_projectmatrix, temp, out);
696         iw = 1.0f / out[3];
697         out[0] = r_view_x + (out[0] * iw + 1.0f) * r_view_width * 0.5f;
698         out[1] = r_view_y + (out[1] * iw + 1.0f) * r_view_height * 0.5f;
699         out[2] = r_view_z + (out[2] * iw + 1.0f) * r_view_depth * 0.5f;
700 }
701
702 // called at beginning of frame
703 void R_Mesh_Start(void)
704 {
705         BACKENDACTIVECHECK
706         CHECKGLERROR
707         GL_Backend_ResetState();
708 }
709
710 int gl_backend_rebindtextures;
711
712 void GL_Backend_RenumberElements(int *out, int count, const int *in, int offset)
713 {
714         int i;
715         if (offset)
716         {
717                 for (i = 0;i < count;i++)
718                         *out++ = *in++ + offset;
719         }
720         else
721                 memcpy(out, in, sizeof(*out) * count);
722 }
723
724 // renders triangles using vertices from the active arrays
725 int paranoidblah = 0;
726 void R_Mesh_Draw(int numverts, int numtriangles, const int *elements)
727 {
728         int numelements = numtriangles * 3;
729         if (numverts == 0 || numtriangles == 0)
730         {
731                 Con_Printf("R_Mesh_Draw(%d, %d, %08p);\n", numverts, numtriangles, elements);
732                 return;
733         }
734         CHECKGLERROR
735         if (r_showtrispass)
736         {
737                 R_Mesh_Draw_ShowTris(numverts, numtriangles, elements);
738                 return;
739         }
740         c_meshs++;
741         c_meshelements += numelements;
742         if (gl_paranoid.integer)
743         {
744                 int i, j, size;
745                 const int *p;
746                 if (!qglIsEnabled(GL_VERTEX_ARRAY))
747                         Con_Print("R_Mesh_Draw: vertex array not enabled\n");
748                 for (j = 0, size = numverts * (int)sizeof(float[3]), p = gl_state.pointer_vertex;j < size;j += sizeof(int), p++)
749                         paranoidblah += *p;
750                 if (gl_state.pointer_color)
751                 {
752                         if (!qglIsEnabled(GL_COLOR_ARRAY))
753                                 Con_Print("R_Mesh_Draw: color array set but not enabled\n");
754                         for (j = 0, size = numverts * (int)sizeof(float[4]), p = gl_state.pointer_color;j < size;j += sizeof(int), p++)
755                                 paranoidblah += *p;
756                 }
757                 for (i = 0;i < backendunits;i++)
758                 {
759                         if (gl_state.units[i].t1d || gl_state.units[i].t2d || gl_state.units[i].t3d || gl_state.units[i].tcubemap || gl_state.units[i].arrayenabled)
760                         {
761                                 if (gl_state.units[i].arrayenabled && !(gl_state.units[i].t1d || gl_state.units[i].t2d || gl_state.units[i].t3d || gl_state.units[i].tcubemap))
762                                         Con_Print("R_Mesh_Draw: array enabled but no texture bound\n");
763                                 GL_ClientActiveTexture(i);
764                                 if (!qglIsEnabled(GL_TEXTURE_COORD_ARRAY))
765                                         Con_Print("R_Mesh_Draw: texcoord array set but not enabled\n");
766                                 for (j = 0, size = numverts * ((gl_state.units[i].t3d || gl_state.units[i].tcubemap) ? (int)sizeof(float[3]) : (int)sizeof(float[2])), p = gl_state.units[i].pointer_texcoord;j < size;j += sizeof(int), p++)
767                                         paranoidblah += *p;
768                         }
769                 }
770                 for (i = 0;i < numtriangles * 3;i++)
771                 {
772                         if (elements[i] < 0 || elements[i] >= numverts)
773                         {
774                                 Con_Printf("R_Mesh_Draw: invalid vertex index %i (outside range 0 - %i) in elements list\n", elements[i], numverts);
775                                 return;
776                         }
777                 }
778                 CHECKGLERROR
779         }
780         if (r_render.integer)
781         {
782                 CHECKGLERROR
783                 if (gl_mesh_testmanualfeeding.integer)
784                 {
785                         int i, j;
786                         const GLfloat *p;
787                         qglBegin(GL_TRIANGLES);
788                         for (i = 0;i < numtriangles * 3;i++)
789                         {
790                                 for (j = 0;j < backendunits;j++)
791                                 {
792                                         if (gl_state.units[j].pointer_texcoord)
793                                         {
794                                                 if (backendunits > 1)
795                                                 {
796                                                         if (gl_state.units[j].t3d || gl_state.units[j].tcubemap)
797                                                         {
798                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + elements[i] * 3;
799                                                                 qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2]);
800                                                         }
801                                                         else
802                                                         {
803                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + elements[i] * 2;
804                                                                 qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, p[0], p[1]);
805                                                         }
806                                                 }
807                                                 else
808                                                 {
809                                                         if (gl_state.units[j].t3d || gl_state.units[j].tcubemap)
810                                                         {
811                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + elements[i] * 3;
812                                                                 qglTexCoord3f(p[0], p[1], p[2]);
813                                                         }
814                                                         else
815                                                         {
816                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + elements[i] * 2;
817                                                                 qglTexCoord2f(p[0], p[1]);
818                                                         }
819                                                 }
820                                         }
821                                 }
822                                 if (gl_state.pointer_color)
823                                 {
824                                         p = ((const GLfloat *)(gl_state.pointer_color)) + elements[i] * 4;
825                                         qglColor4f(p[0], p[1], p[2], p[3]);
826                                 }
827                                 p = ((const GLfloat *)(gl_state.pointer_vertex)) + elements[i] * 3;
828                                 qglVertex3f(p[0], p[1], p[2]);
829                         }
830                         qglEnd();
831                         CHECKGLERROR
832                 }
833                 else if (gl_mesh_testarrayelement.integer)
834                 {
835                         int i;
836                         qglBegin(GL_TRIANGLES);
837                         for (i = 0;i < numtriangles * 3;i++)
838                         {
839                                 qglArrayElement(elements[i]);
840                         }
841                         qglEnd();
842                         CHECKGLERROR
843                 }
844                 else if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
845                 {
846                         qglDrawRangeElements(GL_TRIANGLES, 0, numverts, numelements, GL_UNSIGNED_INT, elements);CHECKGLERROR
847                 }
848                 else
849                 {
850                         qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_INT, elements);CHECKGLERROR
851                 }
852                 CHECKGLERROR
853         }
854 }
855
856 // restores backend state, used when done with 3D rendering
857 void R_Mesh_Finish(void)
858 {
859         int i;
860         BACKENDACTIVECHECK
861         CHECKGLERROR
862         GL_LockArrays(0, 0);
863         CHECKGLERROR
864
865         for (i = backendunits - 1;i >= 0;i--)
866         {
867                 if (qglActiveTexture)
868                         qglActiveTexture(GL_TEXTURE0_ARB + i);CHECKGLERROR
869                 if (qglClientActiveTexture)
870                         qglClientActiveTexture(GL_TEXTURE0_ARB + i);CHECKGLERROR
871                 qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
872                 qglDisable(GL_TEXTURE_1D);CHECKGLERROR
873                 qglDisable(GL_TEXTURE_2D);CHECKGLERROR
874                 if (gl_texture3d)
875                 {
876                         qglDisable(GL_TEXTURE_3D);CHECKGLERROR
877                 }
878                 if (gl_texturecubemap)
879                 {
880                         qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
881                 }
882                 qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);CHECKGLERROR
883                 if (gl_combine.integer)
884                 {
885                         qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 1);CHECKGLERROR
886                         qglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, 1);CHECKGLERROR
887                 }
888         }
889         qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR
890         qglDisableClientState(GL_VERTEX_ARRAY);CHECKGLERROR
891
892         qglDisable(GL_BLEND);CHECKGLERROR
893         qglEnable(GL_DEPTH_TEST);CHECKGLERROR
894         qglDepthMask(GL_TRUE);CHECKGLERROR
895         qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);CHECKGLERROR
896 }
897
898 void R_Mesh_Matrix(const matrix4x4_t *matrix)
899 {
900         if (memcmp(matrix, &backend_modelmatrix, sizeof(matrix4x4_t)))
901         {
902                 backend_modelmatrix = *matrix;
903                 Matrix4x4_Concat(&backend_modelviewmatrix, &backend_viewmatrix, matrix);
904                 Matrix4x4_Transpose(&backend_glmodelviewmatrix, &backend_modelviewmatrix);
905                 qglLoadMatrixf(&backend_glmodelviewmatrix.m[0][0]);
906         }
907 }
908
909 void R_Mesh_State(const rmeshstate_t *m)
910 {
911         int i, combinergb, combinealpha, scale;
912         gltextureunit_t *unit;
913         matrix4x4_t tempmatrix;
914
915         BACKENDACTIVECHECK
916
917         if (gl_state.pointer_vertex != m->pointer_vertex)
918         {
919                 gl_state.pointer_vertex = m->pointer_vertex;
920                 CHECKGLERROR
921                 qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), gl_state.pointer_vertex);
922                 CHECKGLERROR
923         }
924
925         if (r_showtrispass)
926                 return;
927
928         if (gl_state.pointer_color != m->pointer_color)
929         {
930                 CHECKGLERROR
931                 if (!gl_state.pointer_color)
932                 {
933                         qglEnableClientState(GL_COLOR_ARRAY);
934                         CHECKGLERROR
935                 }
936                 else if (!m->pointer_color)
937                 {
938                         qglDisableClientState(GL_COLOR_ARRAY);
939                         CHECKGLERROR
940                         // when color array is on the glColor gets trashed, set it again
941                         qglColor4f(gl_state.color4f[0], gl_state.color4f[1], gl_state.color4f[2], gl_state.color4f[3]);
942                         CHECKGLERROR
943                 }
944                 gl_state.pointer_color = m->pointer_color;
945                 qglColorPointer(4, GL_FLOAT, sizeof(float[4]), gl_state.pointer_color);
946                 CHECKGLERROR
947         }
948
949         if (gl_backend_rebindtextures)
950         {
951                 gl_backend_rebindtextures = false;
952                 GL_SetupTextureState();
953         }
954
955         for (i = 0, unit = gl_state.units;i < backendunits;i++, unit++)
956         {
957                 // update 1d texture binding
958                 if (unit->t1d != m->tex1d[i])
959                 {
960                         if (m->tex1d[i])
961                         {
962                                 if (unit->t1d == 0)
963                                 {
964                                         GL_ActiveTexture(i);
965                                         qglEnable(GL_TEXTURE_1D);CHECKGLERROR
966                                 }
967                                 unit->t1d = m->tex1d[i];
968                                 GL_ActiveTexture(i);
969                                 qglBindTexture(GL_TEXTURE_1D, unit->t1d);CHECKGLERROR
970                         }
971                         else
972                         {
973                                 if (unit->t1d)
974                                 {
975                                         unit->t1d = 0;
976                                         GL_ActiveTexture(i);
977                                         qglDisable(GL_TEXTURE_1D);CHECKGLERROR
978                                 }
979                         }
980                 }
981                 // update 2d texture binding
982                 if (unit->t2d != m->tex[i])
983                 {
984                         if (m->tex[i])
985                         {
986                                 if (unit->t2d == 0)
987                                 {
988                                         GL_ActiveTexture(i);
989                                         qglEnable(GL_TEXTURE_2D);CHECKGLERROR
990                                 }
991                                 unit->t2d = m->tex[i];
992                                 GL_ActiveTexture(i);
993                                 qglBindTexture(GL_TEXTURE_2D, unit->t2d);CHECKGLERROR
994                         }
995                         else
996                         {
997                                 if (unit->t2d)
998                                 {
999                                         unit->t2d = 0;
1000                                         GL_ActiveTexture(i);
1001                                         qglDisable(GL_TEXTURE_2D);CHECKGLERROR
1002                                 }
1003                         }
1004                 }
1005                 // update 3d texture binding
1006                 if (unit->t3d != m->tex3d[i])
1007                 {
1008                         if (m->tex3d[i])
1009                         {
1010                                 if (unit->t3d == 0)
1011                                 {
1012                                         GL_ActiveTexture(i);
1013                                         qglEnable(GL_TEXTURE_3D);CHECKGLERROR
1014                                 }
1015                                 unit->t3d = m->tex3d[i];
1016                                 GL_ActiveTexture(i);
1017                                 qglBindTexture(GL_TEXTURE_3D, unit->t3d);CHECKGLERROR
1018                         }
1019                         else
1020                         {
1021                                 if (unit->t3d)
1022                                 {
1023                                         unit->t3d = 0;
1024                                         GL_ActiveTexture(i);
1025                                         qglDisable(GL_TEXTURE_3D);CHECKGLERROR
1026                                 }
1027                         }
1028                 }
1029                 // update cubemap texture binding
1030                 if (unit->tcubemap != m->texcubemap[i])
1031                 {
1032                         if (m->texcubemap[i])
1033                         {
1034                                 if (unit->tcubemap == 0)
1035                                 {
1036                                         GL_ActiveTexture(i);
1037                                         qglEnable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1038                                 }
1039                                 unit->tcubemap = m->texcubemap[i];
1040                                 GL_ActiveTexture(i);
1041                                 qglBindTexture(GL_TEXTURE_CUBE_MAP_ARB, unit->tcubemap);CHECKGLERROR
1042                         }
1043                         else
1044                         {
1045                                 if (unit->tcubemap)
1046                                 {
1047                                         unit->tcubemap = 0;
1048                                         GL_ActiveTexture(i);
1049                                         qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1050                                 }
1051                         }
1052                 }
1053                 // update texture unit settings if the unit is enabled
1054                 if (unit->t1d || unit->t2d || unit->t3d || unit->tcubemap)
1055                 {
1056                         // texture unit is enabled, enable the array
1057                         if (!unit->arrayenabled)
1058                         {
1059                                 unit->arrayenabled = true;
1060                                 GL_ClientActiveTexture(i);
1061                                 qglEnableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
1062                         }
1063                         // update combine settings
1064                         if (gl_combine.integer)
1065                         {
1066                                 // GL_ARB_texture_env_combine
1067                                 combinergb = m->texcombinergb[i] ? m->texcombinergb[i] : GL_MODULATE;
1068                                 if (unit->combinergb != combinergb)
1069                                 {
1070                                         unit->combinergb = combinergb;
1071                                         GL_ActiveTexture(i);
1072                                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, unit->combinergb);CHECKGLERROR
1073                                 }
1074                                 combinealpha = m->texcombinealpha[i] ? m->texcombinealpha[i] : GL_MODULATE;
1075                                 if (unit->combinealpha != combinealpha)
1076                                 {
1077                                         unit->combinealpha = combinealpha;
1078                                         GL_ActiveTexture(i);
1079                                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, unit->combinealpha);CHECKGLERROR
1080                                 }
1081                                 scale = max(m->texrgbscale[i], 1);
1082                                 if (unit->rgbscale != scale)
1083                                 {
1084                                         GL_ActiveTexture(i);
1085                                         qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, (unit->rgbscale = scale));CHECKGLERROR
1086                                 }
1087                                 scale = max(m->texalphascale[i], 1);
1088                                 if (unit->alphascale != scale)
1089                                 {
1090                                         GL_ActiveTexture(i);
1091                                         qglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, (unit->alphascale = scale));CHECKGLERROR
1092                                 }
1093                         }
1094                         else
1095                         {
1096                                 // normal GL texenv
1097                                 combinergb = m->texcombinergb[i] ? m->texcombinergb[i] : GL_MODULATE;
1098                                 if (unit->combinergb != combinergb)
1099                                 {
1100                                         unit->combinergb = combinergb;
1101                                         GL_ActiveTexture(i);
1102                                         qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, unit->combinergb);CHECKGLERROR
1103                                 }
1104                         }
1105                         // update array settings
1106                         if (m->pointer_texcoord3f[i])
1107                         {
1108                                 // 3d texcoord array
1109                                 if (unit->pointer_texcoord != m->pointer_texcoord3f[i] || !unit->arrayis3d)
1110                                 {
1111                                         unit->pointer_texcoord = m->pointer_texcoord3f[i];
1112                                         unit->arrayis3d = true;
1113                                         GL_ClientActiveTexture(i);
1114                                         qglTexCoordPointer(3, GL_FLOAT, sizeof(float[3]), unit->pointer_texcoord);
1115                                         CHECKGLERROR
1116                                 }
1117                         }
1118                         else
1119                         {
1120                                 // 2d texcoord array
1121                                 if (unit->pointer_texcoord != m->pointer_texcoord[i] || unit->arrayis3d)
1122                                 {
1123                                         unit->pointer_texcoord = m->pointer_texcoord[i];
1124                                         unit->arrayis3d = false;
1125                                         GL_ClientActiveTexture(i);
1126                                         qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), unit->pointer_texcoord);
1127                                         CHECKGLERROR
1128                                 }
1129                         }
1130                         // update texmatrix
1131                         if (m->texmatrix[i].m[3][3])
1132                         {
1133                                 // texmatrix specified, check if it is different
1134                                 if (!unit->texmatrixenabled || memcmp(&unit->matrix, &m->texmatrix[i], sizeof(matrix4x4_t)))
1135                                 {
1136                                         unit->texmatrixenabled = true;
1137                                         unit->matrix = m->texmatrix[i];
1138                                         Matrix4x4_Transpose(&tempmatrix, &unit->matrix);
1139                                         qglMatrixMode(GL_TEXTURE);
1140                                         GL_ActiveTexture(i);
1141                                         qglLoadMatrixf(&tempmatrix.m[0][0]);
1142                                         qglMatrixMode(GL_MODELVIEW);
1143                                 }
1144                         }
1145                         else
1146                         {
1147                                 // no texmatrix specified, revert to identity
1148                                 if (unit->texmatrixenabled)
1149                                 {
1150                                         unit->texmatrixenabled = false;
1151                                         qglMatrixMode(GL_TEXTURE);
1152                                         GL_ActiveTexture(i);
1153                                         qglLoadIdentity();
1154                                         qglMatrixMode(GL_MODELVIEW);
1155                                 }
1156                         }
1157                 }
1158                 else
1159                 {
1160                         // texture unit is disabled, disable the array
1161                         if (unit->arrayenabled)
1162                         {
1163                                 unit->arrayenabled = false;
1164                                 GL_ClientActiveTexture(i);
1165                                 qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
1166                         }
1167                         // no need to update most settings on a disabled texture unit
1168                 }
1169         }
1170 }
1171
1172 void R_Mesh_Draw_ShowTris(int numverts, int numtriangles, const int *elements)
1173 {
1174         qglBegin(GL_LINES);
1175         for (;numtriangles;numtriangles--, elements += 3)
1176         {
1177                 qglArrayElement(elements[0]);qglArrayElement(elements[1]);
1178                 qglArrayElement(elements[1]);qglArrayElement(elements[2]);
1179                 qglArrayElement(elements[2]);qglArrayElement(elements[0]);
1180         }
1181         qglEnd();
1182         CHECKGLERROR
1183 }
1184
1185 /*
1186 ==============================================================================
1187
1188                                                 SCREEN SHOTS
1189
1190 ==============================================================================
1191 */
1192
1193 qboolean SCR_ScreenShot(char *filename, qbyte *buffer1, qbyte *buffer2, qbyte *buffer3, int x, int y, int width, int height, qboolean flipx, qboolean flipy, qboolean flipdiagonal, qboolean jpeg)
1194 {
1195         int     indices[3] = {0,1,2};
1196         qboolean ret;
1197
1198         if (!r_render.integer)
1199                 return false;
1200
1201         qglReadPixels (x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer1);
1202         CHECKGLERROR
1203
1204         Image_CopyMux (buffer2, buffer1, width, height, flipx, flipy, flipdiagonal, 3, 3, indices);
1205
1206         if (jpeg)
1207                 ret = JPEG_SaveImage_preflipped (filename, width, height, buffer2);
1208         else
1209                 ret = Image_WriteTGARGB_preflipped (filename, width, height, buffer2, buffer3);
1210
1211         return ret;
1212 }
1213
1214 //=============================================================================
1215
1216 void R_ClearScreen(void)
1217 {
1218         if (r_render.integer)
1219         {
1220                 // clear to black
1221                 qglClearColor(0,0,0,0);CHECKGLERROR
1222                 qglClearDepth(1);CHECKGLERROR
1223                 if (gl_stencil)
1224                 {
1225                         // LordHavoc: we use a stencil centered around 128 instead of 0,
1226                         // to avoid clamping interfering with strange shadow volume
1227                         // drawing orders
1228                         qglClearStencil(128);CHECKGLERROR
1229                 }
1230                 // clear the screen
1231                 GL_Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | (gl_stencil ? GL_STENCIL_BUFFER_BIT : 0));
1232                 // set dithering mode
1233                 if (gl_dither.integer)
1234                 {
1235                         qglEnable(GL_DITHER);CHECKGLERROR
1236                 }
1237                 else
1238                 {
1239                         qglDisable(GL_DITHER);CHECKGLERROR
1240                 }
1241         }
1242 }
1243
1244 /*
1245 ====================
1246 CalcFov
1247 ====================
1248 */
1249 float CalcFov (float fov_x, float width, float height)
1250 {
1251         // calculate vision size and alter by aspect, then convert back to angle
1252         return atan (((height/width)/vid_pixelaspect.value)*tan(fov_x/360.*M_PI))*360./M_PI; 
1253 }
1254
1255 /*
1256 ==================
1257 SCR_UpdateScreen
1258
1259 This is called every frame, and can also be called explicitly to flush
1260 text to the screen.
1261 ==================
1262 */
1263 void SCR_UpdateScreen (void)
1264 {
1265         if (gl_delayfinish.integer)
1266         {
1267                 R_Mesh_Finish();
1268                 R_TimeReport("meshfinish");
1269                 VID_Finish();
1270                 R_TimeReport("finish");
1271         }
1272
1273         R_Mesh_Start();
1274
1275         if (r_textureunits.integer > gl_textureunits)
1276                 Cvar_SetValueQuick(&r_textureunits, gl_textureunits);
1277         if (r_textureunits.integer < 1)
1278                 Cvar_SetValueQuick(&r_textureunits, 1);
1279
1280         if (gl_combine.integer && (!gl_combine_extension || r_textureunits.integer < 2))
1281                 Cvar_SetValueQuick(&gl_combine, 0);
1282
1283 showtris:
1284         R_TimeReport("setup");
1285
1286         R_ClearScreen();
1287
1288         R_TimeReport("clear");
1289
1290         if (scr_conlines < vid.conheight && cls.signon == SIGNONS)
1291         {
1292                 float size;
1293                 int contents;
1294
1295                 // bound viewsize
1296                 if (scr_viewsize.value < 30)
1297                         Cvar_Set ("viewsize","30");
1298                 if (scr_viewsize.value > 120)
1299                         Cvar_Set ("viewsize","120");
1300                 
1301                 // bound field of view
1302                 if (scr_fov.value < 1)
1303                         Cvar_Set ("fov","1");
1304                 if (scr_fov.value > 170)
1305                         Cvar_Set ("fov","170");
1306         
1307                 // intermission is always full screen
1308                 if (cl.intermission)
1309                 {
1310                         size = 1;
1311                         sb_lines = 0;
1312                 }
1313                 else
1314                 {
1315                         if (scr_viewsize.value >= 120)
1316                                 sb_lines = 0;           // no status bar at all
1317                         else if (scr_viewsize.value >= 110)
1318                                 sb_lines = 24;          // no inventory
1319                         else
1320                                 sb_lines = 24+16+8;
1321                         size = scr_viewsize.value * (1.0 / 100.0);
1322                         size = min(size, 1);
1323                 }
1324         
1325                 r_refdef.width = vid.realwidth * size;
1326                 r_refdef.height = vid.realheight * size;
1327                 r_refdef.x = (vid.realwidth - r_refdef.width)/2;
1328                 r_refdef.y = (vid.realheight - r_refdef.height)/2;
1329         
1330                 // LordHavoc: viewzoom (zoom in for sniper rifles, etc)
1331                 r_refdef.fov_x = scr_fov.value * cl.viewzoom;
1332                 r_refdef.fov_y = CalcFov (r_refdef.fov_x, r_refdef.width, r_refdef.height);
1333
1334                 if (r_waterwarp.value > 0)
1335                 {
1336                         if (cl.worldmodel)
1337                         {
1338                                 Mod_CheckLoaded(cl.worldmodel);
1339                                 contents = CL_PointSuperContents(r_vieworigin);
1340                                 if (contents & SUPERCONTENTS_LIQUIDSMASK)
1341                                 {
1342                                         r_refdef.fov_x *= 1 - (((sin(cl.time * 4.7) + 1) * 0.015) * r_waterwarp.value);
1343                                         r_refdef.fov_y *= 1 - (((sin(cl.time * 3.0) + 1) * 0.015) * r_waterwarp.value);
1344                                 }
1345                         }
1346                 }
1347
1348                 R_RenderView();
1349
1350                 if (scr_zoomwindow.integer)
1351                 {
1352                         float sizex = bound(10, scr_zoomwindow_viewsizex.value, 100) / 100.0;
1353                         float sizey = bound(10, scr_zoomwindow_viewsizey.value, 100) / 100.0;
1354                         r_refdef.width = vid.realwidth * sizex;
1355                         r_refdef.height = vid.realheight * sizey;
1356                         r_refdef.x = (vid.realwidth - r_refdef.width)/2;
1357                         r_refdef.y = 0;
1358                         r_refdef.fov_x = scr_zoomwindow_fov.value;
1359                         r_refdef.fov_y = CalcFov(r_refdef.fov_x, r_refdef.width, r_refdef.height);
1360
1361                         R_RenderView();
1362                 }
1363         }
1364
1365         // draw 2D stuff
1366         R_DrawQueue();
1367
1368         if (r_showtrispass)
1369                 r_showtrispass = false;
1370         else if (r_showtris.value > 0)
1371         {
1372                 rmeshstate_t m;
1373                 GL_BlendFunc(GL_ONE, GL_ONE);
1374                 GL_DepthTest(GL_FALSE);
1375                 GL_DepthMask(GL_FALSE);
1376                 memset(&m, 0, sizeof(m));
1377                 R_Mesh_State(&m);
1378                 r_showtrispass = true;
1379                 GL_ShowTrisColor(0.2,0.2,0.2,1);
1380                 goto showtris;
1381         }
1382
1383         if (gl_delayfinish.integer)
1384         {
1385                 // tell driver to commit it's partially full geometry queue to the rendering queue
1386                 // (this doesn't wait for the commands themselves to complete)
1387                 qglFlush();
1388         }
1389         else
1390         {
1391                 R_Mesh_Finish();
1392                 R_TimeReport("meshfinish");
1393                 VID_Finish();
1394                 R_TimeReport("finish");
1395         }
1396 }
1397
1398
1399 //===========================================================================
1400 // dynamic vertex array buffer subsystem
1401 //===========================================================================
1402
1403 float varray_vertex3f[65536*3];
1404 float varray_color4f[65536*4];
1405 float varray_texcoord2f[4][65536*2];
1406 float varray_texcoord3f[4][65536*3];
1407 float varray_normal3f[65536*3];
1408 int earray_element3i[65536];
1409
1410 //===========================================================================
1411 // vertex array caching subsystem
1412 //===========================================================================
1413
1414 typedef struct rcachearraylink_s
1415 {
1416         struct rcachearraylink_s *next, *prev;
1417         struct rcachearrayitem_s *data;
1418 }
1419 rcachearraylink_t;
1420
1421 typedef struct rcachearrayitem_s
1422 {
1423         // the original request structure
1424         rcachearrayrequest_t request;
1425         // active
1426         int active;
1427         // offset into r_mesh_rcachedata
1428         int offset;
1429         // for linking this into the sequential list
1430         rcachearraylink_t sequentiallink;
1431         // for linking this into the lookup list
1432         rcachearraylink_t hashlink;
1433 }
1434 rcachearrayitem_t;
1435
1436 #define RCACHEARRAY_HASHSIZE 65536
1437 #define RCACHEARRAY_ITEMS 4096
1438 #define RCACHEARRAY_DEFAULTSIZE (4 << 20)
1439
1440 // all active items are linked into this chain in sorted order
1441 static rcachearraylink_t r_mesh_rcachesequentialchain;
1442 // all inactive items are linked into this chain in unknown order
1443 static rcachearraylink_t r_mesh_rcachefreechain;
1444 // all active items are also linked into these chains (using their hashlink)
1445 static rcachearraylink_t r_mesh_rcachechain[RCACHEARRAY_HASHSIZE];
1446
1447 // all items are stored here, whether active or inactive
1448 static rcachearrayitem_t r_mesh_rcacheitems[RCACHEARRAY_ITEMS];
1449
1450 // size of data buffer
1451 static int r_mesh_rcachedata_size = RCACHEARRAY_DEFAULTSIZE;
1452 // data buffer
1453 static qbyte r_mesh_rcachedata[RCACHEARRAY_DEFAULTSIZE];
1454
1455 // current state
1456 static int r_mesh_rcachedata_offset;
1457 static rcachearraylink_t *r_mesh_rcachesequentialchain_current;
1458
1459 static void R_Mesh_CacheArray_Startup(void)
1460 {
1461         int i;
1462         rcachearraylink_t *l;
1463         // prepare all the linked lists
1464         l = &r_mesh_rcachesequentialchain;l->next = l->prev = l;l->data = NULL;
1465         l = &r_mesh_rcachefreechain;l->next = l->prev = l;l->data = NULL;
1466         memset(&r_mesh_rcachechain, 0, sizeof(r_mesh_rcachechain));
1467         for (i = 0;i < RCACHEARRAY_HASHSIZE;i++)
1468         {
1469                 l = &r_mesh_rcachechain[i];
1470                 l->next = l->prev = l;
1471                 l->data = NULL;
1472         }
1473         memset(&r_mesh_rcacheitems, 0, sizeof(r_mesh_rcacheitems));
1474         for (i = 0;i < RCACHEARRAY_ITEMS;i++)
1475         {
1476                 r_mesh_rcacheitems[i].hashlink.data = r_mesh_rcacheitems[i].sequentiallink.data = &r_mesh_rcacheitems[i];
1477                 l = &r_mesh_rcacheitems[i].sequentiallink;
1478                 l->next = &r_mesh_rcachefreechain;
1479                 l->prev = l->next->prev;
1480                 l->next->prev = l->prev->next = l;
1481         }
1482         // clear other state
1483         r_mesh_rcachedata_offset = 0;
1484         r_mesh_rcachesequentialchain_current = &r_mesh_rcachesequentialchain;
1485 }
1486
1487 static void R_Mesh_CacheArray_Shutdown(void)
1488 {
1489 }
1490
1491 /*
1492 static void R_Mesh_CacheArray_ValidateState(int num)
1493 {
1494         rcachearraylink_t *l, *lhead;
1495         lhead = &r_mesh_rcachesequentialchain;
1496         if (r_mesh_rcachesequentialchain_current == lhead)
1497                 return;
1498         for (l = lhead->next;l != lhead;l = l->next)
1499                 if (r_mesh_rcachesequentialchain_current == l)
1500                         return;
1501         Sys_Error("%i", num);
1502 }
1503 */
1504
1505 int R_Mesh_CacheArray(rcachearrayrequest_t *r)
1506 {
1507         rcachearraylink_t *l, *lhead, *lnext;
1508         rcachearrayitem_t *d;
1509         int hashindex, offset, offsetend;
1510
1511         //R_Mesh_CacheArray_ValidateState(3);
1512         // calculate a hashindex to choose a cache chain
1513         r->data = NULL;
1514         hashindex = CRC_Block((void *)r, sizeof(*r)) % RCACHEARRAY_HASHSIZE;
1515
1516         // is it already cached?
1517         for (lhead = &r_mesh_rcachechain[hashindex], l = lhead->next;l != lhead;l = l->next)
1518         {
1519                 if (!memcmp(&l->data->request, r, sizeof(l->data->request)))
1520                 {
1521                         // we have it cached already
1522                         r->data = r_mesh_rcachedata + l->data->offset;
1523                         return false;
1524                 }
1525         }
1526
1527         // we need to add a new cache item, this means finding a place for the new
1528         // data and making sure we have a free item available, lots of work...
1529
1530         // check if buffer needs to wrap
1531         if (r_mesh_rcachedata_offset + r->data_size > r_mesh_rcachedata_size)
1532         {
1533                 /*
1534                 if (r->data_size * 10 > r_mesh_rcachedata_size)
1535                 {
1536                         // realloc whole cache
1537                 }
1538                 */
1539                 // reset back to start
1540                 r_mesh_rcachedata_offset = 0;
1541                 r_mesh_rcachesequentialchain_current = &r_mesh_rcachesequentialchain;
1542         }
1543         offset = r_mesh_rcachedata_offset;
1544         r_mesh_rcachedata_offset += r->data_size;
1545         offsetend = r_mesh_rcachedata_offset;
1546         //R_Mesh_CacheArray_ValidateState(4);
1547
1548         /*
1549         {
1550                 int n;
1551                 for (lhead = &r_mesh_rcachesequentialchain, l = lhead->next, n = 0;l != lhead;l = l->next, n++);
1552                 Con_Printf("R_Mesh_CacheArray: new data range %i:%i, %i items are already linked\n", offset, offsetend, n);
1553         }
1554         */
1555
1556         // make room for the new data (remove old items)
1557         lhead = &r_mesh_rcachesequentialchain;
1558         l = r_mesh_rcachesequentialchain_current;
1559         if (l == lhead)
1560                 l = l->next;
1561         while (l != lhead && l->data->offset < offsetend && l->data->offset + l->data->request.data_size > offset)
1562         {
1563         //r_mesh_rcachesequentialchain_current = l;
1564         //R_Mesh_CacheArray_ValidateState(8);
1565                 lnext = l->next;
1566                 // if at the end of the chain, wrap around
1567                 if (lnext == lhead)
1568                         lnext = lnext->next;
1569         //r_mesh_rcachesequentialchain_current = lnext;
1570         //R_Mesh_CacheArray_ValidateState(10);
1571
1572                 // unlink from sequential chain
1573                 l->next->prev = l->prev;
1574                 l->prev->next = l->next;
1575         //R_Mesh_CacheArray_ValidateState(11);
1576                 // link into free chain
1577                 l->next = &r_mesh_rcachefreechain;
1578                 l->prev = l->next->prev;
1579                 l->next->prev = l->prev->next = l;
1580         //R_Mesh_CacheArray_ValidateState(12);
1581
1582                 l = &l->data->hashlink;
1583                 // unlink from hash chain
1584                 l->next->prev = l->prev;
1585                 l->prev->next = l->next;
1586
1587                 l = lnext;
1588         //r_mesh_rcachesequentialchain_current = l;
1589         //R_Mesh_CacheArray_ValidateState(9);
1590         }
1591         //r_mesh_rcachesequentialchain_current = l;
1592         //R_Mesh_CacheArray_ValidateState(5);
1593         // gobble an extra item if we have no free items available
1594         if (r_mesh_rcachefreechain.next == &r_mesh_rcachefreechain)
1595         {
1596                 lnext = l->next;
1597
1598                 // unlink from sequential chain
1599                 l->next->prev = l->prev;
1600                 l->prev->next = l->next;
1601                 // link into free chain
1602                 l->next = &r_mesh_rcachefreechain;
1603                 l->prev = l->next->prev;
1604                 l->next->prev = l->prev->next = l;
1605
1606                 l = &l->data->hashlink;
1607                 // unlink from hash chain
1608                 l->next->prev = l->prev;
1609                 l->prev->next = l->next;
1610
1611                 l = lnext;
1612         }
1613         r_mesh_rcachesequentialchain_current = l;
1614         //R_Mesh_CacheArray_ValidateState(6);
1615
1616         // now take an item from the free chain
1617         l = r_mesh_rcachefreechain.next;
1618         // set it up
1619         d = l->data;
1620         d->request = *r;
1621         d->offset = offset;
1622         // unlink
1623         l->next->prev = l->prev;
1624         l->prev->next = l->next;
1625         // relink to sequential
1626         l->next = r_mesh_rcachesequentialchain_current->prev;
1627         l->prev = l->next->prev;
1628         while (l->next->data && l->data && l->next->data->offset <= d->offset)
1629         {
1630                 //Con_Print(">\n");
1631                 l->next = l->next->next;
1632                 l->prev = l->prev->next;
1633         }
1634         while (l->prev->data && l->data && l->prev->data->offset >= d->offset)
1635         {
1636                 //Con_Print("<\n");
1637                 l->prev = l->prev->prev;
1638                 l->next = l->next->prev;
1639         }
1640         l->next->prev = l->prev->next = l;
1641         // also link into hash chain
1642         l = &l->data->hashlink;
1643         l->next = &r_mesh_rcachechain[hashindex];
1644         l->prev = l->next->prev;
1645         l->prev->next = l;
1646         l->next->prev = l->prev->next = l;
1647
1648
1649         //r_mesh_rcachesequentialchain_current = d->sequentiallink.next;
1650
1651         //R_Mesh_CacheArray_ValidateState(7);
1652         // and finally set the data pointer
1653         r->data = r_mesh_rcachedata + d->offset;
1654         // and tell the caller to fill the array
1655         return true;
1656 }
1657