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