50de89788c7513570ea2222a6efcc91cd5fd72e0
[xonotic/darkplaces.git] / r_shadow.c
1
2 #include "quakedef.h"
3 #include "r_shadow.h"
4 #include "cl_collision.h"
5 #include "portals.h"
6
7 extern void R_Shadow_EditLights_Init(void);
8
9 #define SHADOWSTAGE_NONE 0
10 #define SHADOWSTAGE_STENCIL 1
11 #define SHADOWSTAGE_LIGHT 2
12 #define SHADOWSTAGE_ERASESTENCIL 3
13
14 int r_shadowstage = SHADOWSTAGE_NONE;
15 int r_shadow_reloadlights = false;
16
17 int r_shadow_lightingmode = 0;
18
19 mempool_t *r_shadow_mempool;
20
21 int maxshadowelements;
22 int *shadowelements;
23 int maxtrianglefacinglight;
24 qbyte *trianglefacinglight;
25 int *trianglefacinglightlist;
26
27 int maxshadowvertices;
28 float *shadowvertices;
29
30 rtexturepool_t *r_shadow_texturepool;
31 rtexture_t *r_shadow_normalscubetexture;
32 rtexture_t *r_shadow_attenuation2dtexture;
33 rtexture_t *r_shadow_attenuation3dtexture;
34 rtexture_t *r_shadow_blankbumptexture;
35 rtexture_t *r_shadow_blankglosstexture;
36 rtexture_t *r_shadow_blankwhitetexture;
37
38 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
39 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
40 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
41 cvar_t r_shadow_realtime = {0, "r_shadow_realtime", "0"};
42 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
43 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
44 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
45 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
46 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
47 cvar_t r_shadow_shadownudge = {0, "r_shadow_shadownudge", "1"};
48 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
49 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "100000"};
50 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
51
52 int c_rt_lights, c_rt_clears, c_rt_scissored;
53 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
54 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
55
56 void R_Shadow_ClearWorldLights(void);
57 void R_Shadow_SaveWorldLights(void);
58 void R_Shadow_LoadWorldLights(void);
59 void R_Shadow_LoadLightsFile(void);
60 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
61
62 void r_shadow_start(void)
63 {
64         // allocate vertex processing arrays
65         r_shadow_mempool = Mem_AllocPool("R_Shadow");
66         maxshadowelements = 0;
67         shadowelements = NULL;
68         maxshadowvertices = 0;
69         shadowvertices = NULL;
70         maxtrianglefacinglight = 0;
71         trianglefacinglight = NULL;
72         trianglefacinglightlist = NULL;
73         r_shadow_normalscubetexture = NULL;
74         r_shadow_attenuation2dtexture = NULL;
75         r_shadow_attenuation3dtexture = NULL;
76         r_shadow_blankbumptexture = NULL;
77         r_shadow_blankglosstexture = NULL;
78         r_shadow_blankwhitetexture = NULL;
79         r_shadow_texturepool = NULL;
80         R_Shadow_ClearWorldLights();
81         r_shadow_reloadlights = true;
82 }
83
84 void r_shadow_shutdown(void)
85 {
86         R_Shadow_ClearWorldLights();
87         r_shadow_reloadlights = true;
88         r_shadow_normalscubetexture = NULL;
89         r_shadow_attenuation2dtexture = NULL;
90         r_shadow_attenuation3dtexture = NULL;
91         r_shadow_blankbumptexture = NULL;
92         r_shadow_blankglosstexture = NULL;
93         r_shadow_blankwhitetexture = NULL;
94         R_FreeTexturePool(&r_shadow_texturepool);
95         maxshadowelements = 0;
96         shadowelements = NULL;
97         maxshadowvertices = 0;
98         shadowvertices = NULL;
99         maxtrianglefacinglight = 0;
100         trianglefacinglight = NULL;
101         trianglefacinglightlist = NULL;
102         Mem_FreePool(&r_shadow_mempool);
103 }
104
105 void r_shadow_newmap(void)
106 {
107         R_Shadow_ClearWorldLights();
108         r_shadow_reloadlights = true;
109 }
110
111 void R_Shadow_Init(void)
112 {
113         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
114         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
115         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
116         Cvar_RegisterVariable(&r_shadow_realtime);
117         Cvar_RegisterVariable(&r_shadow_gloss);
118         Cvar_RegisterVariable(&r_shadow_debuglight);
119         Cvar_RegisterVariable(&r_shadow_scissor);
120         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
121         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
122         Cvar_RegisterVariable(&r_shadow_shadownudge);
123         Cvar_RegisterVariable(&r_shadow_portallight);
124         Cvar_RegisterVariable(&r_shadow_projectdistance);
125         Cvar_RegisterVariable(&r_shadow_texture3d);
126         R_Shadow_EditLights_Init();
127         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
128 }
129
130 void R_Shadow_ProjectVertices(float *verts, int numverts, const float *relativelightorigin, float projectdistance)
131 {
132         int i;
133         float *in, *out, diff[4];
134         in = verts;
135         out = verts + numverts * 4;
136         for (i = 0;i < numverts;i++, in += 4, out += 4)
137         {
138                 VectorSubtract(in, relativelightorigin, diff);
139                 VectorNormalizeFast(diff);
140                 VectorMA(in, projectdistance, diff, out);
141                 VectorMA(in, r_shadow_shadownudge.value, diff, in);
142         }
143 }
144
145 int R_Shadow_MakeTriangleShadowFlags(const int *elements, const float *vertex, int numtris, qbyte *facing, int *list, const float *relativelightorigin)
146 {
147         int i, tris = 0;
148         const float *v0, *v1, *v2;
149         for (i = 0;i < numtris;i++, elements += 3)
150         {
151                 // calculate triangle facing flag
152                 v0 = vertex + elements[0] * 4;
153                 v1 = vertex + elements[1] * 4;
154                 v2 = vertex + elements[2] * 4;
155                 // we do not need to normalize the surface normal because both sides
156                 // of the comparison use it, therefore they are both multiplied the
157                 // same amount...  furthermore the subtract can be done on the
158                 // vectors, saving a little bit of math in the dotproducts
159 #if 1
160                 // fast version
161                 // subtracts v1 from v0 and v2, combined into a crossproduct,
162                 // combined with a dotproduct of the light location relative to the
163                 // first point of the triangle (any point works, since any triangle
164                 // is obviously flat), and finally a comparison to determine if the
165                 // light is infront of the triangle (the goal of this statement)
166                 if((relativelightorigin[0] - v0[0]) * ((v0[1] - v1[1]) * (v2[2] - v1[2]) - (v0[2] - v1[2]) * (v2[1] - v1[1]))
167                  + (relativelightorigin[1] - v0[1]) * ((v0[2] - v1[2]) * (v2[0] - v1[0]) - (v0[0] - v1[0]) * (v2[2] - v1[2]))
168                  + (relativelightorigin[2] - v0[2]) * ((v0[0] - v1[0]) * (v2[1] - v1[1]) - (v0[1] - v1[1]) * (v2[0] - v1[0])) > 0)
169                 {
170                         facing[i] = true;
171                         list[tris++] = i;
172                 }
173                 else
174                         facing[i] = false;
175 #else
176                 // readable version
177                 {
178                 float dir0[3], dir1[3], temp[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                 if (DotProduct(relativelightorigin, temp) >= DotProduct(v0, temp))
198                 {
199                         facing[i] = true;
200                         list[tris++] = i;
201                 }
202                 else
203                         facing[i] = false;
204                 }
205 #endif
206         }
207         return tris;
208 }
209
210 int R_Shadow_BuildShadowVolumeTriangles(const int *elements, const int *neighbors, int numverts, const qbyte *facing, const int *facinglist, int numfacing, int *out)
211 {
212         int i, tris;
213         const int *e, *n;
214         // check each frontface for bordering backfaces,
215         // and cast shadow polygons from those edges,
216         // also create front and back caps for shadow volume
217         tris = numfacing * 2;
218         // output front caps
219         for (i = 0;i < numfacing;i++)
220         {
221                 e = elements + facinglist[i] * 3;
222                 out[0] = e[0];
223                 out[1] = e[1];
224                 out[2] = e[2];
225                 out += 3;
226         }
227         // output back caps
228         for (i = 0;i < numfacing;i++)
229         {
230                 e = elements + facinglist[i] * 3;
231                 out[0] = e[2] + numverts;
232                 out[1] = e[1] + numverts;
233                 out[2] = e[0] + numverts;
234                 out += 3;
235         }
236         // output sides around frontfaces
237         for (i = 0;i < numfacing;i++)
238         {
239                 n = neighbors + facinglist[i] * 3;
240                 // check the edges
241                 if (n[0] < 0 || !facing[n[0]])
242                 {
243                         e = elements + facinglist[i] * 3;
244                         out[0] = e[1];
245                         out[1] = e[0];
246                         out[2] = e[0] + numverts;
247                         out[3] = e[1];
248                         out[4] = e[0] + numverts;
249                         out[5] = e[1] + numverts;
250                         out += 6;
251                         tris += 2;
252                 }
253                 if (n[1] < 0 || !facing[n[1]])
254                 {
255                         e = elements + facinglist[i] * 3;
256                         out[0] = e[2];
257                         out[1] = e[1];
258                         out[2] = e[1] + numverts;
259                         out[3] = e[2];
260                         out[4] = e[1] + numverts;
261                         out[5] = e[2] + numverts;
262                         out += 6;
263                         tris += 2;
264                 }
265                 if (n[2] < 0 || !facing[n[2]])
266                 {
267                         e = elements + facinglist[i] * 3;
268                         out[0] = e[0];
269                         out[1] = e[2];
270                         out[2] = e[2] + numverts;
271                         out[3] = e[0];
272                         out[4] = e[2] + numverts;
273                         out[5] = e[0] + numverts;
274                         out += 6;
275                         tris += 2;
276                 }
277         }
278         return tris;
279 }
280
281 void R_Shadow_ResizeTriangleFacingLight(int numtris)
282 {
283         // make sure trianglefacinglight is big enough for this volume
284         // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
285         // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
286         if (maxtrianglefacinglight < numtris)
287         {
288                 maxtrianglefacinglight = numtris;
289                 if (trianglefacinglight)
290                         Mem_Free(trianglefacinglight);
291                 if (trianglefacinglightlist)
292                         Mem_Free(trianglefacinglightlist);
293                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
294                 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
295         }
296 }
297
298 int *R_Shadow_ResizeShadowElements(int numtris)
299 {
300         // make sure shadowelements is big enough for this volume
301         if (maxshadowelements < numtris * 24)
302         {
303                 maxshadowelements = numtris * 24;
304                 if (shadowelements)
305                         Mem_Free(shadowelements);
306                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
307         }
308         return shadowelements;
309 }
310
311 float *R_Shadow_VertexBuffer(int numvertices)
312 {
313         if (maxshadowvertices < numvertices)
314         {
315                 maxshadowvertices = numvertices;
316                 if (shadowvertices)
317                         Mem_Free(shadowvertices);
318                 shadowvertices = Mem_Alloc(r_shadow_mempool, maxshadowvertices * sizeof(float[4]));
319         }
320         return shadowvertices;
321 }
322
323 void R_Shadow_Volume(int numverts, int numtris, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
324 {
325         int tris;
326         if (projectdistance < 0.1)
327         {
328                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
329                 return;
330         }
331         if (!numverts)
332                 return;
333 // terminology:
334 //
335 // frontface:
336 // a triangle facing the light source
337 //
338 // backface:
339 // a triangle not facing the light source
340 //
341 // shadow volume:
342 // an extrusion of the frontfaces, beginning at the original geometry and
343 // ending further from the light source than the original geometry
344 // (presumably at least as far as the light's radius, if the light has a
345 // radius at all), capped at both front and back to avoid any problems
346 //
347 // description:
348 // draws the shadow volumes of the model.
349 // requirements:
350 // vertex locations must already be in vertices before use.
351 // vertices must have capacity for numverts * 2.
352
353         // make sure trianglefacinglight is big enough for this volume
354         if (maxtrianglefacinglight < numtris)
355                 R_Shadow_ResizeTriangleFacingLight(numtris);
356
357         // make sure shadowelements is big enough for this volume
358         if (maxshadowelements < numtris * 24)
359                 R_Shadow_ResizeShadowElements(numtris);
360
361         // check which triangles are facing the light
362         tris = R_Shadow_MakeTriangleShadowFlags(elements, varray_vertex, numtris, trianglefacinglight, trianglefacinglightlist, relativelightorigin);
363         if (!tris)
364                 return;
365
366         // output triangle elements
367         tris = R_Shadow_BuildShadowVolumeTriangles(elements, neighbors, numverts, trianglefacinglight, trianglefacinglightlist, tris, shadowelements);
368         if (!tris)
369                 return;
370
371         // by clever use of elements we can construct the whole shadow from
372         // the unprojected vertices and the projected vertices
373         R_Shadow_ProjectVertices(varray_vertex, numverts, relativelightorigin, projectdistance);
374
375         if (r_shadowstage == SHADOWSTAGE_STENCIL)
376         {
377                 // increment stencil if backface is behind depthbuffer
378                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
379                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
380                 R_Mesh_Draw(numverts * 2, tris, shadowelements);
381                 c_rt_shadowmeshes++;
382                 c_rt_shadowtris += numtris;
383                 // decrement stencil if frontface is behind depthbuffer
384                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
385                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
386         }
387         R_Mesh_Draw(numverts * 2, tris, shadowelements);
388         c_rt_shadowmeshes++;
389         c_rt_shadowtris += numtris;
390 }
391
392 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
393 {
394         shadowmesh_t *mesh;
395         if (r_shadowstage == SHADOWSTAGE_STENCIL)
396         {
397                 // increment stencil if backface is behind depthbuffer
398                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
399                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
400                 for (mesh = firstmesh;mesh;mesh = mesh->next)
401                 {
402                         R_Mesh_GetSpace(mesh->numverts);
403                         memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
404                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
405                         c_rtcached_shadowmeshes++;
406                         c_rtcached_shadowtris += mesh->numtriangles;
407                 }
408                 // decrement stencil if frontface is behind depthbuffer
409                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
410                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
411         }
412         for (mesh = firstmesh;mesh;mesh = mesh->next)
413         {
414                 R_Mesh_GetSpace(mesh->numverts);
415                 memcpy(varray_vertex, mesh->verts, mesh->numverts * sizeof(float[4]));
416                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->elements);
417                 c_rtcached_shadowmeshes++;
418                 c_rtcached_shadowtris += mesh->numtriangles;
419         }
420 }
421
422 float r_shadow_attenpower, r_shadow_attenscale;
423 static void R_Shadow_MakeTextures(void)
424 {
425         int x, y, z, d, side;
426         float v[3], s, t, intensity;
427         qbyte *data;
428         R_FreeTexturePool(&r_shadow_texturepool);
429         r_shadow_texturepool = R_AllocTexturePool();
430         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
431         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
432 #define NORMSIZE 64
433 #define ATTEN2DSIZE 64
434 #define ATTEN3DSIZE 32
435         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
436         data[0] = 128;
437         data[1] = 128;
438         data[2] = 255;
439         data[3] = 255;
440         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
441         data[0] = 255;
442         data[1] = 255;
443         data[2] = 255;
444         data[3] = 255;
445         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
446         data[0] = 255;
447         data[1] = 255;
448         data[2] = 255;
449         data[3] = 255;
450         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
451         for (side = 0;side < 6;side++)
452         {
453                 for (y = 0;y < NORMSIZE;y++)
454                 {
455                         for (x = 0;x < NORMSIZE;x++)
456                         {
457                                 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
458                                 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
459                                 switch(side)
460                                 {
461                                 case 0:
462                                         v[0] = 1;
463                                         v[1] = -t;
464                                         v[2] = -s;
465                                         break;
466                                 case 1:
467                                         v[0] = -1;
468                                         v[1] = -t;
469                                         v[2] = s;
470                                         break;
471                                 case 2:
472                                         v[0] = s;
473                                         v[1] = 1;
474                                         v[2] = t;
475                                         break;
476                                 case 3:
477                                         v[0] = s;
478                                         v[1] = -1;
479                                         v[2] = -t;
480                                         break;
481                                 case 4:
482                                         v[0] = s;
483                                         v[1] = -t;
484                                         v[2] = 1;
485                                         break;
486                                 case 5:
487                                         v[0] = -s;
488                                         v[1] = -t;
489                                         v[2] = -1;
490                                         break;
491                                 }
492                                 intensity = 127.0f / sqrt(DotProduct(v, v));
493                                 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
494                                 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
495                                 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
496                                 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
497                         }
498                 }
499         }
500         r_shadow_normalscubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalscube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
501         for (y = 0;y < ATTEN2DSIZE;y++)
502         {
503                 for (x = 0;x < ATTEN2DSIZE;x++)
504                 {
505                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
506                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
507                         v[2] = 0;
508                         intensity = 1.0f - sqrt(DotProduct(v, v));
509                         if (intensity > 0)
510                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
511                         d = bound(0, intensity, 255);
512                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
513                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
514                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
515                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
516                 }
517         }
518         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
519         if (r_shadow_texture3d.integer)
520         {
521                 for (z = 0;z < ATTEN3DSIZE;z++)
522                 {
523                         for (y = 0;y < ATTEN3DSIZE;y++)
524                         {
525                                 for (x = 0;x < ATTEN3DSIZE;x++)
526                                 {
527                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
528                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
529                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
530                                         intensity = 1.0f - sqrt(DotProduct(v, v));
531                                         if (intensity > 0)
532                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
533                                         d = bound(0, intensity, 255);
534                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
535                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
536                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
537                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
538                                 }
539                         }
540                 }
541                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
542         }
543         Mem_Free(data);
544 }
545
546 void R_Shadow_Stage_Begin(void)
547 {
548         rmeshstate_t m;
549
550         if (r_shadow_texture3d.integer && !gl_texture3d)
551                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
552
553         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
554         if (!r_shadow_attenuation2dtexture
555          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
556          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
557          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
558                 R_Shadow_MakeTextures();
559         if (r_shadow_reloadlights && cl.worldmodel)
560         {
561                 R_Shadow_ClearWorldLights();
562                 r_shadow_reloadlights = false;
563                 R_Shadow_LoadWorldLights();
564                 if (r_shadow_worldlightchain == NULL)
565                 {
566                         R_Shadow_LoadLightsFile();
567                         if (r_shadow_worldlightchain == NULL)
568                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
569                 }
570         }
571
572         memset(&m, 0, sizeof(m));
573         m.blendfunc1 = GL_ONE;
574         m.blendfunc2 = GL_ZERO;
575         R_Mesh_State(&m);
576         GL_Color(0, 0, 0, 1);
577         r_shadowstage = SHADOWSTAGE_NONE;
578
579         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
580         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
581         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
582 }
583
584 void R_Shadow_Stage_ShadowVolumes(void)
585 {
586         rmeshstate_t m;
587         memset(&m, 0, sizeof(m));
588         R_Mesh_TextureState(&m);
589         GL_Color(1, 1, 1, 1);
590         qglColorMask(0, 0, 0, 0);
591         qglDisable(GL_BLEND);
592         qglDepthMask(0);
593         qglDepthFunc(GL_LESS);
594         qglEnable(GL_STENCIL_TEST);
595         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
596         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
597         qglEnable(GL_CULL_FACE);
598         qglEnable(GL_DEPTH_TEST);
599         r_shadowstage = SHADOWSTAGE_STENCIL;
600         qglClear(GL_STENCIL_BUFFER_BIT);
601         c_rt_clears++;
602         // LordHavoc note: many shadow volumes reside entirely inside the world
603         // (that is to say they are entirely bounded by their lit surfaces),
604         // which can be optimized by handling things as an inverted light volume,
605         // with the shadow boundaries of the world being simulated by an altered
606         // (129) bias to stencil clearing on such lights
607         // FIXME: generate inverted light volumes for use as shadow volumes and
608         // optimize for them as noted above
609 }
610
611 void R_Shadow_Stage_LightWithoutShadows(void)
612 {
613         rmeshstate_t m;
614         memset(&m, 0, sizeof(m));
615         R_Mesh_TextureState(&m);
616         qglActiveTexture(GL_TEXTURE0_ARB);
617
618         qglEnable(GL_BLEND);
619         qglBlendFunc(GL_ONE, GL_ONE);
620         GL_Color(1, 1, 1, 1);
621         qglColorMask(1, 1, 1, 1);
622         qglDepthMask(0);
623         qglDepthFunc(GL_EQUAL);
624         qglDisable(GL_STENCIL_TEST);
625         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
626         qglStencilFunc(GL_EQUAL, 128, 0xFF);
627         qglEnable(GL_CULL_FACE);
628         qglEnable(GL_DEPTH_TEST);
629         r_shadowstage = SHADOWSTAGE_LIGHT;
630         c_rt_lights++;
631 }
632
633 void R_Shadow_Stage_LightWithShadows(void)
634 {
635         rmeshstate_t m;
636         memset(&m, 0, sizeof(m));
637         R_Mesh_TextureState(&m);
638         qglActiveTexture(GL_TEXTURE0_ARB);
639
640         qglEnable(GL_BLEND);
641         qglBlendFunc(GL_ONE, GL_ONE);
642         GL_Color(1, 1, 1, 1);
643         qglColorMask(1, 1, 1, 1);
644         qglDepthMask(0);
645         qglDepthFunc(GL_EQUAL);
646         qglEnable(GL_STENCIL_TEST);
647         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
648         // only draw light where this geometry was already rendered AND the
649         // stencil is 128 (values other than this mean shadow)
650         qglStencilFunc(GL_EQUAL, 128, 0xFF);
651         qglEnable(GL_CULL_FACE);
652         qglEnable(GL_DEPTH_TEST);
653         r_shadowstage = SHADOWSTAGE_LIGHT;
654         c_rt_lights++;
655 }
656
657 void R_Shadow_Stage_End(void)
658 {
659         rmeshstate_t m;
660         // attempt to restore state to what Mesh_State thinks it is
661         qglDisable(GL_BLEND);
662         qglBlendFunc(GL_ONE, GL_ZERO);
663         qglDepthMask(1);
664         // now restore the rest of the state to normal
665         GL_Color(1, 1, 1, 1);
666         qglColorMask(1, 1, 1, 1);
667         qglDisable(GL_SCISSOR_TEST);
668         qglDepthFunc(GL_LEQUAL);
669         qglDisable(GL_STENCIL_TEST);
670         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
671         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
672         qglEnable(GL_CULL_FACE);
673         qglEnable(GL_DEPTH_TEST);
674         // force mesh state to reset by using various combinations of features
675         memset(&m, 0, sizeof(m));
676         m.blendfunc1 = GL_SRC_ALPHA;
677         m.blendfunc2 = GL_ONE_MINUS_SRC_ALPHA;
678         R_Mesh_State(&m);
679         m.blendfunc1 = GL_ONE;
680         m.blendfunc2 = GL_ZERO;
681         R_Mesh_State(&m);
682         r_shadowstage = SHADOWSTAGE_NONE;
683 }
684
685 #if 0
686 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
687 {
688         int i, ix1, iy1, ix2, iy2;
689         float x1, y1, x2, y2, x, y;
690         vec3_t smins, smaxs;
691         vec4_t v, v2;
692         if (!r_shadow_scissor.integer)
693                 return false;
694         // if view is inside the box, just say yes it's visible
695         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
696          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
697          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
698         {
699                 qglDisable(GL_SCISSOR_TEST);
700                 return false;
701         }
702         VectorSubtract(r_origin, origin, v);
703         if (DotProduct(v, v) < radius * radius)
704         {
705                 qglDisable(GL_SCISSOR_TEST);
706                 return false;
707         }
708         // create viewspace bbox
709         for (i = 0;i < 8;i++)
710         {
711                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
712                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
713                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
714                 v2[0] = DotProduct(v, vright);
715                 v2[1] = DotProduct(v, vup);
716                 v2[2] = DotProduct(v, vpn);
717                 if (i)
718                 {
719                         if (smins[0] > v2[0]) smins[0] = v2[0];
720                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
721                         if (smins[1] > v2[1]) smins[1] = v2[1];
722                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
723                         if (smins[2] > v2[2]) smins[2] = v2[2];
724                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
725                 }
726                 else
727                 {
728                         smins[0] = smaxs[0] = v2[0];
729                         smins[1] = smaxs[1] = v2[1];
730                         smins[2] = smaxs[2] = v2[2];
731                 }
732         }
733         // now we have a bbox in viewspace
734         // clip it to the viewspace version of the sphere
735         v[0] = origin[0] - r_origin[0];
736         v[1] = origin[1] - r_origin[1];
737         v[2] = origin[2] - r_origin[2];
738         v2[0] = DotProduct(v, vright);
739         v2[1] = DotProduct(v, vup);
740         v2[2] = DotProduct(v, vpn);
741         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
742         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
743         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
744         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
745         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
746         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
747         // clip it to the view plane
748         if (smins[2] < 1)
749                 smins[2] = 1;
750         // return true if that culled the box
751         if (smins[2] >= smaxs[2])
752                 return true;
753         // ok some of it is infront of the view, transform each corner back to
754         // worldspace and then to screenspace and make screen rect
755         // initialize these variables just to avoid compiler warnings
756         x1 = y1 = x2 = y2 = 0;
757         for (i = 0;i < 8;i++)
758         {
759                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
760                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
761                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
762                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
763                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
764                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
765                 v[3] = 1.0f;
766                 GL_TransformToScreen(v, v2);
767                 //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]);
768                 x = v2[0];
769                 y = v2[1];
770                 if (i)
771                 {
772                         if (x1 > x) x1 = x;
773                         if (x2 < x) x2 = x;
774                         if (y1 > y) y1 = y;
775                         if (y2 < y) y2 = y;
776                 }
777                 else
778                 {
779                         x1 = x2 = x;
780                         y1 = y2 = y;
781                 }
782         }
783         /*
784         // this code doesn't handle boxes with any points behind view properly
785         x1 = 1000;x2 = -1000;
786         y1 = 1000;y2 = -1000;
787         for (i = 0;i < 8;i++)
788         {
789                 v[0] = (i & 1) ? mins[0] : maxs[0];
790                 v[1] = (i & 2) ? mins[1] : maxs[1];
791                 v[2] = (i & 4) ? mins[2] : maxs[2];
792                 v[3] = 1.0f;
793                 GL_TransformToScreen(v, v2);
794                 //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]);
795                 if (v2[2] > 0)
796                 {
797                         x = v2[0];
798                         y = v2[1];
799
800                         if (x1 > x) x1 = x;
801                         if (x2 < x) x2 = x;
802                         if (y1 > y) y1 = y;
803                         if (y2 < y) y2 = y;
804                 }
805         }
806         */
807         ix1 = x1 - 1.0f;
808         iy1 = y1 - 1.0f;
809         ix2 = x2 + 1.0f;
810         iy2 = y2 + 1.0f;
811         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
812         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
813         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
814         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
815         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
816         if (ix2 <= ix1 || iy2 <= iy1)
817                 return true;
818         // set up the scissor rectangle
819         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
820         qglEnable(GL_SCISSOR_TEST);
821         c_rt_scissored++;
822         return false;
823 }
824 #endif
825
826 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
827 {
828         int i, ix1, iy1, ix2, iy2;
829         float x1, y1, x2, y2, x, y, f;
830         vec3_t smins, smaxs;
831         vec4_t v, v2;
832         if (!r_shadow_scissor.integer)
833                 return false;
834         // if view is inside the box, just say yes it's visible
835         if (BoxesOverlap(r_origin, r_origin, mins, maxs))
836         {
837                 qglDisable(GL_SCISSOR_TEST);
838                 return false;
839         }
840         for (i = 0;i < 3;i++)
841         {
842                 if (vpn[i] >= 0)
843                 {
844                         v[i] = mins[i];
845                         v2[i] = maxs[i];
846                 }
847                 else
848                 {
849                         v[i] = maxs[i];
850                         v2[i] = mins[i];
851                 }
852         }
853         f = DotProduct(vpn, r_origin) + 1;
854         if (DotProduct(vpn, v2) <= f)
855         {
856                 // entirely behind nearclip plane
857                 qglDisable(GL_SCISSOR_TEST);
858                 return false;
859         }
860         if (DotProduct(vpn, v) >= f)
861         {
862                 // entirely infront of nearclip plane
863                 x1 = y1 = x2 = y2 = 0;
864                 for (i = 0;i < 8;i++)
865                 {
866                         v[0] = (i & 1) ? mins[0] : maxs[0];
867                         v[1] = (i & 2) ? mins[1] : maxs[1];
868                         v[2] = (i & 4) ? mins[2] : maxs[2];
869                         v[3] = 1.0f;
870                         GL_TransformToScreen(v, v2);
871                         //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]);
872                         x = v2[0];
873                         y = v2[1];
874                         if (i)
875                         {
876                                 if (x1 > x) x1 = x;
877                                 if (x2 < x) x2 = x;
878                                 if (y1 > y) y1 = y;
879                                 if (y2 < y) y2 = y;
880                         }
881                         else
882                         {
883                                 x1 = x2 = x;
884                                 y1 = y2 = y;
885                         }
886                 }
887         }
888         else
889         {
890                 // clipped by nearclip plane
891                 // this is nasty and crude...
892                 // create viewspace bbox
893                 for (i = 0;i < 8;i++)
894                 {
895                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
896                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
897                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
898                         v2[0] = DotProduct(v, vright);
899                         v2[1] = DotProduct(v, vup);
900                         v2[2] = DotProduct(v, vpn);
901                         if (i)
902                         {
903                                 if (smins[0] > v2[0]) smins[0] = v2[0];
904                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
905                                 if (smins[1] > v2[1]) smins[1] = v2[1];
906                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
907                                 if (smins[2] > v2[2]) smins[2] = v2[2];
908                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
909                         }
910                         else
911                         {
912                                 smins[0] = smaxs[0] = v2[0];
913                                 smins[1] = smaxs[1] = v2[1];
914                                 smins[2] = smaxs[2] = v2[2];
915                         }
916                 }
917                 // now we have a bbox in viewspace
918                 // clip it to the view plane
919                 if (smins[2] < 1)
920                         smins[2] = 1;
921                 // return true if that culled the box
922                 if (smins[2] >= smaxs[2])
923                         return true;
924                 // ok some of it is infront of the view, transform each corner back to
925                 // worldspace and then to screenspace and make screen rect
926                 // initialize these variables just to avoid compiler warnings
927                 x1 = y1 = x2 = y2 = 0;
928                 for (i = 0;i < 8;i++)
929                 {
930                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
931                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
932                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
933                         v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
934                         v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
935                         v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
936                         v[3] = 1.0f;
937                         GL_TransformToScreen(v, v2);
938                         //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]);
939                         x = v2[0];
940                         y = v2[1];
941                         if (i)
942                         {
943                                 if (x1 > x) x1 = x;
944                                 if (x2 < x) x2 = x;
945                                 if (y1 > y) y1 = y;
946                                 if (y2 < y) y2 = y;
947                         }
948                         else
949                         {
950                                 x1 = x2 = x;
951                                 y1 = y2 = y;
952                         }
953                 }
954                 /*
955                 // this code doesn't handle boxes with any points behind view properly
956                 x1 = 1000;x2 = -1000;
957                 y1 = 1000;y2 = -1000;
958                 for (i = 0;i < 8;i++)
959                 {
960                         v[0] = (i & 1) ? mins[0] : maxs[0];
961                         v[1] = (i & 2) ? mins[1] : maxs[1];
962                         v[2] = (i & 4) ? mins[2] : maxs[2];
963                         v[3] = 1.0f;
964                         GL_TransformToScreen(v, v2);
965                         //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]);
966                         if (v2[2] > 0)
967                         {
968                                 x = v2[0];
969                                 y = v2[1];
970
971                                 if (x1 > x) x1 = x;
972                                 if (x2 < x) x2 = x;
973                                 if (y1 > y) y1 = y;
974                                 if (y2 < y) y2 = y;
975                         }
976                 }
977                 */
978         }
979         ix1 = x1 - 1.0f;
980         iy1 = y1 - 1.0f;
981         ix2 = x2 + 1.0f;
982         iy2 = y2 + 1.0f;
983         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
984         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
985         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
986         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
987         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
988         if (ix2 <= ix1 || iy2 <= iy1)
989                 return true;
990         // set up the scissor rectangle
991         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
992         qglEnable(GL_SCISSOR_TEST);
993         c_rt_scissored++;
994         return false;
995 }
996
997 void R_Shadow_VertexLighting(float *color, int numverts, const float *vertex, const float *normal, const float *lightcolor, const float *relativelightorigin, float lightradius)
998 {
999         float dist, dot, intensity, iradius = 1.0f / lightradius, radius2 = lightradius * lightradius, v[3];
1000         for (;numverts > 0;numverts--, vertex += 4, color += 4, normal += 4)
1001         {
1002                 VectorSubtract(vertex, relativelightorigin, v);
1003                 if ((dot = DotProduct(normal, v)) > 0 && (dist = DotProduct(v, v)) < radius2)
1004                 {
1005                         dist = sqrt(dist);
1006                         intensity = pow(1 - (dist * iradius), r_shadow_attenpower) * r_shadow_attenscale * dot / dist;
1007                         VectorScale(lightcolor, intensity, color);
1008                 }
1009                 else
1010                         VectorClear(color);
1011         }
1012 }
1013
1014 void R_Shadow_VertexLightingWithXYAttenuationTexture(float *color, int numverts, const float *vertex, const float *normal, const float *lightcolor, const float *relativelightorigin, float lightradius, const float *zdir)
1015 {
1016         float dist, dot, intensity, iradius = 1.0f / lightradius, v[3];
1017         for (;numverts > 0;numverts--, vertex += 4, color += 4, normal += 4)
1018         {
1019                 VectorSubtract(vertex, relativelightorigin, v);
1020                 if ((dot = DotProduct(normal, v)) > 0 && (dist = fabs(DotProduct(zdir, v))) < lightradius)
1021                 {
1022                         intensity = pow(1 - (dist * iradius), r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(v,v));
1023                         VectorScale(lightcolor, intensity, color);
1024                 }
1025                 else
1026                         VectorClear(color);
1027         }
1028 }
1029
1030 // FIXME: this should be done in a vertex program when possible
1031 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1032 void R_Shadow_TransformVertices(float *out, int numverts, const float *vertex, const matrix4x4_t *matrix)
1033 {
1034         do
1035         {
1036                 Matrix4x4_Transform(matrix, vertex, out);
1037                 vertex += 4;
1038                 out += 4;
1039         }
1040         while (--numverts);
1041 }
1042
1043 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)
1044 {
1045         int i;
1046         float lightdir[3];
1047         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
1048         {
1049                 VectorSubtract(vertex, relativelightorigin, lightdir);
1050                 // the cubemap normalizes this for us
1051                 out[0] = DotProduct(svectors, lightdir);
1052                 out[1] = DotProduct(tvectors, lightdir);
1053                 out[2] = DotProduct(normals, lightdir);
1054         }
1055 }
1056
1057 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)
1058 {
1059         int i;
1060         float lightdir[3], eyedir[3], halfdir[3];
1061         for (i = 0;i < numverts;i++, vertex += 4, svectors += 4, tvectors += 4, normals += 4, out += 4)
1062         {
1063                 VectorSubtract(vertex, relativelightorigin, lightdir);
1064                 VectorNormalizeFast(lightdir);
1065                 VectorSubtract(vertex, relativeeyeorigin, eyedir);
1066                 VectorNormalizeFast(eyedir);
1067                 VectorAdd(lightdir, eyedir, halfdir);
1068                 // the cubemap normalizes this for us
1069                 out[0] = DotProduct(svectors, halfdir);
1070                 out[1] = DotProduct(tvectors, halfdir);
1071                 out[2] = DotProduct(normals, halfdir);
1072         }
1073 }
1074
1075 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertices, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1076 {
1077         int renders;
1078         float color[3];
1079         rmeshstate_t m;
1080         memset(&m, 0, sizeof(m));
1081         if (gl_dot3arb)
1082         {
1083                 if (!bumptexture)
1084                         bumptexture = r_shadow_blankbumptexture;
1085                 // colorscale accounts for how much we multiply the brightness during combine
1086                 // mult is how many times the final pass of the lighting will be
1087                 // performed to get more brightness than otherwise possible
1088                 // limit mult to 64 for sanity sake
1089                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1090                 {
1091                         // 3/2 3D combine path
1092                         m.tex[0] = R_GetTexture(bumptexture);
1093                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1094                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1095                         m.texcombinergb[0] = GL_REPLACE;
1096                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1097                         R_Mesh_TextureState(&m);
1098                         qglColorMask(0,0,0,1);
1099                         qglDisable(GL_BLEND);
1100                         GL_Color(1,1,1,1);
1101                         R_Mesh_GetSpace(numverts);
1102                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1103                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1104                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin);
1105                         R_Shadow_TransformVertices(varray_texcoord[2], numverts, vertices, matrix_modeltoattenuationxyz);
1106                         R_Mesh_Draw(numverts, numtriangles, elements);
1107                         c_rt_lightmeshes++;
1108                         c_rt_lighttris += numtriangles;
1109
1110                         m.tex[0] = R_GetTexture(basetexture);
1111                         m.tex[1] = 0;
1112                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1113                         m.tex3d[2] = 0;
1114                         m.texcombinergb[0] = GL_MODULATE;
1115                         m.texcombinergb[1] = GL_MODULATE;
1116                         R_Mesh_TextureState(&m);
1117                         qglColorMask(1,1,1,0);
1118                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1119                         qglEnable(GL_BLEND);
1120
1121                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1122                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1123                         {
1124                                 GL_Color(color[0], color[1], color[2], 1);
1125                                 R_Mesh_GetSpace(numverts);
1126                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1127                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1128                                 if (lightcubemap)
1129                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1130                                 R_Mesh_Draw(numverts, numtriangles, elements);
1131                                 c_rt_lightmeshes++;
1132                                 c_rt_lighttris += numtriangles;
1133                         }
1134                 }
1135                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1136                 {
1137                         // 1/2/2 3D combine path
1138                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1139                         R_Mesh_TextureState(&m);
1140                         qglColorMask(0,0,0,1);
1141                         qglDisable(GL_BLEND);
1142                         GL_Color(1,1,1,1);
1143                         R_Mesh_GetSpace(numverts);
1144                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1145                         R_Shadow_TransformVertices(varray_texcoord[0], numverts, vertices, matrix_modeltoattenuationxyz);
1146                         R_Mesh_Draw(numverts, numtriangles, elements);
1147                         c_rt_lightmeshes++;
1148                         c_rt_lighttris += numtriangles;
1149
1150                         m.tex[0] = R_GetTexture(bumptexture);
1151                         m.tex3d[0] = 0;
1152                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1153                         m.texcombinergb[0] = GL_REPLACE;
1154                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1155                         R_Mesh_TextureState(&m);
1156                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1157                         qglEnable(GL_BLEND);
1158                         R_Mesh_GetSpace(numverts);
1159                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1160                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1161                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin);
1162                         R_Mesh_Draw(numverts, numtriangles, elements);
1163                         c_rt_lightmeshes++;
1164                         c_rt_lighttris += numtriangles;
1165
1166                         m.tex[0] = R_GetTexture(basetexture);
1167                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1168                         m.texcombinergb[0] = GL_MODULATE;
1169                         m.texcombinergb[1] = GL_MODULATE;
1170                         R_Mesh_TextureState(&m);
1171                         qglColorMask(1,1,1,0);
1172                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1173
1174                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1175                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1176                         {
1177                                 GL_Color(color[0], color[1], color[2], 1);
1178                                 R_Mesh_GetSpace(numverts);
1179                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1180                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1181                                 if (lightcubemap)
1182                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1183                                 R_Mesh_Draw(numverts, numtriangles, elements);
1184                                 c_rt_lightmeshes++;
1185                                 c_rt_lighttris += numtriangles;
1186                         }
1187                 }
1188                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1189                 {
1190                         // 2/2 3D combine path
1191                         m.tex[0] = R_GetTexture(bumptexture);
1192                         m.tex3d[0] = 0;
1193                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1194                         m.texcombinergb[0] = GL_REPLACE;
1195                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1196                         R_Mesh_TextureState(&m);
1197                         GL_Color(1,1,1,1);
1198                         qglColorMask(0,0,0,1);
1199                         qglDisable(GL_BLEND);
1200                         R_Mesh_GetSpace(numverts);
1201                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1202                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1203                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin);
1204                         R_Mesh_Draw(numverts, numtriangles, elements);
1205                         c_rt_lightmeshes++;
1206                         c_rt_lighttris += numtriangles;
1207
1208                         m.tex[0] = R_GetTexture(basetexture);
1209                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1210                         m.texcombinergb[0] = GL_MODULATE;
1211                         m.texcombinergb[1] = GL_MODULATE;
1212                         R_Mesh_TextureState(&m);
1213                         qglColorMask(1,1,1,0);
1214                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1215                         qglEnable(GL_BLEND);
1216
1217                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1218                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1219                         {
1220                                 GL_Color(color[0], color[1], color[2], 1);
1221                                 R_Mesh_GetSpace(numverts);
1222                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1223                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1224                                 R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltoattenuationxyz);
1225                                 R_Mesh_Draw(numverts, numtriangles, elements);
1226                                 c_rt_lightmeshes++;
1227                                 c_rt_lighttris += numtriangles;
1228                         }
1229                 }
1230                 else if (r_textureunits.integer >= 4)
1231                 {
1232                         // 4/2 2D combine path
1233                         m.tex[0] = R_GetTexture(bumptexture);
1234                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1235                         m.texcombinergb[0] = GL_REPLACE;
1236                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1237                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1238                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1239                         R_Mesh_TextureState(&m);
1240                         qglColorMask(0,0,0,1);
1241                         qglDisable(GL_BLEND);
1242                         GL_Color(1,1,1,1);
1243                         R_Mesh_GetSpace(numverts);
1244                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1245                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1246                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin);
1247                         R_Shadow_TransformVertices(varray_texcoord[2], numverts, vertices, matrix_modeltoattenuationxyz);
1248                         R_Shadow_TransformVertices(varray_texcoord[3], numverts, vertices, matrix_modeltoattenuationz);
1249                         R_Mesh_Draw(numverts, numtriangles, elements);
1250                         c_rt_lightmeshes++;
1251                         c_rt_lighttris += numtriangles;
1252
1253                         m.tex[0] = R_GetTexture(basetexture);
1254                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1255                         m.texcombinergb[0] = GL_MODULATE;
1256                         m.texcombinergb[1] = GL_MODULATE;
1257                         m.tex[2] = 0;
1258                         m.tex[3] = 0;
1259                         R_Mesh_TextureState(&m);
1260                         qglColorMask(1,1,1,0);
1261                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1262                         qglEnable(GL_BLEND);
1263
1264                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1265                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1266                         {
1267                                 GL_Color(color[0], color[1], color[2], 1);
1268                                 R_Mesh_GetSpace(numverts);
1269                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1270                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1271                                 if (lightcubemap)
1272                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1273                                 R_Mesh_Draw(numverts, numtriangles, elements);
1274                                 c_rt_lightmeshes++;
1275                                 c_rt_lighttris += numtriangles;
1276                         }
1277                 }
1278                 else
1279                 {
1280                         // 2/2/2 2D combine path
1281                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1282                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1283                         R_Mesh_TextureState(&m);
1284                         qglColorMask(0,0,0,1);
1285                         qglDisable(GL_BLEND);
1286                         GL_Color(1,1,1,1);
1287                         R_Mesh_GetSpace(numverts);
1288                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1289                         R_Shadow_TransformVertices(varray_texcoord[0], numverts, vertices, matrix_modeltoattenuationxyz);
1290                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltoattenuationz);
1291                         R_Mesh_Draw(numverts, numtriangles, elements);
1292                         c_rt_lightmeshes++;
1293                         c_rt_lighttris += numtriangles;
1294
1295                         m.tex[0] = R_GetTexture(bumptexture);
1296                         m.tex[1] = 0;
1297                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1298                         m.texcombinergb[0] = GL_REPLACE;
1299                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1300                         R_Mesh_TextureState(&m);
1301                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1302                         qglEnable(GL_BLEND);
1303                         R_Mesh_GetSpace(numverts);
1304                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1305                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1306                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin);
1307                         R_Mesh_Draw(numverts, numtriangles, elements);
1308                         c_rt_lightmeshes++;
1309                         c_rt_lighttris += numtriangles;
1310
1311                         m.tex[0] = R_GetTexture(basetexture);
1312                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1313                         m.texcombinergb[0] = GL_MODULATE;
1314                         m.texcombinergb[1] = GL_MODULATE;
1315                         R_Mesh_TextureState(&m);
1316                         qglColorMask(1,1,1,0);
1317                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1318
1319                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1320                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1321                         {
1322                                 GL_Color(color[0], color[1], color[2], 1);
1323                                 R_Mesh_GetSpace(numverts);
1324                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1325                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1326                                 if (lightcubemap)
1327                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1328                                 R_Mesh_Draw(numverts, numtriangles, elements);
1329                                 c_rt_lightmeshes++;
1330                                 c_rt_lighttris += numtriangles;
1331                         }
1332                 }
1333         }
1334         else
1335         {
1336                 if (r_textureunits.integer >= 2)
1337                 {
1338                         // voodoo2
1339 #if 1
1340                         m.tex[0] = R_GetTexture(basetexture);
1341                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1342                         R_Mesh_TextureState(&m);
1343                         qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
1344                         qglEnable(GL_BLEND);
1345 #else
1346                         m.tex[0] = R_GetTexture(basetexture);
1347                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1348                         m.blendfunc1 = GL_SRC_ALPHA;
1349                         m.blendfunc2 = GL_ONE;
1350                         R_Mesh_State(&m);
1351 #endif
1352                         GL_UseColorArray();
1353                         R_Mesh_GetSpace(numverts);
1354                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1355                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1356                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltoattenuationxyz);
1357                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1358                         R_Shadow_VertexLightingWithXYAttenuationTexture(varray_color, numverts, vertices, normals, color, relativelightorigin, lightradius, matrix_modeltofilter->m[2]);
1359                         R_Mesh_Draw(numverts, numtriangles, elements);
1360                 }
1361                 else
1362                 {
1363                         // voodoo1
1364 #if 1
1365                         m.tex[0] = R_GetTexture(basetexture);
1366                         R_Mesh_TextureState(&m);
1367                         qglBlendFunc(GL_SRC_ALPHA, GL_ONE);
1368                         qglEnable(GL_BLEND);
1369 #else
1370                         m.tex[0] = R_GetTexture(basetexture);
1371                         m.blendfunc1 = GL_SRC_ALPHA;
1372                         m.blendfunc2 = GL_ONE;
1373                         R_Mesh_State(&m);
1374 #endif
1375                         GL_UseColorArray();
1376                         R_Mesh_GetSpace(numverts);
1377                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1378                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1379                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color);
1380                         R_Shadow_VertexLighting(varray_color, numverts, vertices, normals, color, relativelightorigin, lightradius);
1381                         R_Mesh_Draw(numverts, numtriangles, elements);
1382                 }
1383         }
1384 }
1385
1386 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertices, const float *svectors, const float *tvectors, const float *normals, const float *texcoords, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1387 {
1388         int renders;
1389         float color[3];
1390         rmeshstate_t m;
1391         if (!gl_dot3arb)
1392                 return;
1393         memset(&m, 0, sizeof(m));
1394         if (!bumptexture)
1395                 bumptexture = r_shadow_blankbumptexture;
1396         if (!glosstexture)
1397                 glosstexture = r_shadow_blankglosstexture;
1398         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1399         {
1400                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1401                 {
1402                         // 2/0/0/0/1/2 3D combine blendsquare path
1403                         m.tex[0] = R_GetTexture(bumptexture);
1404                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1405                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1406                         R_Mesh_TextureState(&m);
1407                         qglColorMask(0,0,0,1);
1408                         qglDisable(GL_BLEND);
1409                         GL_Color(1,1,1,1);
1410                         R_Mesh_GetSpace(numverts);
1411                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1412                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1413                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
1414                         R_Mesh_Draw(numverts, numtriangles, elements);
1415                         c_rt_lightmeshes++;
1416                         c_rt_lighttris += numtriangles;
1417
1418                         m.tex[0] = 0;
1419                         m.texcubemap[1] = 0;
1420                         m.texcombinergb[1] = GL_MODULATE;
1421                         R_Mesh_TextureState(&m);
1422                         // square alpha in framebuffer a few times to make it shiny
1423                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1424                         qglEnable(GL_BLEND);
1425                         // these comments are a test run through this math for intensity 0.5
1426                         // 0.5 * 0.5 = 0.25
1427                         // 0.25 * 0.25 = 0.0625
1428                         // 0.0625 * 0.0625 = 0.00390625
1429                         for (renders = 0;renders < 3;renders++)
1430                         {
1431                                 R_Mesh_GetSpace(numverts);
1432                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1433                                 R_Mesh_Draw(numverts, numtriangles, elements);
1434                         }
1435                         c_rt_lightmeshes += 3;
1436                         c_rt_lighttris += numtriangles * 3;
1437
1438                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1439                         R_Mesh_TextureState(&m);
1440                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1441                         R_Mesh_GetSpace(numverts);
1442                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1443                         R_Shadow_TransformVertices(varray_texcoord[0], numverts, vertices, matrix_modeltoattenuationxyz);
1444                         R_Mesh_Draw(numverts, numtriangles, elements);
1445                         c_rt_lightmeshes++;
1446                         c_rt_lighttris += numtriangles;
1447
1448                         m.tex3d[0] = 0;
1449                         m.tex[0] = R_GetTexture(glosstexture);
1450                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1451                         R_Mesh_TextureState(&m);
1452                         qglColorMask(1,1,1,0);
1453                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1454
1455                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color);
1456                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1457                         {
1458                                 GL_Color(color[0], color[1], color[2], 1);
1459                                 R_Mesh_GetSpace(numverts);
1460                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1461                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1462                                 if (lightcubemap)
1463                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1464                                 R_Mesh_Draw(numverts, numtriangles, elements);
1465                                 c_rt_lightmeshes++;
1466                                 c_rt_lighttris += numtriangles;
1467                         }
1468                 }
1469                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1470                 {
1471                         // 2/0/0/0/2 3D combine blendsquare path
1472                         m.tex[0] = R_GetTexture(bumptexture);
1473                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1474                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1475                         R_Mesh_TextureState(&m);
1476                         qglColorMask(0,0,0,1);
1477                         qglDisable(GL_BLEND);
1478                         GL_Color(1,1,1,1);
1479                         R_Mesh_GetSpace(numverts);
1480                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1481                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1482                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
1483                         R_Mesh_Draw(numverts, numtriangles, elements);
1484                         c_rt_lightmeshes++;
1485                         c_rt_lighttris += numtriangles;
1486
1487                         m.tex[0] = 0;
1488                         m.texcubemap[1] = 0;
1489                         m.texcombinergb[1] = GL_MODULATE;
1490                         R_Mesh_TextureState(&m);
1491                         // square alpha in framebuffer a few times to make it shiny
1492                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1493                         qglEnable(GL_BLEND);
1494                         // these comments are a test run through this math for intensity 0.5
1495                         // 0.5 * 0.5 = 0.25
1496                         // 0.25 * 0.25 = 0.0625
1497                         // 0.0625 * 0.0625 = 0.00390625
1498                         for (renders = 0;renders < 3;renders++)
1499                         {
1500                                 R_Mesh_GetSpace(numverts);
1501                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1502                                 R_Mesh_Draw(numverts, numtriangles, elements);
1503                         }
1504                         c_rt_lightmeshes += 3;
1505                         c_rt_lighttris += numtriangles * 3;
1506
1507                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1508                         m.tex[1] = R_GetTexture(glosstexture);
1509                         R_Mesh_TextureState(&m);
1510                         qglColorMask(1,1,1,0);
1511                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1512                         c_rt_lightmeshes++;
1513                         c_rt_lighttris += numtriangles;
1514
1515                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color);
1516                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1517                         {
1518                                 GL_Color(color[0], color[1], color[2], 1);
1519                                 R_Mesh_GetSpace(numverts);
1520                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1521                                 R_Shadow_TransformVertices(varray_texcoord[0], numverts, vertices, matrix_modeltoattenuationxyz);
1522                                 memcpy(varray_texcoord[1], texcoords, numverts * sizeof(float[4]));
1523                                 R_Mesh_Draw(numverts, numtriangles, elements);
1524                                 c_rt_lightmeshes++;
1525                                 c_rt_lighttris += numtriangles;
1526                         }
1527                 }
1528                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1529                 {
1530                         // 2/0/0/0/2/2 2D combine blendsquare path
1531                         m.tex[0] = R_GetTexture(bumptexture);
1532                         m.texcubemap[1] = R_GetTexture(r_shadow_normalscubetexture);
1533                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1534                         R_Mesh_TextureState(&m);
1535                         qglColorMask(0,0,0,1);
1536                         qglDisable(GL_BLEND);
1537                         GL_Color(1,1,1,1);
1538                         R_Mesh_GetSpace(numverts);
1539                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1540                         memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1541                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord[1], numverts, vertices, svectors, tvectors, normals, relativelightorigin, relativeeyeorigin);
1542                         R_Mesh_Draw(numverts, numtriangles, elements);
1543                         c_rt_lightmeshes++;
1544                         c_rt_lighttris += numtriangles;
1545
1546                         m.tex[0] = 0;
1547                         m.texcubemap[1] = 0;
1548                         m.texcombinergb[1] = GL_MODULATE;
1549                         R_Mesh_TextureState(&m);
1550                         // square alpha in framebuffer a few times to make it shiny
1551                         qglBlendFunc(GL_ZERO, GL_DST_ALPHA);
1552                         qglEnable(GL_BLEND);
1553                         // these comments are a test run through this math for intensity 0.5
1554                         // 0.5 * 0.5 = 0.25
1555                         // 0.25 * 0.25 = 0.0625
1556                         // 0.0625 * 0.0625 = 0.00390625
1557                         for (renders = 0;renders < 3;renders++)
1558                         {
1559                                 R_Mesh_GetSpace(numverts);
1560                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1561                                 R_Mesh_Draw(numverts, numtriangles, elements);
1562                         }
1563                         c_rt_lightmeshes += 3;
1564                         c_rt_lighttris += numtriangles * 3;
1565
1566                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1567                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1568                         R_Mesh_TextureState(&m);
1569                         qglBlendFunc(GL_DST_ALPHA, GL_ZERO);
1570                         R_Mesh_GetSpace(numverts);
1571                         memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1572                         R_Shadow_TransformVertices(varray_texcoord[0], numverts, vertices, matrix_modeltoattenuationxyz);
1573                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltoattenuationz);
1574                         R_Mesh_Draw(numverts, numtriangles, elements);
1575                         c_rt_lightmeshes++;
1576                         c_rt_lighttris += numtriangles;
1577
1578                         m.tex[0] = R_GetTexture(glosstexture);
1579                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1580                         R_Mesh_TextureState(&m);
1581                         qglColorMask(1,1,1,0);
1582                         qglBlendFunc(GL_DST_ALPHA, GL_ONE);
1583
1584                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color);
1585                         for (renders = 0;renders < 64 && (color[0] > 0 || color[1] > 0 || color[2] > 0);renders++, color[0] = max(0, color[0] - 1.0f), color[1] = max(0, color[1] - 1.0f), color[2] = max(0, color[2] - 1.0f))
1586                         {
1587                                 GL_Color(color[0], color[1], color[2], 1);
1588                                 R_Mesh_GetSpace(numverts);
1589                                 memcpy(varray_vertex, vertices, numverts * sizeof(float[4]));
1590                                 memcpy(varray_texcoord[0], texcoords, numverts * sizeof(float[4]));
1591                                 if (lightcubemap)
1592                                         R_Shadow_TransformVertices(varray_texcoord[1], numverts, vertices, matrix_modeltofilter);
1593                                 R_Mesh_Draw(numverts, numtriangles, elements);
1594                                 c_rt_lightmeshes++;
1595                                 c_rt_lighttris += numtriangles;
1596                         }
1597                 }
1598         }
1599 }
1600
1601 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1602 {
1603         R_Mesh_Matrix(matrix);
1604         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1605 }
1606
1607 cvar_t r_editlights = {0, "r_editlights", "0"};
1608 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1609 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1610 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1611 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1612 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1613 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1614 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1615 worldlight_t *r_shadow_worldlightchain;
1616 worldlight_t *r_shadow_selectedlight;
1617 vec3_t r_editlights_cursorlocation;
1618
1619 static int castshadowcount = 1;
1620 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1621 {
1622         int i, j, k, l, maxverts, *mark, tris;
1623         float *verts;
1624         worldlight_t *e;
1625         shadowmesh_t *mesh, *castmesh;
1626         mleaf_t *leaf;
1627         msurface_t *surf;
1628         qbyte *pvs;
1629         surfmesh_t *surfmesh;
1630
1631         if (radius < 15 || DotProduct(color, color) < 0.03)
1632         {
1633                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1634                 return;
1635         }
1636
1637         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1638         VectorCopy(origin, e->origin);
1639         VectorCopy(color, e->light);
1640         e->lightradius = radius;
1641         e->style = style;
1642         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1643         {
1644                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1645                 e->style = 0;
1646         }
1647         e->castshadows = castshadow;
1648
1649         e->cullradius = e->lightradius;
1650         for (k = 0;k < 3;k++)
1651         {
1652                 e->mins[k] = e->origin[k] - e->lightradius;
1653                 e->maxs[k] = e->origin[k] + e->lightradius;
1654         }
1655
1656         e->next = r_shadow_worldlightchain;
1657         r_shadow_worldlightchain = e;
1658         if (cubemapname && cubemapname[0])
1659         {
1660                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1661                 strcpy(e->cubemapname, cubemapname);
1662                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1663         }
1664         if (cl.worldmodel)
1665         {
1666                 castshadowcount++;
1667                 i = Mod_PointContents(e->origin, cl.worldmodel);
1668                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1669                 {
1670                         qbyte *byteleafpvs;
1671                         qbyte *bytesurfacepvs;
1672
1673                         byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1674                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1675
1676                         Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1677
1678                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1679                                 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1680                                         leaf->worldnodeframe = castshadowcount;
1681
1682                         for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1683                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1684                                         surf->castshadow = castshadowcount;
1685
1686                         Mem_Free(byteleafpvs);
1687                         Mem_Free(bytesurfacepvs);
1688                 }
1689                 else
1690                 {
1691                         leaf = Mod_PointInLeaf(origin, cl.worldmodel);
1692                         pvs = Mod_LeafPVS(leaf, cl.worldmodel);
1693                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1694                         {
1695                                 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1696                                 {
1697                                         leaf->worldnodeframe = castshadowcount;
1698                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1699                                         {
1700                                                 surf = cl.worldmodel->surfaces + *mark;
1701                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1702                                                         surf->castshadow = castshadowcount;
1703                                         }
1704                                 }
1705                         }
1706                 }
1707
1708                 e->numleafs = 0;
1709                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1710                         if (leaf->worldnodeframe == castshadowcount)
1711                                 e->numleafs++;
1712                 e->numsurfaces = 0;
1713                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1714                         if (surf->castshadow == castshadowcount)
1715                                 e->numsurfaces++;
1716
1717                 if (e->numleafs)
1718                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1719                 if (e->numsurfaces)
1720                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1721                 e->numleafs = 0;
1722                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1723                         if (leaf->worldnodeframe == castshadowcount)
1724                                 e->leafs[e->numleafs++] = leaf;
1725                 e->numsurfaces = 0;
1726                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1727                         if (surf->castshadow == castshadowcount)
1728                                 e->surfaces[e->numsurfaces++] = surf;
1729
1730                 // find bounding box of lit leafs
1731                 VectorCopy(e->origin, e->mins);
1732                 VectorCopy(e->origin, e->maxs);
1733                 for (j = 0;j < e->numleafs;j++)
1734                 {
1735                         leaf = e->leafs[j];
1736                         for (k = 0;k < 3;k++)
1737                         {
1738                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1739                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1740                         }
1741                 }
1742
1743                 for (k = 0;k < 3;k++)
1744                 {
1745                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1746                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1747                 }
1748                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1749
1750                 if (e->castshadows)
1751                 {
1752                         maxverts = 256;
1753                         verts = NULL;
1754                         castshadowcount++;
1755                         for (j = 0;j < e->numsurfaces;j++)
1756                         {
1757                                 surf = e->surfaces[j];
1758                                 if (surf->flags & SURF_SHADOWCAST)
1759                                 {
1760                                         surf->castshadow = castshadowcount;
1761                                         if (maxverts < surf->poly_numverts)
1762                                                 maxverts = surf->poly_numverts;
1763                                 }
1764                         }
1765                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1766                         // make a mesh to cast a shadow volume from
1767                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1768                         for (j = 0;j < e->numsurfaces;j++)
1769                                 if (e->surfaces[j]->castshadow == castshadowcount)
1770                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1771                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->numverts, surfmesh->verts, surfmesh->numtriangles, surfmesh->index);
1772                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1773
1774                         // cast shadow volume from castmesh
1775                         for (mesh = castmesh;mesh;mesh = mesh->next)
1776                         {
1777                                 R_Shadow_ResizeTriangleFacingLight(castmesh->numtriangles);
1778                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1779
1780                                 if (maxverts < castmesh->numverts * 2)
1781                                 {
1782                                         maxverts = castmesh->numverts * 2;
1783                                         if (verts)
1784                                                 Mem_Free(verts);
1785                                         verts = NULL;
1786                                 }
1787                                 if (verts == NULL && maxverts > 0)
1788                                         verts = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[4]));
1789
1790                                 // now that we have the buffers big enough, construct shadow volume mesh
1791                                 memcpy(verts, castmesh->verts, castmesh->numverts * sizeof(float[4]));
1792                                 R_Shadow_ProjectVertices(verts, castmesh->numverts, e->origin, r_shadow_projectdistance.value);//, e->lightradius);
1793                                 tris = R_Shadow_MakeTriangleShadowFlags(castmesh->elements, verts, castmesh->numtriangles, trianglefacinglight, trianglefacinglightlist, e->origin);
1794                                 tris = R_Shadow_BuildShadowVolumeTriangles(castmesh->elements, castmesh->neighbors, castmesh->numverts, trianglefacinglight, trianglefacinglightlist, tris, shadowelements);
1795                                 // add the constructed shadow volume mesh
1796                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, castmesh->numverts, verts, tris, shadowelements);
1797                         }
1798                         if (verts)
1799                                 Mem_Free(verts);
1800                         verts = NULL;
1801                         // we're done with castmesh now
1802                         Mod_ShadowMesh_Free(castmesh);
1803                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1804                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1805                                 l += mesh->numtriangles;
1806                         Con_Printf("static shadow volume built containing %i triangles\n", l);
1807                 }
1808         }
1809         Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
1810 }
1811
1812 void R_Shadow_FreeWorldLight(worldlight_t *light)
1813 {
1814         worldlight_t **lightpointer;
1815         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1816         if (*lightpointer != light)
1817                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1818         *lightpointer = light->next;
1819         if (light->cubemapname)
1820                 Mem_Free(light->cubemapname);
1821         if (light->shadowvolume)
1822                 Mod_ShadowMesh_Free(light->shadowvolume);
1823         if (light->surfaces)
1824                 Mem_Free(light->surfaces);
1825         if (light->leafs)
1826                 Mem_Free(light->leafs);
1827         Mem_Free(light);
1828 }
1829
1830 void R_Shadow_ClearWorldLights(void)
1831 {
1832         while (r_shadow_worldlightchain)
1833                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1834         r_shadow_selectedlight = NULL;
1835 }
1836
1837 void R_Shadow_SelectLight(worldlight_t *light)
1838 {
1839         if (r_shadow_selectedlight)
1840                 r_shadow_selectedlight->selected = false;
1841         r_shadow_selectedlight = light;
1842         if (r_shadow_selectedlight)
1843                 r_shadow_selectedlight->selected = true;
1844 }
1845
1846
1847 void R_DrawLightSprite(int texnum, const vec3_t origin, vec_t scale, float cr, float cg, float cb, float ca)
1848 {
1849         rmeshstate_t m;
1850         float diff[3];
1851
1852         if (fogenabled)
1853         {
1854                 VectorSubtract(origin, r_origin, diff);
1855                 ca *= 1 - exp(fogdensity/DotProduct(diff,diff));
1856         }
1857
1858         memset(&m, 0, sizeof(m));
1859         m.blendfunc1 = GL_SRC_ALPHA;
1860         m.blendfunc2 = GL_ONE;
1861         m.tex[0] = texnum;
1862         R_Mesh_Matrix(&r_identitymatrix);
1863         R_Mesh_State(&m);
1864
1865         GL_Color(cr * r_colorscale, cg * r_colorscale, cb * r_colorscale, ca);
1866         R_Mesh_GetSpace(4);
1867         varray_texcoord[0][ 0] = 0;varray_texcoord[0][ 1] = 0;
1868         varray_texcoord[0][ 4] = 0;varray_texcoord[0][ 5] = 1;
1869         varray_texcoord[0][ 8] = 1;varray_texcoord[0][ 9] = 1;
1870         varray_texcoord[0][12] = 1;varray_texcoord[0][13] = 0;
1871         varray_vertex[0] = origin[0] - vright[0] * scale - vup[0] * scale;
1872         varray_vertex[1] = origin[1] - vright[1] * scale - vup[1] * scale;
1873         varray_vertex[2] = origin[2] - vright[2] * scale - vup[2] * scale;
1874         varray_vertex[4] = origin[0] - vright[0] * scale + vup[0] * scale;
1875         varray_vertex[5] = origin[1] - vright[1] * scale + vup[1] * scale;
1876         varray_vertex[6] = origin[2] - vright[2] * scale + vup[2] * scale;
1877         varray_vertex[8] = origin[0] + vright[0] * scale + vup[0] * scale;
1878         varray_vertex[9] = origin[1] + vright[1] * scale + vup[1] * scale;
1879         varray_vertex[10] = origin[2] + vright[2] * scale + vup[2] * scale;
1880         varray_vertex[12] = origin[0] + vright[0] * scale - vup[0] * scale;
1881         varray_vertex[13] = origin[1] + vright[1] * scale - vup[1] * scale;
1882         varray_vertex[14] = origin[2] + vright[2] * scale - vup[2] * scale;
1883         R_Mesh_Draw(4, 2, polygonelements);
1884 }
1885
1886 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1887 {
1888         cachepic_t *pic;
1889         pic = Draw_CachePic("gfx/crosshair1.tga");
1890         if (pic)
1891                 R_DrawLightSprite(R_GetTexture(pic->tex), r_editlights_cursorlocation, r_editlights_cursorgrid.value * 0.5f, 1, 1, 1, 0.5);
1892 }
1893
1894 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1895 {
1896         float intensity;
1897         const worldlight_t *light;
1898         light = calldata1;
1899         intensity = 0.5;
1900         if (light->selected)
1901                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1902         if (light->shadowvolume)
1903                 R_DrawLightSprite(calldata2, light->origin, 8, intensity, intensity, intensity, 0.5);
1904         else
1905                 R_DrawLightSprite(calldata2, light->origin, 8, intensity * 0.5, intensity * 0.5, intensity * 0.5, 0.5);
1906 }
1907
1908 void R_Shadow_DrawLightSprites(void)
1909 {
1910         int i, texnums[5];
1911         cachepic_t *pic;
1912         worldlight_t *light;
1913
1914         for (i = 0;i < 5;i++)
1915         {
1916                 pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1));
1917                 if (pic)
1918                         texnums[i] = R_GetTexture(pic->tex);
1919                 else
1920                         texnums[i] = 0;
1921         }
1922
1923         for (light = r_shadow_worldlightchain;light;light = light->next)
1924                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, texnums[((int) light) % 5]);
1925         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1926 }
1927
1928 void R_Shadow_SelectLightInView(void)
1929 {
1930         float bestrating, rating, temp[3];
1931         worldlight_t *best, *light;
1932         best = NULL;
1933         bestrating = 0;
1934         for (light = r_shadow_worldlightchain;light;light = light->next)
1935         {
1936                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1937                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1938                 if (rating >= 0.95)
1939                 {
1940                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1941                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
1942                         {
1943                                 bestrating = rating;
1944                                 best = light;
1945                         }
1946                 }
1947         }
1948         R_Shadow_SelectLight(best);
1949 }
1950
1951 void R_Shadow_LoadWorldLights(void)
1952 {
1953         int n, a, style, shadow;
1954         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1955         float origin[3], radius, color[3];
1956         if (cl.worldmodel == NULL)
1957         {
1958                 Con_Printf("No map loaded.\n");
1959                 return;
1960         }
1961         COM_StripExtension(cl.worldmodel->name, name);
1962         strcat(name, ".rtlights");
1963         lightsstring = COM_LoadFile(name, false);
1964         if (lightsstring)
1965         {
1966                 s = lightsstring;
1967                 n = 0;
1968                 while (*s)
1969                 {
1970                         t = s;
1971                         while (*s && *s != '\n')
1972                                 s++;
1973                         if (!*s)
1974                                 break;
1975                         *s = 0;
1976                         shadow = true;
1977                         // check for modifier flags
1978                         if (*t == '!')
1979                         {
1980                                 shadow = false;
1981                                 t++;
1982                         }
1983                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname);
1984                         if (a < 9)
1985                                 cubemapname[0] = 0;
1986                         *s = '\n';
1987                         if (a < 8)
1988                         {
1989                                 Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
1990                                 break;
1991                         }
1992                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
1993                         radius *= r_editlights_rtlightssizescale.value;
1994                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
1995                         s++;
1996                         n++;
1997                 }
1998                 if (*s)
1999                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2000                 Mem_Free(lightsstring);
2001         }
2002 }
2003
2004 void R_Shadow_SaveWorldLights(void)
2005 {
2006         worldlight_t *light;
2007         int bufchars, bufmaxchars;
2008         char *buf, *oldbuf;
2009         char name[MAX_QPATH];
2010         char line[1024];
2011         if (!r_shadow_worldlightchain)
2012                 return;
2013         if (cl.worldmodel == NULL)
2014         {
2015                 Con_Printf("No map loaded.\n");
2016                 return;
2017         }
2018         COM_StripExtension(cl.worldmodel->name, name);
2019         strcat(name, ".rtlights");
2020         bufchars = bufmaxchars = 0;
2021         buf = NULL;
2022         for (light = r_shadow_worldlightchain;light;light = light->next)
2023         {
2024                 sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2025                 if (bufchars + strlen(line) > bufmaxchars)
2026                 {
2027                         bufmaxchars = bufchars + strlen(line) + 2048;
2028                         oldbuf = buf;
2029                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2030                         if (oldbuf)
2031                         {
2032                                 if (bufchars)
2033                                         memcpy(buf, oldbuf, bufchars);
2034                                 Mem_Free(oldbuf);
2035                         }
2036                 }
2037                 if (strlen(line))
2038                 {
2039                         memcpy(buf + bufchars, line, strlen(line));
2040                         bufchars += strlen(line);
2041                 }
2042         }
2043         if (bufchars)
2044                 COM_WriteFile(name, buf, bufchars);
2045         if (buf)
2046                 Mem_Free(buf);
2047 }
2048
2049 void R_Shadow_LoadLightsFile(void)
2050 {
2051         int n, a, style;
2052         char name[MAX_QPATH], *lightsstring, *s, *t;
2053         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2054         if (cl.worldmodel == NULL)
2055         {
2056                 Con_Printf("No map loaded.\n");
2057                 return;
2058         }
2059         COM_StripExtension(cl.worldmodel->name, name);
2060         strcat(name, ".lights");
2061         lightsstring = COM_LoadFile(name, false);
2062         if (lightsstring)
2063         {
2064                 s = lightsstring;
2065                 n = 0;
2066                 while (*s)
2067                 {
2068                         t = s;
2069                         while (*s && *s != '\n')
2070                                 s++;
2071                         if (!*s)
2072                                 break;
2073                         *s = 0;
2074                         a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &origin[0], &origin[1], &origin[2], &falloff, &color[0], &color[1], &color[2], &subtract, &spotdir[0], &spotdir[1], &spotdir[2], &spotcone, &distbias, &style);
2075                         *s = '\n';
2076                         if (a < 14)
2077                         {
2078                                 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
2079                                 break;
2080                         }
2081                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2082                         radius = bound(15, radius, 4096);
2083                         VectorScale(color, (2.0f / (8388608.0f)), color);
2084                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2085                         s++;
2086                         n++;
2087                 }
2088                 if (*s)
2089                         Con_Printf("invalid lights file \"%s\"\n", name);
2090                 Mem_Free(lightsstring);
2091         }
2092 }
2093
2094 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2095 {
2096         int entnum, style, islight;
2097         char key[256], value[1024];
2098         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2099         const char *data;
2100
2101         if (cl.worldmodel == NULL)
2102         {
2103                 Con_Printf("No map loaded.\n");
2104                 return;
2105         }
2106         data = cl.worldmodel->entities;
2107         if (!data)
2108                 return;
2109         for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2110         {
2111                 light = 0;
2112                 origin[0] = origin[1] = origin[2] = 0;
2113                 originhack[0] = originhack[1] = originhack[2] = 0;
2114                 color[0] = color[1] = color[2] = 1;
2115                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2116                 scale = 1;
2117                 style = 0;
2118                 islight = false;
2119                 while (1)
2120                 {
2121                         if (!COM_ParseToken(&data))
2122                                 break; // error
2123                         if (com_token[0] == '}')
2124                                 break; // end of entity
2125                         if (com_token[0] == '_')
2126                                 strcpy(key, com_token + 1);
2127                         else
2128                                 strcpy(key, com_token);
2129                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2130                                 key[strlen(key)-1] = 0;
2131                         if (!COM_ParseToken(&data))
2132                                 break; // error
2133                         strcpy(value, com_token);
2134
2135                         // now that we have the key pair worked out...
2136                         if (!strcmp("light", key))
2137                                 light = atof(value);
2138                         else if (!strcmp("origin", key))
2139                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2140                         else if (!strcmp("color", key))
2141                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2142                         else if (!strcmp("wait", key))
2143                                 scale = atof(value);
2144                         else if (!strcmp("classname", key))
2145                         {
2146                                 if (!strncmp(value, "light", 5))
2147                                 {
2148                                         islight = true;
2149                                         if (!strcmp(value, "light_fluoro"))
2150                                         {
2151                                                 originhack[0] = 0;
2152                                                 originhack[1] = 0;
2153                                                 originhack[2] = 0;
2154                                                 overridecolor[0] = 1;
2155                                                 overridecolor[1] = 1;
2156                                                 overridecolor[2] = 1;
2157                                         }
2158                                         if (!strcmp(value, "light_fluorospark"))
2159                                         {
2160                                                 originhack[0] = 0;
2161                                                 originhack[1] = 0;
2162                                                 originhack[2] = 0;
2163                                                 overridecolor[0] = 1;
2164                                                 overridecolor[1] = 1;
2165                                                 overridecolor[2] = 1;
2166                                         }
2167                                         if (!strcmp(value, "light_globe"))
2168                                         {
2169                                                 originhack[0] = 0;
2170                                                 originhack[1] = 0;
2171                                                 originhack[2] = 0;
2172                                                 overridecolor[0] = 1;
2173                                                 overridecolor[1] = 0.8;
2174                                                 overridecolor[2] = 0.4;
2175                                         }
2176                                         if (!strcmp(value, "light_flame_large_yellow"))
2177                                         {
2178                                                 originhack[0] = 0;
2179                                                 originhack[1] = 0;
2180                                                 originhack[2] = 48;
2181                                                 overridecolor[0] = 1;
2182                                                 overridecolor[1] = 0.5;
2183                                                 overridecolor[2] = 0.1;
2184                                         }
2185                                         if (!strcmp(value, "light_flame_small_yellow"))
2186                                         {
2187                                                 originhack[0] = 0;
2188                                                 originhack[1] = 0;
2189                                                 originhack[2] = 40;
2190                                                 overridecolor[0] = 1;
2191                                                 overridecolor[1] = 0.5;
2192                                                 overridecolor[2] = 0.1;
2193                                         }
2194                                         if (!strcmp(value, "light_torch_small_white"))
2195                                         {
2196                                                 originhack[0] = 0;
2197                                                 originhack[1] = 0;
2198                                                 originhack[2] = 40;
2199                                                 overridecolor[0] = 1;
2200                                                 overridecolor[1] = 0.5;
2201                                                 overridecolor[2] = 0.1;
2202                                         }
2203                                         if (!strcmp(value, "light_torch_small_walltorch"))
2204                                         {
2205                                                 originhack[0] = 0;
2206                                                 originhack[1] = 0;
2207                                                 originhack[2] = 40;
2208                                                 overridecolor[0] = 1;
2209                                                 overridecolor[1] = 0.5;
2210                                                 overridecolor[2] = 0.1;
2211                                         }
2212                                 }
2213                         }
2214                         else if (!strcmp("style", key))
2215                                 style = atoi(value);
2216                 }
2217                 if (light <= 0 && islight)
2218                         light = 300;
2219                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2220                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2221                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2222                         VectorCopy(overridecolor, color);
2223                 VectorScale(color, light, color);
2224                 VectorAdd(origin, originhack, origin);
2225                 if (radius >= 15)
2226                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2227         }
2228 }
2229
2230
2231 void R_Shadow_SetCursorLocationForView(void)
2232 {
2233         vec_t dist, push, frac;
2234         vec3_t dest, endpos, normal;
2235         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2236         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2237         if (frac < 1)
2238         {
2239                 dist = frac * r_editlights_cursordistance.value;
2240                 push = r_editlights_cursorpushback.value;
2241                 if (push > dist)
2242                         push = dist;
2243                 push = -push;
2244                 VectorMA(endpos, push, vpn, endpos);
2245                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2246         }
2247         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2248         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2249         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2250 }
2251
2252 void R_Shadow_UpdateLightingMode(void)
2253 {
2254         r_shadow_lightingmode = 0;
2255         if (r_shadow_realtime.integer)
2256         {
2257                 if (r_shadow_worldlightchain)
2258                         r_shadow_lightingmode = 2;
2259                 else
2260                         r_shadow_lightingmode = 1;
2261         }
2262 }
2263
2264 void R_Shadow_UpdateWorldLightSelection(void)
2265 {
2266         R_Shadow_SetCursorLocationForView();
2267         if (r_editlights.integer)
2268         {
2269                 R_Shadow_SelectLightInView();
2270                 R_Shadow_DrawLightSprites();
2271         }
2272         else
2273                 R_Shadow_SelectLight(NULL);
2274 }
2275
2276 void R_Shadow_EditLights_Clear_f(void)
2277 {
2278         R_Shadow_ClearWorldLights();
2279 }
2280
2281 void R_Shadow_EditLights_Reload_f(void)
2282 {
2283         r_shadow_reloadlights = true;
2284 }
2285
2286 void R_Shadow_EditLights_Save_f(void)
2287 {
2288         if (cl.worldmodel)
2289                 R_Shadow_SaveWorldLights();
2290 }
2291
2292 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2293 {
2294         R_Shadow_ClearWorldLights();
2295         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2296 }
2297
2298 void R_Shadow_EditLights_ImportLightsFile_f(void)
2299 {
2300         R_Shadow_ClearWorldLights();
2301         R_Shadow_LoadLightsFile();
2302 }
2303
2304 void R_Shadow_EditLights_Spawn_f(void)
2305 {
2306         vec3_t color;
2307         if (!r_editlights.integer)
2308         {
2309                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2310                 return;
2311         }
2312         if (Cmd_Argc() != 1)
2313         {
2314                 Con_Printf("r_editlights_spawn does not take parameters\n");
2315                 return;
2316         }
2317         color[0] = color[1] = color[2] = 1;
2318         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2319 }
2320
2321 void R_Shadow_EditLights_Edit_f(void)
2322 {
2323         vec3_t origin, color;
2324         vec_t radius;
2325         int style, shadows;
2326         char cubemapname[1024];
2327         if (!r_editlights.integer)
2328         {
2329                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2330                 return;
2331         }
2332         if (!r_shadow_selectedlight)
2333         {
2334                 Con_Printf("No selected light.\n");
2335                 return;
2336         }
2337         VectorCopy(r_shadow_selectedlight->origin, origin);
2338         radius = r_shadow_selectedlight->lightradius;
2339         VectorCopy(r_shadow_selectedlight->light, color);
2340         style = r_shadow_selectedlight->style;
2341         if (r_shadow_selectedlight->cubemapname)
2342                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2343         else
2344                 cubemapname[0] = 0;
2345         shadows = r_shadow_selectedlight->castshadows;
2346         if (!strcmp(Cmd_Argv(1), "origin"))
2347         {
2348                 if (Cmd_Argc() != 5)
2349                 {
2350                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2351                         return;
2352                 }
2353                 origin[0] = atof(Cmd_Argv(2));
2354                 origin[1] = atof(Cmd_Argv(3));
2355                 origin[2] = atof(Cmd_Argv(4));
2356         }
2357         else if (!strcmp(Cmd_Argv(1), "originx"))
2358         {
2359                 if (Cmd_Argc() != 3)
2360                 {
2361                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2362                         return;
2363                 }
2364                 origin[0] = atof(Cmd_Argv(2));
2365         }
2366         else if (!strcmp(Cmd_Argv(1), "originy"))
2367         {
2368                 if (Cmd_Argc() != 3)
2369                 {
2370                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2371                         return;
2372                 }
2373                 origin[1] = atof(Cmd_Argv(2));
2374         }
2375         else if (!strcmp(Cmd_Argv(1), "originz"))
2376         {
2377                 if (Cmd_Argc() != 3)
2378                 {
2379                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2380                         return;
2381                 }
2382                 origin[2] = atof(Cmd_Argv(2));
2383         }
2384         else if (!strcmp(Cmd_Argv(1), "move"))
2385         {
2386                 if (Cmd_Argc() != 5)
2387                 {
2388                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2389                         return;
2390                 }
2391                 origin[0] += atof(Cmd_Argv(2));
2392                 origin[1] += atof(Cmd_Argv(3));
2393                 origin[2] += atof(Cmd_Argv(4));
2394         }
2395         else if (!strcmp(Cmd_Argv(1), "movex"))
2396         {
2397                 if (Cmd_Argc() != 3)
2398                 {
2399                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2400                         return;
2401                 }
2402                 origin[0] += atof(Cmd_Argv(2));
2403         }
2404         else if (!strcmp(Cmd_Argv(1), "movey"))
2405         {
2406                 if (Cmd_Argc() != 3)
2407                 {
2408                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2409                         return;
2410                 }
2411                 origin[1] += atof(Cmd_Argv(2));
2412         }
2413         else if (!strcmp(Cmd_Argv(1), "movez"))
2414         {
2415                 if (Cmd_Argc() != 3)
2416                 {
2417                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2418                         return;
2419                 }
2420                 origin[2] += atof(Cmd_Argv(2));
2421         }
2422         else if (!strcmp(Cmd_Argv(1), "color"))
2423         {
2424                 if (Cmd_Argc() != 5)
2425                 {
2426                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2427                         return;
2428                 }
2429                 color[0] = atof(Cmd_Argv(2));
2430                 color[1] = atof(Cmd_Argv(3));
2431                 color[2] = atof(Cmd_Argv(4));
2432         }
2433         else if (!strcmp(Cmd_Argv(1), "radius"))
2434         {
2435                 if (Cmd_Argc() != 3)
2436                 {
2437                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2438                         return;
2439                 }
2440                 radius = atof(Cmd_Argv(2));
2441         }
2442         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2443         {
2444                 if (Cmd_Argc() != 3)
2445                 {
2446                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2447                         return;
2448                 }
2449                 style = atoi(Cmd_Argv(2));
2450         }
2451         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2452         {
2453                 if (Cmd_Argc() > 3)
2454                 {
2455                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2456                         return;
2457                 }
2458                 if (Cmd_Argc() == 3)
2459                         strcpy(cubemapname, Cmd_Argv(2));
2460                 else
2461                         cubemapname[0] = 0;
2462         }
2463         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2464         {
2465                 if (Cmd_Argc() != 3)
2466                 {
2467                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2468                         return;
2469                 }
2470                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2471         }
2472         else
2473         {
2474                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2475                 Con_Printf("Selected light's properties:\n");
2476                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2477                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2478                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2479                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2480                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2481                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2482                 return;
2483         }
2484         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2485         r_shadow_selectedlight = NULL;
2486         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2487 }
2488
2489 extern int con_vislines;
2490 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2491 {
2492         float x, y;
2493         char temp[256];
2494         if (r_shadow_selectedlight == NULL)
2495                 return;
2496         x = 0;
2497         y = con_vislines;
2498         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2499         sprintf(temp, "Origin %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2500         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2501         sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2502         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2503         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2504         sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2505 }
2506
2507 void R_Shadow_EditLights_ToggleShadow_f(void)
2508 {
2509         if (!r_editlights.integer)
2510         {
2511                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2512                 return;
2513         }
2514         if (!r_shadow_selectedlight)
2515         {
2516                 Con_Printf("No selected light.\n");
2517                 return;
2518         }
2519         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
2520         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2521         r_shadow_selectedlight = NULL;
2522 }
2523
2524 void R_Shadow_EditLights_Remove_f(void)
2525 {
2526         if (!r_editlights.integer)
2527         {
2528                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2529                 return;
2530         }
2531         if (!r_shadow_selectedlight)
2532         {
2533                 Con_Printf("No selected light.\n");
2534                 return;
2535         }
2536         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2537         r_shadow_selectedlight = NULL;
2538 }
2539
2540 void R_Shadow_EditLights_Init(void)
2541 {
2542         Cvar_RegisterVariable(&r_editlights);
2543         Cvar_RegisterVariable(&r_editlights_cursordistance);
2544         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2545         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2546         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2547         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2548         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2549         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2550         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2551         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2552         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2553         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2554         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2555         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2556         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2557         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2558         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2559 }