]> icculus.org git repositories - divverent/darkplaces.git/blob - gl_backend.c
changed R_Mesh_TexCombine to use GL_COMBINE_ARB mode only when necessary
[divverent/darkplaces.git] / gl_backend.c
1
2 #include "quakedef.h"
3 #include "cl_collision.h"
4
5 cvar_t gl_mesh_drawrangeelements = {0, "gl_mesh_drawrangeelements", "1", "use glDrawRangeElements function if available instead of glDrawElements (for performance comparisons or bug testing)"};
6 cvar_t gl_mesh_testarrayelement = {0, "gl_mesh_testarrayelement", "0", "use glBegin(GL_TRIANGLES);glArrayElement();glEnd(); primitives instead of glDrawElements (useful to test for driver bugs with glDrawElements)"};
7 cvar_t gl_mesh_testmanualfeeding = {0, "gl_mesh_testmanualfeeding", "0", "use glBegin(GL_TRIANGLES);glTexCoord2f();glVertex3f();glEnd(); primitives instead of glDrawElements (useful to test for driver bugs with glDrawElements)"};
8 cvar_t gl_mesh_prefer_short_elements = {0, "gl_mesh_prefer_short_elements", "1", "use GL_UNSIGNED_SHORT element arrays instead of GL_UNSIGNED_INT"};
9 cvar_t gl_paranoid = {0, "gl_paranoid", "0", "enables OpenGL error checking and other tests"};
10 cvar_t gl_printcheckerror = {0, "gl_printcheckerror", "0", "prints all OpenGL error checks, useful to identify location of driver crashes"};
11
12 cvar_t r_render = {0, "r_render", "1", "enables rendering 3D views (you want this on!)"};
13 cvar_t r_renderview = {0, "r_renderview", "1", "enables rendering 3D views (you want this on!)"};
14 cvar_t r_waterwarp = {CVAR_SAVE, "r_waterwarp", "1", "warp view while underwater"};
15 cvar_t gl_polyblend = {CVAR_SAVE, "gl_polyblend", "1", "tints view while underwater, hurt, etc"};
16 cvar_t gl_dither = {CVAR_SAVE, "gl_dither", "1", "enables OpenGL dithering (16bit looks bad with this off)"};
17 cvar_t gl_lockarrays = {0, "gl_lockarrays", "0", "enables use of glLockArraysEXT, may cause glitches with some broken drivers, and may be slower than normal"};
18 cvar_t gl_lockarrays_minimumvertices = {0, "gl_lockarrays_minimumvertices", "1", "minimum number of vertices required for use of glLockArraysEXT, setting this too low may reduce performance"};
19 cvar_t gl_vbo = {CVAR_SAVE, "gl_vbo", "3", "make use of GL_ARB_vertex_buffer_object extension to store static geometry in video memory for faster rendering, 0 disables VBO allocation or use, 1 enables VBOs for vertex and triangle data, 2 only for vertex data, 3 for vertex data and triangle data of simple meshes (ones with only one surface)"};
20 cvar_t gl_fbo = {CVAR_SAVE, "gl_fbo", "1", "make use of GL_ARB_framebuffer_object extension to enable shadowmaps and other features using pixel formats different from the framebuffer"};
21
22 cvar_t v_flipped = {0, "v_flipped", "0", "mirror the screen (poor man's left handed mode)"};
23 qboolean v_flipped_state = false;
24
25 int gl_maxdrawrangeelementsvertices;
26 int gl_maxdrawrangeelementsindices;
27
28 #ifdef DEBUGGL
29 int errornumber = 0;
30
31 void GL_PrintError(int errornumber, char *filename, int linenumber)
32 {
33         switch(errornumber)
34         {
35 #ifdef GL_INVALID_ENUM
36         case GL_INVALID_ENUM:
37                 Con_Printf("GL_INVALID_ENUM at %s:%i\n", filename, linenumber);
38                 break;
39 #endif
40 #ifdef GL_INVALID_VALUE
41         case GL_INVALID_VALUE:
42                 Con_Printf("GL_INVALID_VALUE at %s:%i\n", filename, linenumber);
43                 break;
44 #endif
45 #ifdef GL_INVALID_OPERATION
46         case GL_INVALID_OPERATION:
47                 Con_Printf("GL_INVALID_OPERATION at %s:%i\n", filename, linenumber);
48                 break;
49 #endif
50 #ifdef GL_STACK_OVERFLOW
51         case GL_STACK_OVERFLOW:
52                 Con_Printf("GL_STACK_OVERFLOW at %s:%i\n", filename, linenumber);
53                 break;
54 #endif
55 #ifdef GL_STACK_UNDERFLOW
56         case GL_STACK_UNDERFLOW:
57                 Con_Printf("GL_STACK_UNDERFLOW at %s:%i\n", filename, linenumber);
58                 break;
59 #endif
60 #ifdef GL_OUT_OF_MEMORY
61         case GL_OUT_OF_MEMORY:
62                 Con_Printf("GL_OUT_OF_MEMORY at %s:%i\n", filename, linenumber);
63                 break;
64 #endif
65 #ifdef GL_TABLE_TOO_LARGE
66         case GL_TABLE_TOO_LARGE:
67                 Con_Printf("GL_TABLE_TOO_LARGE at %s:%i\n", filename, linenumber);
68                 break;
69 #endif
70 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT
71         case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
72                 Con_Printf("GL_INVALID_FRAMEBUFFER_OPERATION at %s:%i\n", filename, linenumber);
73                 break;
74 #endif
75         default:
76                 Con_Printf("GL UNKNOWN (%i) at %s:%i\n", errornumber, filename, linenumber);
77                 break;
78         }
79 }
80 #endif
81
82 #define BACKENDACTIVECHECK if (!gl_state.active) Sys_Error("GL backend function called when backend is not active");
83
84 void SCR_ScreenShot_f (void);
85
86 typedef struct gl_bufferobjectinfo_s
87 {
88         int target;
89         int object;
90         size_t size;
91         char name[MAX_QPATH];
92 }
93 gl_bufferobjectinfo_t;
94
95 typedef struct gltextureunit_s
96 {
97         const void *pointer_texcoord;
98         size_t pointer_texcoord_offset;
99         int pointer_texcoord_buffer;
100         int t2d, t3d, tcubemap, trectangle;
101         int arrayenabled;
102         unsigned int arraycomponents;
103         int rgbscale, alphascale;
104         int combine;
105         int combinergb, combinealpha;
106         // texmatrixenabled exists only to avoid unnecessary texmatrix compares
107         int texmatrixenabled;
108         matrix4x4_t matrix;
109 }
110 gltextureunit_t;
111
112 typedef struct gl_state_s
113 {
114         int cullface;
115         int cullfaceenable;
116         int blendfunc1;
117         int blendfunc2;
118         int blend;
119         GLboolean depthmask;
120         int colormask; // stored as bottom 4 bits: r g b a (3 2 1 0 order)
121         int depthtest;
122         float depthrange[2];
123         float polygonoffset[2];
124         int alphatest;
125         int scissortest;
126         unsigned int unit;
127         unsigned int clientunit;
128         gltextureunit_t units[MAX_TEXTUREUNITS];
129         float color4f[4];
130         int lockrange_first;
131         int lockrange_count;
132         int vertexbufferobject;
133         int elementbufferobject;
134         qboolean pointer_color_enabled;
135         const void *pointer_vertex;
136         const void *pointer_color;
137         size_t pointer_vertex_offset;
138         size_t pointer_color_offset;
139         int pointer_vertex_buffer;
140         int pointer_color_buffer;
141         qboolean active;
142 }
143 gl_state_t;
144
145 static gl_state_t gl_state;
146
147 static memexpandablearray_t gl_bufferobjectinfoarray;
148
149 static r_viewport_t backend_viewport;
150 static matrix4x4_t backend_modelmatrix;
151 static matrix4x4_t backend_modelviewmatrix;
152
153 /*
154 note: here's strip order for a terrain row:
155 0--1--2--3--4
156 |\ |\ |\ |\ |
157 | \| \| \| \|
158 A--B--C--D--E
159 clockwise
160
161 A0B, 01B, B1C, 12C, C2D, 23D, D3E, 34E
162
163 *elements++ = i + row;
164 *elements++ = i;
165 *elements++ = i + row + 1;
166 *elements++ = i;
167 *elements++ = i + 1;
168 *elements++ = i + row + 1;
169
170
171 for (y = 0;y < rows - 1;y++)
172 {
173         for (x = 0;x < columns - 1;x++)
174         {
175                 i = y * rows + x;
176                 *elements++ = i + columns;
177                 *elements++ = i;
178                 *elements++ = i + columns + 1;
179                 *elements++ = i;
180                 *elements++ = i + 1;
181                 *elements++ = i + columns + 1;
182         }
183 }
184
185 alternative:
186 0--1--2--3--4
187 | /| /|\ | /|
188 |/ |/ | \|/ |
189 A--B--C--D--E
190 counterclockwise
191
192 for (y = 0;y < rows - 1;y++)
193 {
194         for (x = 0;x < columns - 1;x++)
195         {
196                 i = y * rows + x;
197                 *elements++ = i;
198                 *elements++ = i + columns;
199                 *elements++ = i + columns + 1;
200                 *elements++ = i + columns;
201                 *elements++ = i + columns + 1;
202                 *elements++ = i + 1;
203         }
204 }
205 */
206
207 int polygonelement3i[(POLYGONELEMENTS_MAXPOINTS-2)*3];
208 unsigned short polygonelement3s[(POLYGONELEMENTS_MAXPOINTS-2)*3];
209 int quadelement3i[QUADELEMENTS_MAXQUADS*6];
210 unsigned short quadelement3s[QUADELEMENTS_MAXQUADS*6];
211
212 void GL_VBOStats_f(void)
213 {
214         GL_Mesh_ListVBOs(true);
215 }
216
217 static void GL_Backend_ResetState(void);
218
219 static void gl_backend_start(void)
220 {
221         CHECKGLERROR
222
223         if (qglDrawRangeElements != NULL)
224         {
225                 CHECKGLERROR
226                 qglGetIntegerv(GL_MAX_ELEMENTS_VERTICES, &gl_maxdrawrangeelementsvertices);
227                 CHECKGLERROR
228                 qglGetIntegerv(GL_MAX_ELEMENTS_INDICES, &gl_maxdrawrangeelementsindices);
229                 CHECKGLERROR
230                 Con_DPrintf("GL_MAX_ELEMENTS_VERTICES = %i\nGL_MAX_ELEMENTS_INDICES = %i\n", gl_maxdrawrangeelementsvertices, gl_maxdrawrangeelementsindices);
231         }
232
233         if (vid.support.arb_fragment_shader)
234         {
235                 Con_DPrintf("GLSL shader support detected: texture units = %i texenv, %i image, %i array\n", vid.texunits, vid.teximageunits, vid.texarrayunits);
236                 vid.teximageunits = bound(1, vid.teximageunits, MAX_TEXTUREUNITS);
237                 vid.texarrayunits = bound(1, vid.texarrayunits, MAX_TEXTUREUNITS);
238         }
239         else
240                 Con_DPrintf("GL_MAX_TEXTUREUNITS = %i\n", vid.texunits);
241
242         Mem_ExpandableArray_NewArray(&gl_bufferobjectinfoarray, r_main_mempool, sizeof(gl_bufferobjectinfo_t), 128);
243
244         Con_DPrintf("OpenGL backend started.\n");
245
246         CHECKGLERROR
247
248         gl_state.active = true;
249         GL_Backend_ResetState();
250 }
251
252 static void gl_backend_shutdown(void)
253 {
254         gl_state.active = false;
255
256         Con_DPrint("OpenGL Backend shutting down\n");
257
258         Mem_ExpandableArray_FreeArray(&gl_bufferobjectinfoarray);
259
260         memset(&gl_state, 0, sizeof(gl_state));
261 }
262
263 static void gl_backend_newmap(void)
264 {
265 }
266
267 void gl_backend_init(void)
268 {
269         int i;
270
271         for (i = 0;i < POLYGONELEMENTS_MAXPOINTS - 2;i++)
272         {
273                 polygonelement3s[i * 3 + 0] = 0;
274                 polygonelement3s[i * 3 + 1] = i + 1;
275                 polygonelement3s[i * 3 + 2] = i + 2;
276         }
277         // elements for rendering a series of quads as triangles
278         for (i = 0;i < QUADELEMENTS_MAXQUADS;i++)
279         {
280                 quadelement3s[i * 6 + 0] = i * 4;
281                 quadelement3s[i * 6 + 1] = i * 4 + 1;
282                 quadelement3s[i * 6 + 2] = i * 4 + 2;
283                 quadelement3s[i * 6 + 3] = i * 4;
284                 quadelement3s[i * 6 + 4] = i * 4 + 2;
285                 quadelement3s[i * 6 + 5] = i * 4 + 3;
286         }
287
288         for (i = 0;i < (POLYGONELEMENTS_MAXPOINTS - 2)*3;i++)
289                 polygonelement3i[i] = polygonelement3s[i];
290         for (i = 0;i < QUADELEMENTS_MAXQUADS*3;i++)
291                 quadelement3i[i] = quadelement3s[i];
292
293         Cvar_RegisterVariable(&r_render);
294         Cvar_RegisterVariable(&r_renderview);
295         Cvar_RegisterVariable(&r_waterwarp);
296         Cvar_RegisterVariable(&gl_polyblend);
297         Cvar_RegisterVariable(&v_flipped);
298         Cvar_RegisterVariable(&gl_dither);
299         Cvar_RegisterVariable(&gl_lockarrays);
300         Cvar_RegisterVariable(&gl_lockarrays_minimumvertices);
301         Cvar_RegisterVariable(&gl_vbo);
302         Cvar_RegisterVariable(&gl_paranoid);
303         Cvar_RegisterVariable(&gl_printcheckerror);
304
305         Cvar_RegisterVariable(&gl_mesh_drawrangeelements);
306         Cvar_RegisterVariable(&gl_mesh_testarrayelement);
307         Cvar_RegisterVariable(&gl_mesh_testmanualfeeding);
308         Cvar_RegisterVariable(&gl_mesh_prefer_short_elements);
309
310         Cmd_AddCommand("gl_vbostats", GL_VBOStats_f, "prints a list of all buffer objects (vertex data and triangle elements) and total video memory used by them");
311
312         R_RegisterModule("GL_Backend", gl_backend_start, gl_backend_shutdown, gl_backend_newmap);
313 }
314
315 void GL_SetMirrorState(qboolean state);
316
317 void R_Viewport_TransformToScreen(const r_viewport_t *v, const vec4_t in, vec4_t out)
318 {
319         vec4_t temp;
320         float iw;
321         Matrix4x4_Transform4 (&v->viewmatrix, in, temp);
322         Matrix4x4_Transform4 (&v->projectmatrix, temp, out);
323         iw = 1.0f / out[3];
324         out[0] = v->x + (out[0] * iw + 1.0f) * v->width * 0.5f;
325         out[1] = v->y + v->height - (out[1] * iw + 1.0f) * v->height * 0.5f;
326         out[2] = v->z + (out[2] * iw + 1.0f) * v->depth * 0.5f;
327 }
328
329 static void R_Viewport_ApplyNearClipPlane(r_viewport_t *v, double normalx, double normaly, double normalz, double dist)
330 {
331         double q[4];
332         double d;
333         float clipPlane[4], v3[3], v4[3];
334         float normal[3];
335
336         // This is inspired by Oblique Depth Projection from http://www.terathon.com/code/oblique.php
337
338         VectorSet(normal, normalx, normaly, normalz);
339         Matrix4x4_Transform3x3(&v->viewmatrix, normal, clipPlane);
340         VectorScale(normal, dist, v3);
341         Matrix4x4_Transform(&v->viewmatrix, v3, v4);
342         // FIXME: LordHavoc: I think this can be done more efficiently somehow but I can't remember the technique
343         clipPlane[3] = -DotProduct(v4, clipPlane);
344
345 #if 0
346 {
347         // testing code for comparing results
348         float clipPlane2[4];
349         VectorCopy4(clipPlane, clipPlane2);
350         R_Mesh_Matrix(&identitymatrix);
351         VectorSet(q, normal[0], normal[1], normal[2], -dist);
352         qglClipPlane(GL_CLIP_PLANE0, q);
353         qglGetClipPlane(GL_CLIP_PLANE0, q);
354         VectorCopy4(q, clipPlane);
355 }
356 #endif
357
358         // Calculate the clip-space corner point opposite the clipping plane
359         // as (sgn(clipPlane.x), sgn(clipPlane.y), 1, 1) and
360         // transform it into camera space by multiplying it
361         // by the inverse of the projection matrix
362         q[0] = ((clipPlane[0] < 0.0f ? -1.0f : clipPlane[0] > 0.0f ? 1.0f : 0.0f) + v->m[8]) / v->m[0];
363         q[1] = ((clipPlane[1] < 0.0f ? -1.0f : clipPlane[1] > 0.0f ? 1.0f : 0.0f) + v->m[9]) / v->m[5];
364         q[2] = -1.0f;
365         q[3] = (1.0f + v->m[10]) / v->m[14];
366
367         // Calculate the scaled plane vector
368         d = 2.0f / DotProduct4(clipPlane, q);
369
370         // Replace the third row of the projection matrix
371         v->m[2] = clipPlane[0] * d;
372         v->m[6] = clipPlane[1] * d;
373         v->m[10] = clipPlane[2] * d + 1.0f;
374         v->m[14] = clipPlane[3] * d;
375 }
376
377 void R_Viewport_InitOrtho(r_viewport_t *v, const matrix4x4_t *cameramatrix, int x, int y, int width, int height, double x1, double y1, double x2, double y2, double nearclip, double farclip, const double *nearplane)
378 {
379         float left = x1, right = x2, bottom = y2, top = y1, zNear = nearclip, zFar = farclip;
380         memset(v, 0, sizeof(*v));
381         v->type = R_VIEWPORTTYPE_ORTHO;
382         v->cameramatrix = *cameramatrix;
383         v->x = x;
384         v->y = y;
385         v->z = 0;
386         v->width = width;
387         v->height = height;
388         v->depth = 1;
389         v->m[0]  = 2/(right - left);
390         v->m[5]  = 2/(top - bottom);
391         v->m[10] = -2/(zFar - zNear);
392         v->m[12] = - (right + left)/(right - left);
393         v->m[13] = - (top + bottom)/(top - bottom);
394         v->m[14] = - (zFar + zNear)/(zFar - zNear);
395         v->m[15] = 1;
396
397         Matrix4x4_Invert_Full(&v->viewmatrix, &v->cameramatrix);
398         Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
399
400         if (nearplane)
401                 R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
402
403 #if 0
404         {
405                 vec4_t test1;
406                 vec4_t test2;
407                 Vector4Set(test1, (x1+x2)*0.5f, (y1+y2)*0.5f, 0.0f, 1.0f);
408                 R_Viewport_TransformToScreen(v, test1, test2);
409                 Con_Printf("%f %f %f -> %f %f %f\n", test1[0], test1[1], test1[2], test2[0], test2[1], test2[2]);
410         }
411 #endif
412 }
413
414 void R_Viewport_InitPerspective(r_viewport_t *v, const matrix4x4_t *cameramatrix, int x, int y, int width, int height, double frustumx, double frustumy, double nearclip, double farclip, const double *nearplane)
415 {
416         matrix4x4_t tempmatrix, basematrix;
417         memset(v, 0, sizeof(*v));
418
419         if(v_flipped.integer)
420                 frustumx = -frustumx;
421
422         v->type = R_VIEWPORTTYPE_PERSPECTIVE;
423         v->cameramatrix = *cameramatrix;
424         v->x = x;
425         v->y = y;
426         v->z = 0;
427         v->width = width;
428         v->height = height;
429         v->depth = 1;
430         v->m[0]  = 1.0 / frustumx;
431         v->m[5]  = 1.0 / frustumy;
432         v->m[10] = -(farclip + nearclip) / (farclip - nearclip);
433         v->m[11] = -1;
434         v->m[14] = -2 * nearclip * farclip / (farclip - nearclip);
435
436         Matrix4x4_Invert_Full(&tempmatrix, &v->cameramatrix);
437         Matrix4x4_CreateRotate(&basematrix, -90, 1, 0, 0);
438         Matrix4x4_ConcatRotate(&basematrix, 90, 0, 0, 1);
439         Matrix4x4_Concat(&v->viewmatrix, &basematrix, &tempmatrix);
440
441         Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
442
443         if (nearplane)
444                 R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
445 }
446
447 void R_Viewport_InitPerspectiveInfinite(r_viewport_t *v, const matrix4x4_t *cameramatrix, int x, int y, int width, int height, double frustumx, double frustumy, double nearclip, const double *nearplane)
448 {
449         matrix4x4_t tempmatrix, basematrix;
450         const double nudge = 1.0 - 1.0 / (1<<23);
451         memset(v, 0, sizeof(*v));
452
453         if(v_flipped.integer)
454                 frustumx = -frustumx;
455
456         v->type = R_VIEWPORTTYPE_PERSPECTIVE_INFINITEFARCLIP;
457         v->cameramatrix = *cameramatrix;
458         v->x = x;
459         v->y = y;
460         v->z = 0;
461         v->width = width;
462         v->height = height;
463         v->depth = 1;
464         v->m[ 0] = 1.0 / frustumx;
465         v->m[ 5] = 1.0 / frustumy;
466         v->m[10] = -nudge;
467         v->m[11] = -1;
468         v->m[14] = -2 * nearclip * nudge;
469
470         Matrix4x4_Invert_Full(&tempmatrix, &v->cameramatrix);
471         Matrix4x4_CreateRotate(&basematrix, -90, 1, 0, 0);
472         Matrix4x4_ConcatRotate(&basematrix, 90, 0, 0, 1);
473         Matrix4x4_Concat(&v->viewmatrix, &basematrix, &tempmatrix);
474
475         Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
476
477         if (nearplane)
478                 R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
479 }
480
481 float cubeviewmatrix[6][16] =
482 {
483     // standard cubemap projections
484     { // +X
485          0, 0,-1, 0,
486          0,-1, 0, 0,
487         -1, 0, 0, 0,
488          0, 0, 0, 1,
489     },
490     { // -X
491          0, 0, 1, 0,
492          0,-1, 0, 0,
493          1, 0, 0, 0,
494          0, 0, 0, 1,
495     },
496     { // +Y
497          1, 0, 0, 0,
498          0, 0,-1, 0,
499          0, 1, 0, 0,
500          0, 0, 0, 1,
501     },
502     { // -Y
503          1, 0, 0, 0,
504          0, 0, 1, 0,
505          0,-1, 0, 0,
506          0, 0, 0, 1,
507     },
508     { // +Z
509          1, 0, 0, 0,
510          0,-1, 0, 0,
511          0, 0,-1, 0,
512          0, 0, 0, 1,
513     },
514     { // -Z
515         -1, 0, 0, 0,
516          0,-1, 0, 0,
517          0, 0, 1, 0,
518          0, 0, 0, 1,
519     },
520 };
521 float rectviewmatrix[6][16] =
522 {
523     // sign-preserving cubemap projections
524     { // +X
525          0, 0,-1, 0,
526          0, 1, 0, 0,
527          1, 0, 0, 0,
528          0, 0, 0, 1,
529     },
530     { // -X
531          0, 0, 1, 0,
532          0, 1, 0, 0,
533          1, 0, 0, 0,
534          0, 0, 0, 1,
535     },
536     { // +Y
537          1, 0, 0, 0,
538          0, 0,-1, 0,
539          0, 1, 0, 0,
540          0, 0, 0, 1,
541     },
542     { // -Y
543          1, 0, 0, 0,
544          0, 0, 1, 0,
545          0, 1, 0, 0,
546          0, 0, 0, 1,
547     },
548     { // +Z
549          1, 0, 0, 0,
550          0, 1, 0, 0,
551          0, 0,-1, 0,
552          0, 0, 0, 1,
553     },
554     { // -Z
555          1, 0, 0, 0,
556          0, 1, 0, 0,
557          0, 0, 1, 0,
558          0, 0, 0, 1,
559     },
560 };
561
562 void R_Viewport_InitCubeSideView(r_viewport_t *v, const matrix4x4_t *cameramatrix, int side, int size, float nearclip, float farclip, const float *nearplane)
563 {
564         matrix4x4_t tempmatrix, basematrix;
565         memset(v, 0, sizeof(*v));
566         v->type = R_VIEWPORTTYPE_PERSPECTIVECUBESIDE;
567         v->cameramatrix = *cameramatrix;
568         v->width = size;
569         v->height = size;
570         v->depth = 1;
571         v->m[0] = v->m[5] = 1.0f;
572         v->m[10] = -(farclip + nearclip) / (farclip - nearclip);
573         v->m[11] = -1;
574         v->m[14] = -2 * nearclip * farclip / (farclip - nearclip);
575
576         Matrix4x4_FromArrayFloatGL(&basematrix, cubeviewmatrix[side]);
577         Matrix4x4_Invert_Simple(&tempmatrix, &v->cameramatrix);
578         Matrix4x4_Concat(&v->viewmatrix, &basematrix, &tempmatrix);
579         Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
580
581         if (nearplane)
582                 R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
583 }
584
585 void R_Viewport_InitRectSideView(r_viewport_t *v, const matrix4x4_t *cameramatrix, int side, int size, int border, float nearclip, float farclip, const float *nearplane)
586 {
587         matrix4x4_t tempmatrix, basematrix;
588         memset(v, 0, sizeof(*v));
589         v->type = R_VIEWPORTTYPE_PERSPECTIVECUBESIDE;
590         v->cameramatrix = *cameramatrix;
591         v->x = (side & 1) * size;
592         v->y = (side >> 1) * size;
593         v->width = size;
594         v->height = size;
595         v->depth = 1;
596         v->m[0] = v->m[5] = 1.0f * ((float)size - border) / size;
597         v->m[10] = -(farclip + nearclip) / (farclip - nearclip);
598         v->m[11] = -1;
599         v->m[14] = -2 * nearclip * farclip / (farclip - nearclip);
600
601         Matrix4x4_FromArrayFloatGL(&basematrix, rectviewmatrix[side]);
602         Matrix4x4_Invert_Simple(&tempmatrix, &v->cameramatrix);
603         Matrix4x4_Concat(&v->viewmatrix, &basematrix, &tempmatrix);
604         Matrix4x4_FromArrayDoubleGL(&v->projectmatrix, v->m);
605
606         if (nearplane)
607                 R_Viewport_ApplyNearClipPlane(v, nearplane[0], nearplane[1], nearplane[2], nearplane[3]);
608 }
609
610 void R_SetViewport(const r_viewport_t *v)
611 {
612         float glmatrix[16];
613         backend_viewport = *v;
614
615         CHECKGLERROR
616         qglViewport(v->x, v->y, v->width, v->height);CHECKGLERROR
617
618         // Load the projection matrix into OpenGL
619         qglMatrixMode(GL_PROJECTION);CHECKGLERROR
620         qglLoadMatrixd(backend_viewport.m);CHECKGLERROR
621         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
622
623         // FIXME: v_flipped_state is evil, this probably breaks somewhere
624         GL_SetMirrorState(v_flipped.integer && (v->type == R_VIEWPORTTYPE_PERSPECTIVE || v->type == R_VIEWPORTTYPE_PERSPECTIVE_INFINITEFARCLIP));
625
626         // directly force an update of the modelview matrix
627         Matrix4x4_Concat(&backend_modelviewmatrix, &backend_viewport.viewmatrix, &backend_modelmatrix);
628         Matrix4x4_ToArrayFloatGL(&backend_modelviewmatrix, glmatrix);
629         qglLoadMatrixf(glmatrix);CHECKGLERROR
630 }
631
632 void R_GetViewport(r_viewport_t *v)
633 {
634         *v = backend_viewport;
635 }
636
637 static void GL_BindVBO(int bufferobject)
638 {
639         if (gl_state.vertexbufferobject != bufferobject)
640         {
641                 gl_state.vertexbufferobject = bufferobject;
642                 CHECKGLERROR
643                 qglBindBufferARB(GL_ARRAY_BUFFER_ARB, bufferobject);
644                 CHECKGLERROR
645         }
646 }
647
648 static void GL_BindEBO(int bufferobject)
649 {
650         if (gl_state.elementbufferobject != bufferobject)
651         {
652                 gl_state.elementbufferobject = bufferobject;
653                 CHECKGLERROR
654                 qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, bufferobject);
655                 CHECKGLERROR
656         }
657 }
658
659 static void GL_Backend_ResetState(void)
660 {
661         unsigned int i;
662         memset(&gl_state, 0, sizeof(gl_state));
663         gl_state.active = true;
664         gl_state.depthtest = true;
665         gl_state.alphatest = false;
666         gl_state.blendfunc1 = GL_ONE;
667         gl_state.blendfunc2 = GL_ZERO;
668         gl_state.blend = false;
669         gl_state.depthmask = GL_TRUE;
670         gl_state.colormask = 15;
671         gl_state.color4f[0] = gl_state.color4f[1] = gl_state.color4f[2] = gl_state.color4f[3] = 1;
672         gl_state.lockrange_first = 0;
673         gl_state.lockrange_count = 0;
674         gl_state.cullface = v_flipped_state ? GL_BACK : GL_FRONT; // quake is backwards, this culls back faces
675         gl_state.cullfaceenable = true;
676         gl_state.polygonoffset[0] = 0;
677         gl_state.polygonoffset[1] = 0;
678
679         CHECKGLERROR
680
681         qglColorMask(1, 1, 1, 1);
682         qglAlphaFunc(GL_GEQUAL, 0.5);CHECKGLERROR
683         qglDisable(GL_ALPHA_TEST);CHECKGLERROR
684         qglBlendFunc(gl_state.blendfunc1, gl_state.blendfunc2);CHECKGLERROR
685         qglDisable(GL_BLEND);CHECKGLERROR
686         qglCullFace(gl_state.cullface);CHECKGLERROR
687         qglEnable(GL_CULL_FACE);CHECKGLERROR
688         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
689         qglEnable(GL_DEPTH_TEST);CHECKGLERROR
690         qglDepthMask(gl_state.depthmask);CHECKGLERROR
691         qglPolygonOffset(gl_state.polygonoffset[0], gl_state.polygonoffset[1]);
692
693         if (vid.support.arb_vertex_buffer_object)
694         {
695                 qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
696                 qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
697         }
698
699         if (vid.support.ext_framebuffer_object)
700         {
701                 qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
702                 qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
703         }
704
705         qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), NULL);CHECKGLERROR
706         qglEnableClientState(GL_VERTEX_ARRAY);CHECKGLERROR
707
708         qglColorPointer(4, GL_FLOAT, sizeof(float[4]), NULL);CHECKGLERROR
709         qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR
710
711         GL_Color(0, 0, 0, 0);
712         GL_Color(1, 1, 1, 1);
713
714         gl_state.unit = MAX_TEXTUREUNITS;
715         gl_state.clientunit = MAX_TEXTUREUNITS;
716         for (i = 0;i < vid.teximageunits;i++)
717         {
718                 GL_ActiveTexture(i);
719                 qglBindTexture(GL_TEXTURE_2D, 0);CHECKGLERROR
720                 if (vid.support.ext_texture_3d)
721                 {
722                         qglBindTexture(GL_TEXTURE_3D, 0);CHECKGLERROR
723                 }
724                 if (vid.support.arb_texture_cube_map)
725                 {
726                         qglBindTexture(GL_TEXTURE_CUBE_MAP_ARB, 0);CHECKGLERROR
727                 }
728                 if (vid.support.arb_texture_rectangle)
729                 {
730                         qglBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);CHECKGLERROR
731                 }
732         }
733
734         for (i = 0;i < vid.texarrayunits;i++)
735         {
736                 GL_ClientActiveTexture(i);
737                 GL_BindVBO(0);
738                 qglTexCoordPointer(2, GL_FLOAT, sizeof(float[2]), NULL);CHECKGLERROR
739                 qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
740         }
741
742         for (i = 0;i < vid.texunits;i++)
743         {
744                 GL_ActiveTexture(i);
745                 qglDisable(GL_TEXTURE_2D);CHECKGLERROR
746                 if (vid.support.ext_texture_3d)
747                 {
748                         qglDisable(GL_TEXTURE_3D);CHECKGLERROR
749                 }
750                 if (vid.support.arb_texture_cube_map)
751                 {
752                         qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
753                 }
754                 if (vid.support.arb_texture_rectangle)
755                 {
756                         qglDisable(GL_TEXTURE_RECTANGLE_ARB);CHECKGLERROR
757                 }
758                 qglMatrixMode(GL_TEXTURE);CHECKGLERROR
759                 qglLoadIdentity();CHECKGLERROR
760                 qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
761                 qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);CHECKGLERROR
762                 CHECKGLERROR
763         }
764         CHECKGLERROR
765 }
766
767 void GL_ActiveTexture(unsigned int num)
768 {
769         if (gl_state.unit != num)
770         {
771                 gl_state.unit = num;
772                 if (qglActiveTexture)
773                 {
774                         CHECKGLERROR
775                         qglActiveTexture(GL_TEXTURE0_ARB + gl_state.unit);
776                         CHECKGLERROR
777                 }
778         }
779 }
780
781 void GL_ClientActiveTexture(unsigned int num)
782 {
783         if (gl_state.clientunit != num)
784         {
785                 gl_state.clientunit = num;
786                 if (qglActiveTexture)
787                 {
788                         CHECKGLERROR
789                         qglClientActiveTexture(GL_TEXTURE0_ARB + gl_state.clientunit);
790                         CHECKGLERROR
791                 }
792         }
793 }
794
795 void GL_BlendFunc(int blendfunc1, int blendfunc2)
796 {
797         if (gl_state.blendfunc1 != blendfunc1 || gl_state.blendfunc2 != blendfunc2)
798         {
799                 CHECKGLERROR
800                 qglBlendFunc(gl_state.blendfunc1 = blendfunc1, gl_state.blendfunc2 = blendfunc2);CHECKGLERROR
801                 if (gl_state.blendfunc2 == GL_ZERO)
802                 {
803                         if (gl_state.blendfunc1 == GL_ONE)
804                         {
805                                 if (gl_state.blend)
806                                 {
807                                         gl_state.blend = 0;
808                                         qglDisable(GL_BLEND);CHECKGLERROR
809                                 }
810                         }
811                         else
812                         {
813                                 if (!gl_state.blend)
814                                 {
815                                         gl_state.blend = 1;
816                                         qglEnable(GL_BLEND);CHECKGLERROR
817                                 }
818                         }
819                 }
820                 else
821                 {
822                         if (!gl_state.blend)
823                         {
824                                 gl_state.blend = 1;
825                                 qglEnable(GL_BLEND);CHECKGLERROR
826                         }
827                 }
828         }
829 }
830
831 void GL_DepthMask(int state)
832 {
833         if (gl_state.depthmask != state)
834         {
835                 CHECKGLERROR
836                 qglDepthMask(gl_state.depthmask = state);CHECKGLERROR
837         }
838 }
839
840 void GL_DepthTest(int state)
841 {
842         if (gl_state.depthtest != state)
843         {
844                 gl_state.depthtest = state;
845                 CHECKGLERROR
846                 if (gl_state.depthtest)
847                 {
848                         qglEnable(GL_DEPTH_TEST);CHECKGLERROR
849                 }
850                 else
851                 {
852                         qglDisable(GL_DEPTH_TEST);CHECKGLERROR
853                 }
854         }
855 }
856
857 void GL_DepthRange(float nearfrac, float farfrac)
858 {
859         if (gl_state.depthrange[0] != nearfrac || gl_state.depthrange[1] != farfrac)
860         {
861                 gl_state.depthrange[0] = nearfrac;
862                 gl_state.depthrange[1] = farfrac;
863                 qglDepthRange(nearfrac, farfrac);
864         }
865 }
866
867 void GL_PolygonOffset(float planeoffset, float depthoffset)
868 {
869         if (gl_state.polygonoffset[0] != planeoffset || gl_state.polygonoffset[1] != depthoffset)
870         {
871                 gl_state.polygonoffset[0] = planeoffset;
872                 gl_state.polygonoffset[1] = depthoffset;
873                 qglPolygonOffset(planeoffset, depthoffset);
874         }
875 }
876
877 void GL_SetMirrorState(qboolean state)
878 {
879         if(!state != !v_flipped_state)
880         {
881                 // change cull face mode!
882                 if(gl_state.cullface == GL_BACK)
883                         qglCullFace((gl_state.cullface = GL_FRONT));
884                 else if(gl_state.cullface == GL_FRONT)
885                         qglCullFace((gl_state.cullface = GL_BACK));
886         }
887         v_flipped_state = state;
888 }
889
890 void GL_CullFace(int state)
891 {
892         CHECKGLERROR
893
894         if(v_flipped_state)
895         {
896                 if(state == GL_FRONT)
897                         state = GL_BACK;
898                 else if(state == GL_BACK)
899                         state = GL_FRONT;
900         }
901
902         if (state != GL_NONE)
903         {
904                 if (!gl_state.cullfaceenable)
905                 {
906                         gl_state.cullfaceenable = true;
907                         qglEnable(GL_CULL_FACE);CHECKGLERROR
908                 }
909                 if (gl_state.cullface != state)
910                 {
911                         gl_state.cullface = state;
912                         qglCullFace(gl_state.cullface);CHECKGLERROR
913                 }
914         }
915         else
916         {
917                 if (gl_state.cullfaceenable)
918                 {
919                         gl_state.cullfaceenable = false;
920                         qglDisable(GL_CULL_FACE);CHECKGLERROR
921                 }
922         }
923 }
924
925 void GL_AlphaTest(int state)
926 {
927         if (gl_state.alphatest != state)
928         {
929                 gl_state.alphatest = state;
930                 CHECKGLERROR
931                 if (gl_state.alphatest)
932                 {
933                         qglEnable(GL_ALPHA_TEST);CHECKGLERROR
934                 }
935                 else
936                 {
937                         qglDisable(GL_ALPHA_TEST);CHECKGLERROR
938                 }
939         }
940 }
941
942 void GL_ColorMask(int r, int g, int b, int a)
943 {
944         int state = r*8 + g*4 + b*2 + a*1;
945         if (gl_state.colormask != state)
946         {
947                 gl_state.colormask = state;
948                 CHECKGLERROR
949                 qglColorMask((GLboolean)r, (GLboolean)g, (GLboolean)b, (GLboolean)a);CHECKGLERROR
950         }
951 }
952
953 void GL_Color(float cr, float cg, float cb, float ca)
954 {
955         if (gl_state.pointer_color_enabled || gl_state.color4f[0] != cr || gl_state.color4f[1] != cg || gl_state.color4f[2] != cb || gl_state.color4f[3] != ca)
956         {
957                 gl_state.color4f[0] = cr;
958                 gl_state.color4f[1] = cg;
959                 gl_state.color4f[2] = cb;
960                 gl_state.color4f[3] = ca;
961                 CHECKGLERROR
962                 qglColor4f(gl_state.color4f[0], gl_state.color4f[1], gl_state.color4f[2], gl_state.color4f[3]);
963                 CHECKGLERROR
964         }
965 }
966
967 void GL_LockArrays(int first, int count)
968 {
969         if (count < gl_lockarrays_minimumvertices.integer)
970         {
971                 first = 0;
972                 count = 0;
973         }
974         if (gl_state.lockrange_count != count || gl_state.lockrange_first != first)
975         {
976                 if (gl_state.lockrange_count)
977                 {
978                         gl_state.lockrange_count = 0;
979                         CHECKGLERROR
980                         qglUnlockArraysEXT();
981                         CHECKGLERROR
982                 }
983                 if (count && vid.support.ext_compiled_vertex_array && gl_lockarrays.integer)
984                 {
985                         gl_state.lockrange_first = first;
986                         gl_state.lockrange_count = count;
987                         CHECKGLERROR
988                         qglLockArraysEXT(first, count);
989                         CHECKGLERROR
990                 }
991         }
992 }
993
994 void GL_Scissor (int x, int y, int width, int height)
995 {
996         CHECKGLERROR
997         qglScissor(x, y,width,height);
998         CHECKGLERROR
999 }
1000
1001 void GL_ScissorTest(int state)
1002 {
1003         if(gl_state.scissortest == state)
1004                 return;
1005
1006         CHECKGLERROR
1007         if((gl_state.scissortest = state))
1008                 qglEnable(GL_SCISSOR_TEST);
1009         else
1010                 qglDisable(GL_SCISSOR_TEST);
1011         CHECKGLERROR
1012 }
1013
1014 void GL_Clear(int mask)
1015 {
1016         CHECKGLERROR
1017         qglClear(mask);CHECKGLERROR
1018 }
1019
1020 // called at beginning of frame
1021 void R_Mesh_Start(void)
1022 {
1023         BACKENDACTIVECHECK
1024         CHECKGLERROR
1025         if (gl_printcheckerror.integer && !gl_paranoid.integer)
1026         {
1027                 Con_Printf("WARNING: gl_printcheckerror is on but gl_paranoid is off, turning it on...\n");
1028                 Cvar_SetValueQuick(&gl_paranoid, 1);
1029         }
1030 }
1031
1032 qboolean GL_Backend_CompileShader(int programobject, GLenum shadertypeenum, const char *shadertype, int numstrings, const char **strings)
1033 {
1034         int shaderobject;
1035         int shadercompiled;
1036         char compilelog[MAX_INPUTLINE];
1037         shaderobject = qglCreateShaderObjectARB(shadertypeenum);CHECKGLERROR
1038         if (!shaderobject)
1039                 return false;
1040         qglShaderSourceARB(shaderobject, numstrings, strings, NULL);CHECKGLERROR
1041         qglCompileShaderARB(shaderobject);CHECKGLERROR
1042         qglGetObjectParameterivARB(shaderobject, GL_OBJECT_COMPILE_STATUS_ARB, &shadercompiled);CHECKGLERROR
1043         qglGetInfoLogARB(shaderobject, sizeof(compilelog), NULL, compilelog);CHECKGLERROR
1044         if (compilelog[0] && developer.integer > 0)
1045         {
1046                 int i, j, pretextlines = 0;
1047                 for (i = 0;i < numstrings - 1;i++)
1048                         for (j = 0;strings[i][j];j++)
1049                                 if (strings[i][j] == '\n')
1050                                         pretextlines++;
1051                 Con_DPrintf("%s shader compile log:\n%s\n(line offset for any above warnings/errors: %i)\n", shadertype, compilelog, pretextlines);
1052         }
1053         if (!shadercompiled)
1054         {
1055                 qglDeleteObjectARB(shaderobject);CHECKGLERROR
1056                 return false;
1057         }
1058         qglAttachObjectARB(programobject, shaderobject);CHECKGLERROR
1059         qglDeleteObjectARB(shaderobject);CHECKGLERROR
1060         return true;
1061 }
1062
1063 unsigned int GL_Backend_CompileProgram(int vertexstrings_count, const char **vertexstrings_list, int geometrystrings_count, const char **geometrystrings_list, int fragmentstrings_count, const char **fragmentstrings_list)
1064 {
1065         GLint programlinked;
1066         GLuint programobject = 0;
1067         char linklog[MAX_INPUTLINE];
1068         CHECKGLERROR
1069
1070         programobject = qglCreateProgramObjectARB();CHECKGLERROR
1071         if (!programobject)
1072                 return 0;
1073
1074         if (vertexstrings_count && !GL_Backend_CompileShader(programobject, GL_VERTEX_SHADER_ARB, "vertex", vertexstrings_count, vertexstrings_list))
1075                 goto cleanup;
1076
1077 #ifdef GL_GEOMETRY_SHADER_ARB
1078         if (geometrystrings_count && !GL_Backend_CompileShader(programobject, GL_GEOMETRY_SHADER_ARB, "geometry", geometrystrings_count, geometrystrings_list))
1079                 goto cleanup;
1080 #endif
1081
1082         if (fragmentstrings_count && !GL_Backend_CompileShader(programobject, GL_FRAGMENT_SHADER_ARB, "fragment", fragmentstrings_count, fragmentstrings_list))
1083                 goto cleanup;
1084
1085         qglLinkProgramARB(programobject);CHECKGLERROR
1086         qglGetObjectParameterivARB(programobject, GL_OBJECT_LINK_STATUS_ARB, &programlinked);CHECKGLERROR
1087         qglGetInfoLogARB(programobject, sizeof(linklog), NULL, linklog);CHECKGLERROR
1088         if (linklog[0])
1089         {
1090                 Con_DPrintf("program link log:\n%s\n", linklog);
1091                 // software vertex shader is ok but software fragment shader is WAY
1092                 // too slow, fail program if so.
1093                 // NOTE: this string might be ATI specific, but that's ok because the
1094                 // ATI R300 chip (Radeon 9500-9800/X300) is the most likely to use a
1095                 // software fragment shader due to low instruction and dependent
1096                 // texture limits.
1097                 if (strstr(linklog, "fragment shader will run in software"))
1098                         programlinked = false;
1099         }
1100         if (!programlinked)
1101                 goto cleanup;
1102         return programobject;
1103 cleanup:
1104         qglDeleteObjectARB(programobject);CHECKGLERROR
1105         return 0;
1106 }
1107
1108 void GL_Backend_FreeProgram(unsigned int prog)
1109 {
1110         CHECKGLERROR
1111         qglDeleteObjectARB(prog);
1112         CHECKGLERROR
1113 }
1114
1115 void GL_Backend_RenumberElements(int *out, int count, const int *in, int offset)
1116 {
1117         int i;
1118         if (offset)
1119         {
1120                 for (i = 0;i < count;i++)
1121                         *out++ = *in++ + offset;
1122         }
1123         else
1124                 memcpy(out, in, sizeof(*out) * count);
1125 }
1126
1127 // renders triangles using vertices from the active arrays
1128 int paranoidblah = 0;
1129 void R_Mesh_Draw(int firstvertex, int numvertices, int firsttriangle, int numtriangles, const int *element3i, const unsigned short *element3s, int bufferobject3i, int bufferobject3s)
1130 {
1131         unsigned int numelements = numtriangles * 3;
1132         if (numvertices < 3 || numtriangles < 1)
1133         {
1134                 if (numvertices < 0 || numtriangles < 0 || developer.integer >= 100)
1135                         Con_Printf("R_Mesh_Draw(%d, %d, %d, %d, %8p, %8p, %i, %i);\n", firstvertex, numvertices, firsttriangle, numtriangles, (void *)element3i, (void *)element3s, bufferobject3i, bufferobject3s);
1136                 return;
1137         }
1138         if (!gl_mesh_prefer_short_elements.integer)
1139         {
1140                 if (element3i)
1141                         element3s = NULL;
1142                 if (bufferobject3i)
1143                         bufferobject3s = 0;
1144         }
1145         if (element3i)
1146                 element3i += firsttriangle * 3;
1147         if (element3s)
1148                 element3s += firsttriangle * 3;
1149         switch (gl_vbo.integer)
1150         {
1151         default:
1152         case 0:
1153         case 2:
1154                 bufferobject3i = bufferobject3s = 0;
1155                 break;
1156         case 1:
1157                 break;
1158         case 3:
1159                 if (firsttriangle)
1160                         bufferobject3i = bufferobject3s = 0;
1161                 break;
1162         }
1163         CHECKGLERROR
1164         r_refdef.stats.meshes++;
1165         r_refdef.stats.meshes_elements += numelements;
1166         if (gl_paranoid.integer)
1167         {
1168                 unsigned int i, j, size;
1169                 const int *p;
1170                 // note: there's no validation done here on buffer objects because it
1171                 // is somewhat difficult to get at the data, and gl_paranoid can be
1172                 // used without buffer objects if the need arises
1173                 // (the data could be gotten using glMapBuffer but it would be very
1174                 //  slow due to uncachable video memory reads)
1175                 if (!qglIsEnabled(GL_VERTEX_ARRAY))
1176                         Con_Print("R_Mesh_Draw: vertex array not enabled\n");
1177                 CHECKGLERROR
1178                 if (gl_state.pointer_vertex)
1179                         for (j = 0, size = numvertices * 3, p = (int *)((float *)gl_state.pointer_vertex + firstvertex * 3);j < size;j++, p++)
1180                                 paranoidblah += *p;
1181                 if (gl_state.pointer_color_enabled)
1182                 {
1183                         if (!qglIsEnabled(GL_COLOR_ARRAY))
1184                                 Con_Print("R_Mesh_Draw: color array set but not enabled\n");
1185                         CHECKGLERROR
1186                         if (gl_state.pointer_color && gl_state.pointer_color_enabled)
1187                                 for (j = 0, size = numvertices * 4, p = (int *)((float *)gl_state.pointer_color + firstvertex * 4);j < size;j++, p++)
1188                                         paranoidblah += *p;
1189                 }
1190                 for (i = 0;i < vid.texarrayunits;i++)
1191                 {
1192                         if (gl_state.units[i].arrayenabled)
1193                         {
1194                                 GL_ClientActiveTexture(i);
1195                                 if (!qglIsEnabled(GL_TEXTURE_COORD_ARRAY))
1196                                         Con_Print("R_Mesh_Draw: texcoord array set but not enabled\n");
1197                                 CHECKGLERROR
1198                                 if (gl_state.units[i].pointer_texcoord && gl_state.units[i].arrayenabled)
1199                                         for (j = 0, size = numvertices * gl_state.units[i].arraycomponents, p = (int *)((float *)gl_state.units[i].pointer_texcoord + firstvertex * gl_state.units[i].arraycomponents);j < size;j++, p++)
1200                                                 paranoidblah += *p;
1201                         }
1202                 }
1203                 if (element3i)
1204                 {
1205                         for (i = 0;i < (unsigned int) numtriangles * 3;i++)
1206                         {
1207                                 if (element3i[i] < firstvertex || element3i[i] >= firstvertex + numvertices)
1208                                 {
1209                                         Con_Printf("R_Mesh_Draw: invalid vertex index %i (outside range %i - %i) in element3i array\n", element3i[i], firstvertex, firstvertex + numvertices);
1210                                         return;
1211                                 }
1212                         }
1213                 }
1214                 if (element3s)
1215                 {
1216                         for (i = 0;i < (unsigned int) numtriangles * 3;i++)
1217                         {
1218                                 if (element3s[i] < firstvertex || element3s[i] >= firstvertex + numvertices)
1219                                 {
1220                                         Con_Printf("R_Mesh_Draw: invalid vertex index %i (outside range %i - %i) in element3s array\n", element3s[i], firstvertex, firstvertex + numvertices);
1221                                         return;
1222                                 }
1223                         }
1224                 }
1225                 CHECKGLERROR
1226         }
1227         if (r_render.integer || r_refdef.draw2dstage)
1228         {
1229                 CHECKGLERROR
1230                 if (gl_mesh_testmanualfeeding.integer)
1231                 {
1232                         unsigned int i, j, element;
1233                         const GLfloat *p;
1234                         qglBegin(GL_TRIANGLES);
1235                         for (i = 0;i < (unsigned int) numtriangles * 3;i++)
1236                         {
1237                                 element = element3i ? element3i[i] : element3s[i];
1238                                 for (j = 0;j < vid.texarrayunits;j++)
1239                                 {
1240                                         if (gl_state.units[j].pointer_texcoord && gl_state.units[j].arrayenabled)
1241                                         {
1242                                                 if (vid.texarrayunits > 1)
1243                                                 {
1244                                                         if (gl_state.units[j].arraycomponents == 4)
1245                                                         {
1246                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 4;
1247                                                                 qglMultiTexCoord4f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2], p[3]);
1248                                                         }
1249                                                         else if (gl_state.units[j].arraycomponents == 3)
1250                                                         {
1251                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 3;
1252                                                                 qglMultiTexCoord3f(GL_TEXTURE0_ARB + j, p[0], p[1], p[2]);
1253                                                         }
1254                                                         else if (gl_state.units[j].arraycomponents == 2)
1255                                                         {
1256                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 2;
1257                                                                 qglMultiTexCoord2f(GL_TEXTURE0_ARB + j, p[0], p[1]);
1258                                                         }
1259                                                         else
1260                                                         {
1261                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 1;
1262                                                                 qglMultiTexCoord1f(GL_TEXTURE0_ARB + j, p[0]);
1263                                                         }
1264                                                 }
1265                                                 else
1266                                                 {
1267                                                         if (gl_state.units[j].arraycomponents == 4)
1268                                                         {
1269                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 4;
1270                                                                 qglTexCoord4f(p[0], p[1], p[2], p[3]);
1271                                                         }
1272                                                         else if (gl_state.units[j].arraycomponents == 3)
1273                                                         {
1274                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 3;
1275                                                                 qglTexCoord3f(p[0], p[1], p[2]);
1276                                                         }
1277                                                         else if (gl_state.units[j].arraycomponents == 2)
1278                                                         {
1279                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 2;
1280                                                                 qglTexCoord2f(p[0], p[1]);
1281                                                         }
1282                                                         else
1283                                                         {
1284                                                                 p = ((const GLfloat *)(gl_state.units[j].pointer_texcoord)) + element * 1;
1285                                                                 qglTexCoord1f(p[0]);
1286                                                         }
1287                                                 }
1288                                         }
1289                                 }
1290                                 if (gl_state.pointer_color && gl_state.pointer_color_enabled)
1291                                 {
1292                                         p = ((const GLfloat *)(gl_state.pointer_color)) + element * 4;
1293                                         qglColor4f(p[0], p[1], p[2], p[3]);
1294                                 }
1295                                 p = ((const GLfloat *)(gl_state.pointer_vertex)) + element * 3;
1296                                 qglVertex3f(p[0], p[1], p[2]);
1297                         }
1298                         qglEnd();
1299                         CHECKGLERROR
1300                 }
1301                 else if (gl_mesh_testarrayelement.integer)
1302                 {
1303                         int i;
1304                         qglBegin(GL_TRIANGLES);
1305                         if (element3i)
1306                         {
1307                                 for (i = 0;i < numtriangles * 3;i++)
1308                                         qglArrayElement(element3i[i]);
1309                         }
1310                         else if (element3s)
1311                         {
1312                                 for (i = 0;i < numtriangles * 3;i++)
1313                                         qglArrayElement(element3s[i]);
1314                         }
1315                         qglEnd();
1316                         CHECKGLERROR
1317                 }
1318                 else if (bufferobject3s)
1319                 {
1320                         GL_BindEBO(bufferobject3s);
1321                         if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
1322                         {
1323                                 qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_SHORT, (void *)(firsttriangle * sizeof(unsigned short[3])));
1324                                 CHECKGLERROR
1325                         }
1326                         else
1327                         {
1328                                 qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_SHORT, (void *)(firsttriangle * sizeof(unsigned short[3])));
1329                                 CHECKGLERROR
1330                         }
1331                 }
1332                 else if (bufferobject3i)
1333                 {
1334                         GL_BindEBO(bufferobject3i);
1335                         if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
1336                         {
1337                                 qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_INT, (void *)(firsttriangle * sizeof(unsigned int[3])));
1338                                 CHECKGLERROR
1339                         }
1340                         else
1341                         {
1342                                 qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_INT, (void *)(firsttriangle * sizeof(unsigned int[3])));
1343                                 CHECKGLERROR
1344                         }
1345                 }
1346                 else if (element3s)
1347                 {
1348                         GL_BindEBO(0);
1349                         if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
1350                         {
1351                                 qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_SHORT, element3s);
1352                                 CHECKGLERROR
1353                         }
1354                         else
1355                         {
1356                                 qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_SHORT, element3s);
1357                                 CHECKGLERROR
1358                         }
1359                 }
1360                 else if (element3i)
1361                 {
1362                         GL_BindEBO(0);
1363                         if (gl_mesh_drawrangeelements.integer && qglDrawRangeElements != NULL)
1364                         {
1365                                 qglDrawRangeElements(GL_TRIANGLES, firstvertex, firstvertex + numvertices - 1, numelements, GL_UNSIGNED_INT, element3i);
1366                                 CHECKGLERROR
1367                         }
1368                         else
1369                         {
1370                                 qglDrawElements(GL_TRIANGLES, numelements, GL_UNSIGNED_INT, element3i);
1371                                 CHECKGLERROR
1372                         }
1373                 }
1374         }
1375 }
1376
1377 // restores backend state, used when done with 3D rendering
1378 void R_Mesh_Finish(void)
1379 {
1380 }
1381
1382 int R_Mesh_CreateStaticBufferObject(unsigned int target, void *data, size_t size, const char *name)
1383 {
1384         gl_bufferobjectinfo_t *info;
1385         GLuint bufferobject;
1386
1387         if (!gl_vbo.integer)
1388                 return 0;
1389
1390         qglGenBuffersARB(1, &bufferobject);
1391         switch(target)
1392         {
1393         case GL_ELEMENT_ARRAY_BUFFER_ARB: GL_BindEBO(bufferobject);break;
1394         case GL_ARRAY_BUFFER_ARB: GL_BindVBO(bufferobject);break;
1395         default: Sys_Error("R_Mesh_CreateStaticBufferObject: unknown target type %i\n", target);return 0;
1396         }
1397         qglBufferDataARB(target, size, data, GL_STATIC_DRAW_ARB);
1398
1399         info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_AllocRecord(&gl_bufferobjectinfoarray);
1400         memset(info, 0, sizeof(*info));
1401         info->target = target;
1402         info->object = bufferobject;
1403         info->size = size;
1404         strlcpy(info->name, name, sizeof(info->name));
1405
1406         return (int)bufferobject;
1407 }
1408
1409 void R_Mesh_DestroyBufferObject(int bufferobject)
1410 {
1411         int i, endindex;
1412         gl_bufferobjectinfo_t *info;
1413
1414         qglDeleteBuffersARB(1, (GLuint *)&bufferobject);
1415
1416         endindex = Mem_ExpandableArray_IndexRange(&gl_bufferobjectinfoarray);
1417         for (i = 0;i < endindex;i++)
1418         {
1419                 info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_RecordAtIndex(&gl_bufferobjectinfoarray, i);
1420                 if (!info)
1421                         continue;
1422                 if (info->object == bufferobject)
1423                 {
1424                         Mem_ExpandableArray_FreeRecord(&gl_bufferobjectinfoarray, (void *)info);
1425                         break;
1426                 }
1427         }
1428 }
1429
1430 void GL_Mesh_ListVBOs(qboolean printeach)
1431 {
1432         int i, endindex;
1433         size_t ebocount = 0, ebomemory = 0;
1434         size_t vbocount = 0, vbomemory = 0;
1435         gl_bufferobjectinfo_t *info;
1436         endindex = Mem_ExpandableArray_IndexRange(&gl_bufferobjectinfoarray);
1437         for (i = 0;i < endindex;i++)
1438         {
1439                 info = (gl_bufferobjectinfo_t *) Mem_ExpandableArray_RecordAtIndex(&gl_bufferobjectinfoarray, i);
1440                 if (!info)
1441                         continue;
1442                 switch(info->target)
1443                 {
1444                 case GL_ELEMENT_ARRAY_BUFFER_ARB: ebocount++;ebomemory += info->size;if (printeach) Con_Printf("EBO #%i %s = %i bytes\n", info->object, info->name, (int)info->size);break;
1445                 case GL_ARRAY_BUFFER_ARB: vbocount++;vbomemory += info->size;if (printeach) Con_Printf("VBO #%i %s = %i bytes\n", info->object, info->name, (int)info->size);break;
1446                 default: Con_Printf("gl_vbostats: unknown target type %i\n", info->target);break;
1447                 }
1448         }
1449         Con_Printf("vertex buffers: %i element buffers totalling %i bytes (%.3f MB), %i vertex buffers totalling %i bytes (%.3f MB), combined %i bytes (%.3fMB)\n", (int)ebocount, (int)ebomemory, ebomemory / 1048576.0, (int)vbocount, (int)vbomemory, vbomemory / 1048576.0, (int)(ebomemory + vbomemory), (ebomemory + vbomemory) / 1048576.0);
1450 }
1451
1452 void R_Mesh_Matrix(const matrix4x4_t *matrix)
1453 {
1454         if (memcmp(matrix, &backend_modelmatrix, sizeof(matrix4x4_t)))
1455         {
1456                 float glmatrix[16];
1457                 backend_modelmatrix = *matrix;
1458                 Matrix4x4_Concat(&backend_modelviewmatrix, &backend_viewport.viewmatrix, &backend_modelmatrix);
1459                 Matrix4x4_ToArrayFloatGL(&backend_modelviewmatrix, glmatrix);
1460                 CHECKGLERROR
1461                 qglLoadMatrixf(glmatrix);CHECKGLERROR
1462         }
1463 }
1464
1465 void R_Mesh_VertexPointer(const float *vertex3f, int bufferobject, size_t bufferoffset)
1466 {
1467         if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
1468                 bufferobject = 0;
1469         if (gl_state.pointer_vertex != vertex3f || gl_state.pointer_vertex_buffer != bufferobject || gl_state.pointer_vertex_offset != bufferoffset)
1470         {
1471                 gl_state.pointer_vertex = vertex3f;
1472                 gl_state.pointer_vertex_buffer = bufferobject;
1473                 gl_state.pointer_vertex_offset = bufferoffset;
1474                 CHECKGLERROR
1475                 GL_BindVBO(bufferobject);
1476                 qglVertexPointer(3, GL_FLOAT, sizeof(float[3]), bufferobject ? (void *)bufferoffset : vertex3f);CHECKGLERROR
1477         }
1478 }
1479
1480 void R_Mesh_ColorPointer(const float *color4f, int bufferobject, size_t bufferoffset)
1481 {
1482         // note: this can not rely on bufferobject to decide whether a color array
1483         // is supplied, because surfmesh_t shares one vbo for all arrays, which
1484         // means that a valid vbo may be supplied even if there is no color array.
1485         if (color4f)
1486         {
1487                 if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
1488                         bufferobject = 0;
1489                 // caller wants color array enabled
1490                 if (!gl_state.pointer_color_enabled)
1491                 {
1492                         gl_state.pointer_color_enabled = true;
1493                         CHECKGLERROR
1494                         qglEnableClientState(GL_COLOR_ARRAY);CHECKGLERROR
1495                 }
1496                 if (gl_state.pointer_color != color4f || gl_state.pointer_color_buffer != bufferobject || gl_state.pointer_color_offset != bufferoffset)
1497                 {
1498                         gl_state.pointer_color = color4f;
1499                         gl_state.pointer_color_buffer = bufferobject;
1500                         gl_state.pointer_color_offset = bufferoffset;
1501                         CHECKGLERROR
1502                         GL_BindVBO(bufferobject);
1503                         qglColorPointer(4, GL_FLOAT, sizeof(float[4]), bufferobject ? (void *)bufferoffset : color4f);CHECKGLERROR
1504                 }
1505         }
1506         else
1507         {
1508                 // caller wants color array disabled
1509                 if (gl_state.pointer_color_enabled)
1510                 {
1511                         gl_state.pointer_color_enabled = false;
1512                         CHECKGLERROR
1513                         qglDisableClientState(GL_COLOR_ARRAY);CHECKGLERROR
1514                         // when color array is on the glColor gets trashed, set it again
1515                         qglColor4f(gl_state.color4f[0], gl_state.color4f[1], gl_state.color4f[2], gl_state.color4f[3]);CHECKGLERROR
1516                 }
1517         }
1518 }
1519
1520 void R_Mesh_TexCoordPointer(unsigned int unitnum, unsigned int numcomponents, const float *texcoord, int bufferobject, size_t bufferoffset)
1521 {
1522         gltextureunit_t *unit = gl_state.units + unitnum;
1523         // update array settings
1524         CHECKGLERROR
1525         // note: there is no need to check bufferobject here because all cases
1526         // that involve a valid bufferobject also supply a texcoord array
1527         if (texcoord)
1528         {
1529                 if (!gl_vbo.integer || gl_mesh_testarrayelement.integer)
1530                         bufferobject = 0;
1531                 // texture array unit is enabled, enable the array
1532                 if (!unit->arrayenabled)
1533                 {
1534                         unit->arrayenabled = true;
1535                         GL_ClientActiveTexture(unitnum);
1536                         qglEnableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
1537                 }
1538                 // texcoord array
1539                 if (unit->pointer_texcoord != texcoord || unit->pointer_texcoord_buffer != bufferobject || unit->pointer_texcoord_offset != bufferoffset || unit->arraycomponents != numcomponents)
1540                 {
1541                         unit->pointer_texcoord = texcoord;
1542                         unit->pointer_texcoord_buffer = bufferobject;
1543                         unit->pointer_texcoord_offset = bufferoffset;
1544                         unit->arraycomponents = numcomponents;
1545                         GL_ClientActiveTexture(unitnum);
1546                         GL_BindVBO(bufferobject);
1547                         qglTexCoordPointer(unit->arraycomponents, GL_FLOAT, sizeof(float) * unit->arraycomponents, bufferobject ? (void *)bufferoffset : texcoord);CHECKGLERROR
1548                 }
1549         }
1550         else
1551         {
1552                 // texture array unit is disabled, disable the array
1553                 if (unit->arrayenabled)
1554                 {
1555                         unit->arrayenabled = false;
1556                         GL_ClientActiveTexture(unitnum);
1557                         qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
1558                 }
1559         }
1560 }
1561
1562 void R_Mesh_TexBindAll(unsigned int unitnum, int tex2d, int tex3d, int texcubemap, int texrectangle)
1563 {
1564         gltextureunit_t *unit = gl_state.units + unitnum;
1565         if (unitnum >= vid.teximageunits)
1566                 return;
1567         // update 2d texture binding
1568         if (unit->t2d != tex2d)
1569         {
1570                 GL_ActiveTexture(unitnum);
1571                 if (unitnum < vid.texunits)
1572                 {
1573                         if (tex2d)
1574                         {
1575                                 if (unit->t2d == 0)
1576                                 {
1577                                         qglEnable(GL_TEXTURE_2D);CHECKGLERROR
1578                                 }
1579                         }
1580                         else
1581                         {
1582                                 if (unit->t2d)
1583                                 {
1584                                         qglDisable(GL_TEXTURE_2D);CHECKGLERROR
1585                                 }
1586                         }
1587                 }
1588                 unit->t2d = tex2d;
1589                 qglBindTexture(GL_TEXTURE_2D, unit->t2d);CHECKGLERROR
1590         }
1591         // update 3d texture binding
1592         if (unit->t3d != tex3d)
1593         {
1594                 GL_ActiveTexture(unitnum);
1595                 if (unitnum < vid.texunits)
1596                 {
1597                         if (tex3d)
1598                         {
1599                                 if (unit->t3d == 0)
1600                                 {
1601                                         qglEnable(GL_TEXTURE_3D);CHECKGLERROR
1602                                 }
1603                         }
1604                         else
1605                         {
1606                                 if (unit->t3d)
1607                                 {
1608                                         qglDisable(GL_TEXTURE_3D);CHECKGLERROR
1609                                 }
1610                         }
1611                 }
1612                 unit->t3d = tex3d;
1613                 qglBindTexture(GL_TEXTURE_3D, unit->t3d);CHECKGLERROR
1614         }
1615         // update cubemap texture binding
1616         if (unit->tcubemap != texcubemap)
1617         {
1618                 GL_ActiveTexture(unitnum);
1619                 if (unitnum < vid.texunits)
1620                 {
1621                         if (texcubemap)
1622                         {
1623                                 if (unit->tcubemap == 0)
1624                                 {
1625                                         qglEnable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1626                                 }
1627                         }
1628                         else
1629                         {
1630                                 if (unit->tcubemap)
1631                                 {
1632                                         qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1633                                 }
1634                         }
1635                 }
1636                 unit->tcubemap = texcubemap;
1637                 qglBindTexture(GL_TEXTURE_CUBE_MAP_ARB, unit->tcubemap);CHECKGLERROR
1638         }
1639         // update rectangle texture binding
1640         if (unit->trectangle != texrectangle)
1641         {
1642                 GL_ActiveTexture(unitnum);
1643                 if (unitnum < vid.texunits)
1644                 {
1645                         if (texrectangle)
1646                         {
1647                                 if (unit->trectangle == 0)
1648                                 {
1649                                         qglEnable(GL_TEXTURE_RECTANGLE_ARB);CHECKGLERROR
1650                                 }
1651                         }
1652                         else
1653                         {
1654                                 if (unit->trectangle)
1655                                 {
1656                                         qglDisable(GL_TEXTURE_RECTANGLE_ARB);CHECKGLERROR
1657                                 }
1658                         }
1659                 }
1660                 unit->trectangle = texrectangle;
1661                 qglBindTexture(GL_TEXTURE_RECTANGLE_ARB, unit->trectangle);CHECKGLERROR
1662         }
1663 }
1664
1665 void R_Mesh_TexBind(unsigned int unitnum, int texnum)
1666 {
1667         gltextureunit_t *unit = gl_state.units + unitnum;
1668         if (unitnum >= vid.teximageunits)
1669                 return;
1670         // update 2d texture binding
1671         if (unit->t2d != texnum)
1672         {
1673                 GL_ActiveTexture(unitnum);
1674                 if (unitnum < vid.texunits)
1675                 {
1676                         if (texnum)
1677                         {
1678                                 if (unit->t2d == 0)
1679                                 {
1680                                         qglEnable(GL_TEXTURE_2D);CHECKGLERROR
1681                                 }
1682                         }
1683                         else
1684                         {
1685                                 if (unit->t2d)
1686                                 {
1687                                         qglDisable(GL_TEXTURE_2D);CHECKGLERROR
1688                                 }
1689                         }
1690                 }
1691                 unit->t2d = texnum;
1692                 qglBindTexture(GL_TEXTURE_2D, unit->t2d);CHECKGLERROR
1693         }
1694         // update 3d texture binding
1695         if (unit->t3d)
1696         {
1697                 GL_ActiveTexture(unitnum);
1698                 if (unitnum < vid.texunits)
1699                 {
1700                         if (unit->t3d)
1701                         {
1702                                 qglDisable(GL_TEXTURE_3D);CHECKGLERROR
1703                         }
1704                 }
1705                 unit->t3d = 0;
1706                 qglBindTexture(GL_TEXTURE_3D, unit->t3d);CHECKGLERROR
1707         }
1708         // update cubemap texture binding
1709         if (unit->tcubemap != 0)
1710         {
1711                 GL_ActiveTexture(unitnum);
1712                 if (unitnum < vid.texunits)
1713                 {
1714                         if (unit->tcubemap)
1715                         {
1716                                 qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1717                         }
1718                 }
1719                 unit->tcubemap = 0;
1720                 qglBindTexture(GL_TEXTURE_CUBE_MAP_ARB, unit->tcubemap);CHECKGLERROR
1721         }
1722         // update rectangle texture binding
1723         if (unit->trectangle != 0)
1724         {
1725                 GL_ActiveTexture(unitnum);
1726                 if (unitnum < vid.texunits)
1727                 {
1728                         if (unit->trectangle)
1729                         {
1730                                 qglDisable(GL_TEXTURE_RECTANGLE_ARB);CHECKGLERROR
1731                         }
1732                 }
1733                 unit->trectangle = 0;
1734                 qglBindTexture(GL_TEXTURE_RECTANGLE_ARB, unit->trectangle);CHECKGLERROR
1735         }
1736 }
1737
1738 static const float gl_identitymatrix[16] = {1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1};
1739
1740 void R_Mesh_TexMatrix(unsigned int unitnum, const matrix4x4_t *matrix)
1741 {
1742         gltextureunit_t *unit = gl_state.units + unitnum;
1743         if (matrix->m[3][3])
1744         {
1745                 // texmatrix specified, check if it is different
1746                 if (!unit->texmatrixenabled || memcmp(&unit->matrix, matrix, sizeof(matrix4x4_t)))
1747                 {
1748                         float glmatrix[16];
1749                         unit->texmatrixenabled = true;
1750                         unit->matrix = *matrix;
1751                         CHECKGLERROR
1752                         Matrix4x4_ToArrayFloatGL(&unit->matrix, glmatrix);
1753                         GL_ActiveTexture(unitnum);
1754                         qglMatrixMode(GL_TEXTURE);CHECKGLERROR
1755                         qglLoadMatrixf(glmatrix);CHECKGLERROR
1756                         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
1757                 }
1758         }
1759         else
1760         {
1761                 // no texmatrix specified, revert to identity
1762                 if (unit->texmatrixenabled)
1763                 {
1764                         unit->texmatrixenabled = false;
1765                         unit->matrix = identitymatrix;
1766                         CHECKGLERROR
1767                         GL_ActiveTexture(unitnum);
1768                         qglMatrixMode(GL_TEXTURE);CHECKGLERROR
1769                         qglLoadIdentity();CHECKGLERROR
1770                         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
1771                 }
1772         }
1773 }
1774
1775 void R_Mesh_TexCombine(unsigned int unitnum, int combinergb, int combinealpha, int rgbscale, int alphascale)
1776 {
1777         gltextureunit_t *unit = gl_state.units + unitnum;
1778         CHECKGLERROR
1779         if (!combinergb)
1780                 combinergb = GL_MODULATE;
1781         if (!combinealpha)
1782                 combinealpha = GL_MODULATE;
1783         if (!rgbscale)
1784                 rgbscale = 1;
1785         if (!alphascale)
1786                 alphascale = 1;
1787         if (combinergb != combinealpha || rgbscale != 1 || alphascale != 1 || combinergb == GL_DOT3_RGBA_ARB || combinergb == GL_DOT3_RGB_ARB)
1788         {
1789                 if (combinergb == GL_DECAL)
1790                         combinergb = GL_INTERPOLATE_ARB;
1791                 if (unit->combine != GL_COMBINE_ARB)
1792                 {
1793                         unit->combine = GL_COMBINE_ARB;
1794                         GL_ActiveTexture(unitnum);
1795                         qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);CHECKGLERROR
1796                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);CHECKGLERROR // for GL_INTERPOLATE_ARB mode
1797                 }
1798                 if (unit->combinergb != combinergb)
1799                 {
1800                         unit->combinergb = combinergb;
1801                         GL_ActiveTexture(unitnum);
1802                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, unit->combinergb);CHECKGLERROR
1803                 }
1804                 if (unit->combinealpha != combinealpha)
1805                 {
1806                         unit->combinealpha = combinealpha;
1807                         GL_ActiveTexture(unitnum);
1808                         qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, unit->combinealpha);CHECKGLERROR
1809                 }
1810                 if (unit->rgbscale != rgbscale)
1811                 {
1812                         unit->rgbscale = rgbscale;
1813                         GL_ActiveTexture(unitnum);
1814                         qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, unit->rgbscale);CHECKGLERROR
1815                 }
1816                 if (unit->alphascale != alphascale)
1817                 {
1818                         unit->alphascale = alphascale;
1819                         GL_ActiveTexture(unitnum);
1820                         qglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, unit->alphascale);CHECKGLERROR
1821                 }
1822         }
1823         else
1824         {
1825                 if (unit->combine != combinergb)
1826                 {
1827                         unit->combine = combinergb;
1828                         GL_ActiveTexture(unitnum);
1829                         qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, unit->combine);CHECKGLERROR
1830                 }
1831         }
1832 }
1833
1834 void R_Mesh_TextureState(const rmeshstate_t *m)
1835 {
1836         unsigned int i;
1837
1838         BACKENDACTIVECHECK
1839
1840         CHECKGLERROR
1841         for (i = 0;i < vid.teximageunits;i++)
1842                 R_Mesh_TexBindAll(i, m->tex[i], m->tex3d[i], m->texcubemap[i], m->texrectangle[i]);
1843         for (i = 0;i < vid.texarrayunits;i++)
1844         {
1845                 if (m->pointer_texcoord3f[i])
1846                         R_Mesh_TexCoordPointer(i, 3, m->pointer_texcoord3f[i], m->pointer_texcoord_bufferobject[i], m->pointer_texcoord_bufferoffset[i]);
1847                 else
1848                         R_Mesh_TexCoordPointer(i, 2, m->pointer_texcoord[i], m->pointer_texcoord_bufferobject[i], m->pointer_texcoord_bufferoffset[i]);
1849         }
1850         for (i = 0;i < vid.texunits;i++)
1851         {
1852                 R_Mesh_TexMatrix(i, &m->texmatrix[i]);
1853                 R_Mesh_TexCombine(i, m->texcombinergb[i], m->texcombinealpha[i], m->texrgbscale[i], m->texalphascale[i]);
1854         }
1855         CHECKGLERROR
1856 }
1857
1858 void R_Mesh_ResetTextureState(void)
1859 {
1860         unsigned int unitnum;
1861
1862         BACKENDACTIVECHECK
1863
1864         CHECKGLERROR
1865         for (unitnum = 0;unitnum < vid.teximageunits;unitnum++)
1866         {
1867                 gltextureunit_t *unit = gl_state.units + unitnum;
1868                 // update 2d texture binding
1869                 if (unit->t2d)
1870                 {
1871                         GL_ActiveTexture(unitnum);
1872                         if (unitnum < vid.texunits)
1873                         {
1874                                 qglDisable(GL_TEXTURE_2D);CHECKGLERROR
1875                         }
1876                         unit->t2d = 0;
1877                         qglBindTexture(GL_TEXTURE_2D, unit->t2d);CHECKGLERROR
1878                 }
1879                 // update 3d texture binding
1880                 if (unit->t3d)
1881                 {
1882                         GL_ActiveTexture(unitnum);
1883                         if (unitnum < vid.texunits)
1884                         {
1885                                 qglDisable(GL_TEXTURE_3D);CHECKGLERROR
1886                         }
1887                         unit->t3d = 0;
1888                         qglBindTexture(GL_TEXTURE_3D, unit->t3d);CHECKGLERROR
1889                 }
1890                 // update cubemap texture binding
1891                 if (unit->tcubemap)
1892                 {
1893                         GL_ActiveTexture(unitnum);
1894                         if (unitnum < vid.texunits)
1895                         {
1896                                 qglDisable(GL_TEXTURE_CUBE_MAP_ARB);CHECKGLERROR
1897                         }
1898                         unit->tcubemap = 0;
1899                         qglBindTexture(GL_TEXTURE_CUBE_MAP_ARB, unit->tcubemap);CHECKGLERROR
1900                 }
1901                 // update rectangle texture binding
1902                 if (unit->trectangle)
1903                 {
1904                         GL_ActiveTexture(unitnum);
1905                         if (unitnum < vid.texunits)
1906                         {
1907                                 qglDisable(GL_TEXTURE_RECTANGLE_ARB);CHECKGLERROR
1908                         }
1909                         unit->trectangle = 0;
1910                         qglBindTexture(GL_TEXTURE_RECTANGLE_ARB, unit->trectangle);CHECKGLERROR
1911                 }
1912         }
1913         for (unitnum = 0;unitnum < vid.texarrayunits;unitnum++)
1914         {
1915                 gltextureunit_t *unit = gl_state.units + unitnum;
1916                 // texture array unit is disabled, disable the array
1917                 if (unit->arrayenabled)
1918                 {
1919                         unit->arrayenabled = false;
1920                         GL_ClientActiveTexture(unitnum);
1921                         qglDisableClientState(GL_TEXTURE_COORD_ARRAY);CHECKGLERROR
1922                 }
1923         }
1924         for (unitnum = 0;unitnum < vid.texunits;unitnum++)
1925         {
1926                 gltextureunit_t *unit = gl_state.units + unitnum;
1927                 // no texmatrix specified, revert to identity
1928                 if (unit->texmatrixenabled)
1929                 {
1930                         unit->texmatrixenabled = false;
1931                         unit->matrix = identitymatrix;
1932                         CHECKGLERROR
1933                         GL_ActiveTexture(unitnum);
1934                         qglMatrixMode(GL_TEXTURE);CHECKGLERROR
1935                         qglLoadIdentity();CHECKGLERROR
1936                         qglMatrixMode(GL_MODELVIEW);CHECKGLERROR
1937                 }
1938                 if (gl_combine.integer)
1939                 {
1940                         // GL_ARB_texture_env_combine
1941                         if (unit->combinergb != GL_MODULATE)
1942                         {
1943                                 unit->combinergb = GL_MODULATE;
1944                                 GL_ActiveTexture(unitnum);
1945                                 qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, unit->combinergb);CHECKGLERROR
1946                         }
1947                         if (unit->combinealpha != GL_MODULATE)
1948                         {
1949                                 unit->combinealpha = GL_MODULATE;
1950                                 GL_ActiveTexture(unitnum);
1951                                 qglTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, unit->combinealpha);CHECKGLERROR
1952                         }
1953                         if (unit->rgbscale != 1)
1954                         {
1955                                 GL_ActiveTexture(unitnum);
1956                                 qglTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, (unit->rgbscale = 1));CHECKGLERROR
1957                         }
1958                         if (unit->alphascale != 1)
1959                         {
1960                                 GL_ActiveTexture(unitnum);
1961                                 qglTexEnvi(GL_TEXTURE_ENV, GL_ALPHA_SCALE, (unit->alphascale = 1));CHECKGLERROR
1962                         }
1963                 }
1964                 else
1965                 {
1966                         // normal GL texenv
1967                         if (unit->combinergb != GL_MODULATE)
1968                         {
1969                                 unit->combinergb = GL_MODULATE;
1970                                 GL_ActiveTexture(unitnum);
1971                                 qglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, unit->combinergb);CHECKGLERROR
1972                         }
1973                 }
1974         }
1975 }