Added a test when freeing textures (glt->image can be NULL if the program exits becau...
[xonotic/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4
5 #define SHADOWSTAGE_NONE 0
6 #define SHADOWSTAGE_STENCIL 1
7 #define SHADOWSTAGE_LIGHT 2
8 #define SHADOWSTAGE_ERASESTENCIL 3
9
10 int r_shadowstage = SHADOWSTAGE_NONE;
11
12 mempool_t *r_shadow_mempool;
13
14 int maxshadowelements;
15 int *shadowelements;
16 int maxtrianglefacinglight;
17 qbyte *trianglefacinglight;
18
19 rtexturepool_t *r_shadow_texturepool;
20 rtexture_t *r_shadow_normalsattenuationtexture;
21 rtexture_t *r_shadow_normalscubetexture;
22 rtexture_t *r_shadow_attenuation2dtexture;
23 rtexture_t *r_shadow_blankbumptexture;
24
25 cvar_t r_shadow1 = {0, "r_shadow1", "2"};
26 cvar_t r_shadow2 = {0, "r_shadow2", "0"};
27 cvar_t r_shadow3 = {0, "r_shadow3", "32768"};
28 cvar_t r_shadow4 = {0, "r_shadow4", "0"};
29 cvar_t r_shadow5 = {0, "r_shadow5", "0"};
30 cvar_t r_shadow6 = {0, "r_shadow6", "0"};
31 cvar_t r_light_realtime = {0, "r_light_realtime", "0"};
32 cvar_t r_light_quality = {0, "r_light_quality", "1"};
33 cvar_t r_light_gloss = {0, "r_light_gloss", "0"};
34 cvar_t r_light_debuglight = {0, "r_light_debuglight", "-1"};
35
36 void r_shadow_start(void)
37 {
38         // allocate vertex processing arrays
39         r_shadow_mempool = Mem_AllocPool("R_Shadow");
40         maxshadowelements = 0;
41         shadowelements = NULL;
42         maxtrianglefacinglight = 0;
43         trianglefacinglight = NULL;
44         r_shadow_normalsattenuationtexture = NULL;
45         r_shadow_normalscubetexture = NULL;
46         r_shadow_attenuation2dtexture = NULL;
47         r_shadow_blankbumptexture = NULL;
48         r_shadow_texturepool = NULL;
49 }
50
51 void r_shadow_shutdown(void)
52 {
53         r_shadow_normalsattenuationtexture = NULL;
54         r_shadow_normalscubetexture = NULL;
55         r_shadow_attenuation2dtexture = NULL;
56         r_shadow_blankbumptexture = NULL;
57         R_FreeTexturePool(&r_shadow_texturepool);
58         maxshadowelements = 0;
59         shadowelements = NULL;
60         maxtrianglefacinglight = 0;
61         trianglefacinglight = NULL;
62         Mem_FreePool(&r_shadow_mempool);
63 }
64
65 void r_shadow_newmap(void)
66 {
67 }
68
69 void R_Shadow_Init(void)
70 {
71         Cvar_RegisterVariable(&r_shadow1);
72         Cvar_RegisterVariable(&r_shadow2);
73         Cvar_RegisterVariable(&r_shadow3);
74         Cvar_RegisterVariable(&r_shadow4);
75         Cvar_RegisterVariable(&r_shadow5);
76         Cvar_RegisterVariable(&r_shadow6);
77         Cvar_RegisterVariable(&r_light_realtime);
78         Cvar_RegisterVariable(&r_light_quality);
79         Cvar_RegisterVariable(&r_light_gloss);
80         Cvar_RegisterVariable(&r_light_debuglight);
81         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
82 }
83
84 void R_Shadow_Volume(int numverts, int numtris, float *vertex, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
85 {
86         int i, *e, *n, *out, tris;
87         float *v0, *v1, *v2, temp[3], f;
88         if (projectdistance < 0.1)
89         {
90                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
91                 return;
92         }
93 // terminology:
94 //
95 // frontface:
96 // a triangle facing the light source
97 //
98 // backface:
99 // a triangle not facing the light source
100 //
101 // shadow volume:
102 // an extrusion of the backfaces, beginning at the original geometry and
103 // ending further from the light source than the original geometry
104 // (presumably at least as far as the light's radius, if the light has a
105 // radius at all), capped at both front and back to avoid any problems
106 //
107 // description:
108 // draws the shadow volumes of the model.
109 // requirements:
110 // vertex loations must already be in vertex before use.
111 // vertex must have capacity for numverts * 2.
112
113         // make sure trianglefacinglight is big enough for this volume
114         if (maxtrianglefacinglight < numtris)
115         {
116                 maxtrianglefacinglight = numtris;
117                 if (trianglefacinglight)
118                         Mem_Free(trianglefacinglight);
119                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
120         }
121
122         // make sure shadowelements is big enough for this volume
123         if (maxshadowelements < numtris * 24)
124         {
125                 maxshadowelements = numtris * 24;
126                 if (shadowelements)
127                         Mem_Free(shadowelements);
128                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
129         }
130
131         // make projected vertices
132         // by clever use of elements we'll construct the whole shadow from
133         // the unprojected vertices and these projected vertices
134         for (i = 0, v0 = vertex, v1 = vertex + numverts * 4;i < numverts;i++, v0 += 4, v1 += 4)
135         {
136 #if 1
137                 v1[0] = v0[0] + 50.0f * (v0[0] - relativelightorigin[0]);
138                 v1[1] = v0[1] + 50.0f * (v0[1] - relativelightorigin[1]);
139                 v1[2] = v0[2] + 50.0f * (v0[2] - relativelightorigin[2]);
140 #elif 0
141                 VectorSubtract(v0, relativelightorigin, temp);
142                 f = lightradius / sqrt(DotProduct(temp,temp));
143                 if (f < 1)
144                         f = 1;
145                 VectorMA(relativelightorigin, f, temp, v1);
146 #else
147                 VectorSubtract(v0, relativelightorigin, temp);
148                 f = projectdistance / sqrt(DotProduct(temp,temp));
149                 VectorMA(v0, f, temp, v1);
150 #endif
151         }
152
153         // check which triangles are facing the light
154         for (i = 0, e = elements;i < numtris;i++, e += 3)
155         {
156                 // calculate triangle facing flag
157                 v0 = vertex + e[0] * 4;
158                 v1 = vertex + e[1] * 4;
159                 v2 = vertex + e[2] * 4;
160                 // we do not need to normalize the surface normal because both sides
161                 // of the comparison use it, therefore they are both multiplied the
162                 // same amount...  furthermore the subtract can be done on the
163                 // vectors, saving a little bit of math in the dotproducts
164 #if 0
165                 // fast version
166                 // subtracts v1 from v0 and v2, combined into a crossproduct,
167                 // combined with a dotproduct of the light location relative to the
168                 // first point of the triangle (any point works, since the triangle
169                 // is obviously flat), and finally a comparison to determine if the
170                 // light is infront of the triangle (the goal of this statement)
171                 trianglefacinglight[i] =
172                    (relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
173                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
174                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0;
175 #else
176                 // readable version
177                 {
178                 float dir0[3], dir1[3];
179
180                 // calculate two mostly perpendicular edge directions
181                 VectorSubtract(v0, v1, dir0);
182                 VectorSubtract(v2, v1, dir1);
183
184                 // we have two edge directions, we can calculate a third vector from
185                 // them, which is the direction of the surface normal (it's magnitude
186                 // is not 1 however)
187                 CrossProduct(dir0, dir1, temp);
188
189                 // this is entirely unnecessary, but kept for clarity
190                 //VectorNormalize(temp);
191
192                 // compare distance of light along normal, with distance of any point
193                 // of the triangle along the same normal (the triangle is planar,
194                 // I.E. flat, so all points give the same answer)
195                 // the normal is not normalized because it is used on both sides of
196                 // the comparison, so it's magnitude does not matter
197                 //trianglefacinglight[i] = DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp);
198                 f = DotProduct(relativelightorigin, temp) - DotProduct(v0, temp);
199                 trianglefacinglight[i] = f > 0 && f < lightradius * sqrt(DotProduct(temp, temp));
200                 }
201 #endif
202         }
203
204         // output triangle elements
205         out = shadowelements;
206         tris = 0;
207
208         // check each backface for bordering frontfaces,
209         // and cast shadow polygons from those edges,
210         // also create front and back caps for shadow volume
211         for (i = 0, e = elements, n = neighbors;i < numtris;i++, e += 3, n += 3)
212         {
213                 if (!trianglefacinglight[i])
214                 {
215                         // triangle is backface and therefore casts shadow,
216                         // output front and back caps for shadow volume
217 #if 1
218                         // front cap (with flipped winding order)
219                         out[0] = e[0];
220                         out[1] = e[2];
221                         out[2] = e[1];
222                         // rear cap
223                         out[3] = e[0] + numverts;
224                         out[4] = e[1] + numverts;
225                         out[5] = e[2] + numverts;
226                         out += 6;
227                         tris += 2;
228 #elif 1
229                         // rear cap
230                         out[0] = e[0] + numverts;
231                         out[1] = e[1] + numverts;
232                         out[2] = e[2] + numverts;
233                         out += 3;
234                         tris += 1;
235 #endif
236                         // check the edges
237                         if (n[0] < 0 || trianglefacinglight[n[0]])
238                         {
239                                 out[0] = e[0];
240                                 out[1] = e[1];
241                                 out[2] = e[1] + numverts;
242                                 out[3] = e[0];
243                                 out[4] = e[1] + numverts;
244                                 out[5] = e[0] + numverts;
245                                 out += 6;
246                                 tris += 2;
247                         }
248                         if (n[1] < 0 || trianglefacinglight[n[1]])
249                         {
250                                 out[0] = e[1];
251                                 out[1] = e[2];
252                                 out[2] = e[2] + numverts;
253                                 out[3] = e[1];
254                                 out[4] = e[2] + numverts;
255                                 out[5] = e[1] + numverts;
256                                 out += 6;
257                                 tris += 2;
258                         }
259                         if (n[2] < 0 || trianglefacinglight[n[2]])
260                         {
261                                 out[0] = e[2];
262                                 out[1] = e[0];
263                                 out[2] = e[0] + numverts;
264                                 out[3] = e[2];
265                                 out[4] = e[0] + numverts;
266                                 out[5] = e[2] + numverts;
267                                 out += 6;
268                                 tris += 2;
269                         }
270                 }
271         }
272         R_Shadow_RenderVolume(numverts * 2, tris, shadowelements);
273 }
274
275 void R_Shadow_RenderVolume(int numverts, int numtris, int *elements)
276 {
277         if (!numverts || !numtris)
278                 return;
279         // draw the volume
280         if (r_shadowstage == SHADOWSTAGE_STENCIL)
281         {
282                 // increment stencil if backface is behind depthbuffer
283                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
284                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
285                 R_Mesh_Draw(numverts, numtris, elements);
286                 // decrement stencil if frontface is behind depthbuffer
287                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
288                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
289                 R_Mesh_Draw(numverts, numtris, elements);
290         }
291         else
292                 R_Mesh_Draw(numverts, numtris, elements);
293 }
294
295 float r_shadow_atten1, r_shadow_atten2, r_shadow_atten5;
296 #define ATTEN3DSIZE 64
297 static void R_Shadow_Make3DTextures(void)
298 {
299         int x, y, z;
300         float v[3], intensity, ilen, bordercolor[4];
301         qbyte *data;
302         if (r_light_quality.integer != 1 || !gl_texture3d)
303                 return;
304         data = Mem_Alloc(tempmempool, ATTEN3DSIZE * ATTEN3DSIZE * ATTEN3DSIZE * 4);
305         for (z = 0;z < ATTEN3DSIZE;z++)
306         {
307                 for (y = 0;y < ATTEN3DSIZE;y++)
308                 {
309                         for (x = 0;x < ATTEN3DSIZE;x++)
310                         {
311                                 v[0] = (x + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
312                                 v[1] = (y + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
313                                 v[2] = (z + 0.5f) * (2.0f / (float) ATTEN3DSIZE) - 1.0f;
314                                 intensity = 1.0f - sqrt(DotProduct(v, v));
315                                 if (intensity > 0)
316                                         intensity *= intensity;
317                                 ilen = 127.0f * bound(0, intensity * r_shadow_atten1, 1) / sqrt(DotProduct(v, v));
318                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = 128.0f + ilen * v[0];
319                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = 128.0f + ilen * v[1];
320                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = 128.0f + ilen * v[2];
321                                 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = 255;
322                         }
323                 }
324         }
325         r_shadow_normalsattenuationtexture = R_LoadTexture3D(r_shadow_texturepool, "normalsattenuation", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALWAYSPRECACHE, NULL);
326         bordercolor[0] = 0.5f;
327         bordercolor[1] = 0.5f;
328         bordercolor[2] = 0.5f;
329         bordercolor[3] = 1.0f;
330         qglTexParameterfv(GL_TEXTURE_3D, GL_TEXTURE_BORDER_COLOR, bordercolor);
331         Mem_Free(data);
332 }
333
334 static void R_Shadow_MakeTextures(void)
335 {
336         int x, y, d, side;
337         float v[3], s, t, intensity;
338         qbyte *data;
339         data = Mem_Alloc(tempmempool, 6*128*128*4);
340         R_FreeTexturePool(&r_shadow_texturepool);
341         r_shadow_texturepool = R_AllocTexturePool();
342         r_shadow_atten1 = r_shadow1.value;
343         r_shadow_atten2 = r_shadow2.value;
344         r_shadow_atten5 = r_shadow5.value;
345         for (y = 0;y < 128;y++)
346         {
347                 for (x = 0;x < 128;x++)
348                 {
349                         data[((0*128+y)*128+x)*4+0] = 128;
350                         data[((0*128+y)*128+x)*4+1] = 128;
351                         data[((0*128+y)*128+x)*4+2] = 255;
352                         data[((0*128+y)*128+x)*4+3] = 255;
353                 }
354         }
355         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
356         for (side = 0;side < 6;side++)
357         {
358                 for (y = 0;y < 128;y++)
359                 {
360                         for (x = 0;x < 128;x++)
361                         {
362                                 s = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
363                                 t = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
364                                 switch(side)
365                                 {
366                                 case 0:
367                                         v[0] = 1;
368                                         v[1] = -t;
369                                         v[2] = -s;
370                                         break;
371                                 case 1:
372                                         v[0] = -1;
373                                         v[1] = -t;
374                                         v[2] = s;
375                                         break;
376                                 case 2:
377                                         v[0] = s;
378                                         v[1] = 1;
379                                         v[2] = t;
380                                         break;
381                                 case 3:
382                                         v[0] = s;
383                                         v[1] = -1;
384                                         v[2] = -t;
385                                         break;
386                                 case 4:
387                                         v[0] = s;
388                                         v[1] = -t;
389                                         v[2] = 1;
390                                         break;
391                                 case 5:
392                                         v[0] = -s;
393                                         v[1] = -t;
394                                         v[2] = -1;
395                                         break;
396                                 }
397                                 intensity = 127.0f / sqrt(DotProduct(v, v));
398                                 data[((side*128+y)*128+x)*4+0] = 128.0f + intensity * v[0];
399                                 data[((side*128+y)*128+x)*4+1] = 128.0f + intensity * v[1];
400                                 data[((side*128+y)*128+x)*4+2] = 128.0f + intensity * v[2];
401                                 data[((side*128+y)*128+x)*4+3] = 255;
402                         }
403                 }
404         }
405         r_shadow_normalscubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalscube", 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
406         for (y = 0;y < 128;y++)
407         {
408                 for (x = 0;x < 128;x++)
409                 {
410                         v[0] = (x + 0.5f) * (2.0f / 128.0f) - 1.0f;
411                         v[1] = (y + 0.5f) * (2.0f / 128.0f) - 1.0f;
412                         v[2] = 0;
413                         intensity = 1.0f - sqrt(DotProduct(v, v));
414                         if (intensity > 0)
415                                 intensity *= intensity;
416                         intensity = bound(0, intensity * r_shadow_atten1 * 256.0f, 255.0f);
417                         d = bound(0, intensity, 255);
418                         data[((0*128+y)*128+x)*4+0] = d;
419                         data[((0*128+y)*128+x)*4+1] = d;
420                         data[((0*128+y)*128+x)*4+2] = d;
421                         data[((0*128+y)*128+x)*4+3] = d;
422                 }
423         }
424         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", 128, 128, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA | TEXF_MIPMAP, NULL);
425         Mem_Free(data);
426         R_Shadow_Make3DTextures();
427 }
428
429 void R_Shadow_Stage_Begin(void)
430 {
431         rmeshstate_t m;
432
433         if (r_light_quality.integer == 1 && !gl_texture3d)
434         {
435                 Con_Printf("3D texture support not detected, falling back on slower 2D + 1D + normalization lighting\n");
436                 Cvar_SetValueQuick(&r_light_quality, 0);
437         }
438         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
439         if (!r_shadow_attenuation2dtexture
440          || (r_light_quality.integer == 1 && !r_shadow_normalsattenuationtexture)
441          || r_shadow1.value != r_shadow_atten1
442          || r_shadow2.value != r_shadow_atten2
443          || r_shadow5.value != r_shadow_atten5)
444                 R_Shadow_MakeTextures();
445
446         memset(&m, 0, sizeof(m));
447         m.blendfunc1 = GL_ONE;
448         m.blendfunc2 = GL_ZERO;
449         R_Mesh_State(&m);
450         GL_Color(0, 0, 0, 1);
451         r_shadowstage = SHADOWSTAGE_NONE;
452 }
453
454 void R_Shadow_Stage_ShadowVolumes(void)
455 {
456         rmeshstate_t m;
457         memset(&m, 0, sizeof(m));
458         R_Mesh_TextureState(&m);
459         GL_Color(1, 1, 1, 1);
460         qglColorMask(0, 0, 0, 0);
461         qglDisable(GL_BLEND);
462         qglDepthMask(0);
463         qglDepthFunc(GL_LESS);
464         qglClearStencil(0);
465         qglClear(GL_STENCIL_BUFFER_BIT);
466         qglEnable(GL_STENCIL_TEST);
467         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
468         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
469         qglEnable(GL_CULL_FACE);
470         qglEnable(GL_DEPTH_TEST);
471         r_shadowstage = SHADOWSTAGE_STENCIL;
472 }
473
474 void R_Shadow_Stage_Light(void)
475 {
476         rmeshstate_t m;
477         memset(&m, 0, sizeof(m));
478         R_Mesh_TextureState(&m);
479         qglActiveTexture(GL_TEXTURE0_ARB);
480
481         qglEnable(GL_BLEND);
482         qglBlendFunc(GL_ONE, GL_ONE);
483         GL_Color(1, 1, 1, 1);
484         qglColorMask(1, 1, 1, 1);
485         qglDepthMask(0);
486         qglDepthFunc(GL_EQUAL);
487         qglEnable(GL_STENCIL_TEST);
488         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
489         // only draw light where this geometry was already rendered AND the
490         // stencil is 0 (non-zero means shadow)
491         qglStencilFunc(GL_EQUAL, 0, 0xFF);
492         qglEnable(GL_CULL_FACE);
493         qglEnable(GL_DEPTH_TEST);
494         r_shadowstage = SHADOWSTAGE_LIGHT;
495 }
496
497 void R_Shadow_Stage_EraseShadowVolumes(void)
498 {
499         rmeshstate_t m;
500         memset(&m, 0, sizeof(m));
501         R_Mesh_TextureState(&m);
502         GL_Color(1, 1, 1, 1);
503         qglColorMask(0, 0, 0, 0);
504         qglDisable(GL_BLEND);
505         qglDepthMask(0);
506         qglDepthFunc(GL_LESS);
507         qglClearStencil(0);
508         qglClear(GL_STENCIL_BUFFER_BIT);
509         qglEnable(GL_STENCIL_TEST);
510         qglStencilOp(GL_ZERO, GL_KEEP, GL_KEEP);
511         qglStencilFunc(GL_NOTEQUAL, 0, 0xFF);
512         qglDisable(GL_CULL_FACE);
513         qglDisable(GL_DEPTH_TEST);
514         r_shadowstage = SHADOWSTAGE_ERASESTENCIL;
515 }
516
517 void R_Shadow_Stage_End(void)
518 {
519         // attempt to restore state to what Mesh_State thinks it is
520         qglDisable(GL_BLEND);
521         qglBlendFunc(GL_ONE, GL_ZERO);
522         qglDepthMask(1);
523         // now restore the rest of the state to normal
524         GL_Color(1, 1, 1, 1);
525         qglColorMask(1, 1, 1, 1);
526         qglDepthFunc(GL_LEQUAL);
527         qglDisable(GL_STENCIL_TEST);
528         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
529         qglStencilFunc(GL_ALWAYS, 0, 0xFF);
530         qglEnable(GL_CULL_FACE);
531         qglEnable(GL_DEPTH_TEST);
532         r_shadowstage = SHADOWSTAGE_NONE;
533 }
534
535 void R_Shadow_GenTexCoords_Attenuation2D1D(float *out2d, float *out1d, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
536 {
537         int i;
538         float lightvec[3], iradius;
539         iradius = 0.5f / lightradius;
540         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out2d += 4, out1d += 4)
541         {
542                 VectorSubtract(vertex, relativelightorigin, lightvec);
543                 out2d[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
544                 out2d[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
545                 out2d[2] = 0;
546                 out1d[0] = 0.5f + DotProduct(normals, lightvec) * iradius;
547                 out1d[1] = 0.5f;
548                 out1d[2] = 0;
549         }
550 }
551
552 void R_Shadow_GenTexCoords_Diffuse_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, float lightradius)
553 {
554         int i;
555         float lightvec[3], iradius;
556         iradius = 0.5f / lightradius;
557         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
558         {
559                 VectorSubtract(vertex, relativelightorigin, lightvec);
560                 out[0] = 0.5f + DotProduct(svectors, lightvec) * iradius;
561                 out[1] = 0.5f + DotProduct(tvectors, lightvec) * iradius;
562                 out[2] = 0.5f + DotProduct(normals, lightvec) * iradius;
563         }
564 }
565
566 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin)
567 {
568         int i;
569         float lightdir[3];
570         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
571         {
572                 VectorSubtract(vertex, relativelightorigin, lightdir);
573                 // the cubemap normalizes this for us
574                 out[0] = DotProduct(svectors, lightdir);
575                 out[1] = DotProduct(tvectors, lightdir);
576                 out[2] = DotProduct(normals, lightdir);
577         }
578 }
579
580 void R_Shadow_GenTexCoords_Specular_Attenuation3D(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin, float lightradius)
581 {
582         int i;
583         float lightdir[3], eyedir[3], halfdir[3], lightdirlen, iradius;
584         iradius = 0.5f / lightradius;
585         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
586         {
587                 VectorSubtract(vertex, relativelightorigin, lightdir);
588                 // this is used later to make the attenuation correct
589                 lightdirlen = sqrt(DotProduct(lightdir, lightdir)) * iradius;
590                 VectorNormalizeFast(lightdir);
591                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
592                 VectorNormalizeFast(eyedir);
593                 VectorAdd(lightdir, eyedir, halfdir);
594                 VectorNormalizeFast(halfdir);
595                 out[0] = 0.5f + DotProduct(svectors, halfdir) * lightdirlen;
596                 out[1] = 0.5f + DotProduct(tvectors, halfdir) * lightdirlen;
597                 out[2] = 0.5f + DotProduct(normals, halfdir) * lightdirlen;
598         }
599 }
600
601 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out, int numverts, const float *vertex, const float *svectors, const float *tvectors, const float *normals, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
602 {
603         int i;
604         float lightdir[3], eyedir[3], halfdir[3];
605         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
606         {
607                 VectorSubtract(vertex, relativelightorigin, lightdir);
608                 VectorNormalizeFast(lightdir);
609                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
610                 VectorNormalizeFast(eyedir);
611                 VectorAdd(lightdir, eyedir, halfdir);
612                 // the cubemap normalizes this for us
613                 out[0] = DotProduct(svectors, halfdir);
614                 out[1] = DotProduct(tvectors, halfdir);
615                 out[2] = DotProduct(normals, halfdir);
616         }
617 }
618
619 void R_Shadow_GenTexCoords_LightCubeMap(float *out, int numverts, const float *vertex, const vec3_t relativelightorigin)
620 {
621         int i;
622         // FIXME: this needs to be written
623         // this code assumes the vertices are in worldspace (a false assumption)
624         for (i = 0;i < numverts;i++, vertex += 4, out += 4)
625                 VectorSubtract(vertex, relativelightorigin, out);
626 }
627
628 void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, rtexture_t *basetexture, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
629 {
630         int mult;
631         float scale, colorscale;
632         rmeshstate_t m;
633         memset(&m, 0, sizeof(m));
634         if (!bumptexture)
635                 bumptexture = r_shadow_blankbumptexture;
636         // colorscale accounts for how much we multiply the brightness during combine
637         if (r_light_quality.integer == 1)
638         {
639                 if (r_textureunits.integer >= 4)
640                         colorscale = r_colorscale * 0.125f / r_shadow3.value;
641                 else
642                         colorscale = r_colorscale * 0.5f / r_shadow3.value;
643         }
644         else
645                 colorscale = r_colorscale * 0.5f / r_shadow3.value;
646         // limit mult to 64 for sanity sake
647         for (mult = 1, scale = ixtable[mult];mult < 64 && (lightcolor[0] * scale * colorscale > 1 || lightcolor[1] * scale * colorscale > 1 || lightcolor[2] * scale * colorscale > 1);mult++, scale = ixtable[mult]);
648         colorscale *= scale;
649         for (;mult > 0;mult--)
650         {
651                 if (r_light_quality.integer == 1)
652                 {
653                         if (r_textureunits.integer >= 4)
654                         {
655                                 // 4 texture 3D path, two pass
656                                 m.tex[0] = R_GetTexture(bumptexture);
657                                 m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
658                                 m.tex[2] = R_GetTexture(basetexture);
659                                 m.texcubemap[3] = R_GetTexture(lightcubemap);
660                                 m.tex[3] = R_GetTexture(r_notexture);
661                                 m.texcombinergb[0] = GL_REPLACE;
662                                 m.texcombinergb[1] = GL_DOT3_RGB_ARB;
663                                 m.texcombinergb[2] = GL_MODULATE;
664                                 m.texcombinergb[3] = GL_MODULATE;
665                                 m.texrgbscale[1] = 2;
666                                 m.texrgbscale[3] = 4;
667                                 R_Mesh_TextureState(&m);
668                                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
669                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
670                                 memcpy(varray_texcoord[2], texcoords, numverts * sizeof(float[4]));
671                                 if (lightcubemap)
672                                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[3], numverts, varray_vertex, relativelightorigin);
673                                 else
674                                 {
675                                         qglActiveTexture(GL_TEXTURE3_ARB);
676                                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
677                                 }
678                                 R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
679                                 R_Mesh_Draw(numverts, numtriangles, elements);
680                                 if (!lightcubemap)
681                                 {
682                                         qglActiveTexture(GL_TEXTURE3_ARB);
683                                         qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
684                                 }
685                                 if (r_light_gloss.integer && glosstexture)
686                                 {
687                                         m.tex[2] = R_GetTexture(glosstexture);
688                                         R_Mesh_TextureState(&m);
689                                         R_Shadow_GenTexCoords_Specular_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin, lightradius);
690                                         if (!lightcubemap)
691                                         {
692                                                 qglActiveTexture(GL_TEXTURE3_ARB);
693                                                 qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PRIMARY_COLOR_ARB);
694                                         }
695                                         R_Mesh_Draw(numverts, numtriangles, elements);
696                                         if (!lightcubemap)
697                                         {
698                                                 qglActiveTexture(GL_TEXTURE3_ARB);
699                                                 qglTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
700                                         }
701                                 }
702                         }
703                         else
704                         {
705                                 // 2 texture 3D path, four pass
706                                 m.tex[0] = R_GetTexture(bumptexture);
707                                 m.tex3d[1] = R_GetTexture(r_shadow_normalsattenuationtexture);
708                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
709                                 m.texalphascale[1] = 2;
710                                 R_Mesh_TextureState(&m);
711                                 qglColorMask(0,0,0,1);
712                                 qglDisable(GL_BLEND);
713                                 GL_Color(1,1,1,1);
714                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
715                                 R_Shadow_GenTexCoords_Diffuse_Attenuation3D(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
716                                 R_Mesh_Draw(numverts, numtriangles, elements);
717
718                                 m.tex[0] = R_GetTexture(basetexture);
719                                 m.tex3d[1] = 0;
720                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
721                                 m.texcombinergb[1] = GL_MODULATE;
722                                 m.texrgbscale[1] = 1;
723                                 m.texalphascale[1] = 1;
724                                 R_Mesh_TextureState(&m);
725                                 qglColorMask(1,1,1,1);
726                                 qglBlendFunc(GL_DST_ALPHA, GL_ONE);
727                                 qglEnable(GL_BLEND);
728                                 GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
729                                 if (lightcubemap)
730                                         R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
731                                 R_Mesh_Draw(numverts, numtriangles, elements);
732                         }
733                 }
734                 else
735                 {
736                         // 2 texture no3D path, six pass
737                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
738                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
739                         R_Mesh_TextureState(&m);
740                         qglColorMask(0,0,0,1);
741                         qglDisable(GL_BLEND);
742                         GL_Color(1,1,1,1);
743                         R_Shadow_GenTexCoords_Attenuation2D1D(varray_texcoord[0], varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin, lightradius);
744                         R_Mesh_Draw(numverts, numtriangles, elements);
745
746                         m.tex[0] = R_GetTexture(bumptexture);
747                         m.tex[1] = 0;
748                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
749                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
750                         m.texalphascale[1] = 2;
751                         R_Mesh_TextureState(&m);
752                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
753                         qglEnable(GL_BLEND);
754                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
755                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, varray_vertex, svectors, tvectors, normals, relativelightorigin);
756                         R_Mesh_Draw(numverts, numtriangles, elements);
757
758                         m.tex[0] = R_GetTexture(basetexture);
759                         m.texcubemap[1] = R_GetTexture(lightcubemap);
760                         m.texcombinergb[1] = GL_MODULATE;
761                         m.texrgbscale[1] = 1;
762                         m.texalphascale[1] = 1;
763                         R_Mesh_TextureState(&m);
764                         qglColorMask(1,1,1,1);
765                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
766                         GL_Color(lightcolor[0] * colorscale, lightcolor[1] * colorscale, lightcolor[2] * colorscale, 1);
767                         if (lightcubemap)
768                                 R_Shadow_GenTexCoords_LightCubeMap(varray_texcoord[1], numverts, varray_vertex, relativelightorigin);
769                         R_Mesh_Draw(numverts, numtriangles, elements);
770                 }
771         }
772 }
773