]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
reduced size of viewblend triangle from 64000 units to 64 units, in hopes of curing...
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
20
21 Patent warning:
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
26 shadows do not lie.
27
28
29
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
36
37
38
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
44 in some ideal cases).
45
46
47
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however.  Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
57
58
59
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
65
66
67
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
76
77
78
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
82
83
84
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
89
90
91
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
96
97
98
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
105
106
107
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
112 */
113
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
117 #include "portals.h"
118
119 extern void R_Shadow_EditLights_Init(void);
120
121 #define SHADOWSTAGE_NONE 0
122 #define SHADOWSTAGE_STENCIL 1
123 #define SHADOWSTAGE_LIGHT 2
124 #define SHADOWSTAGE_ERASESTENCIL 3
125
126 int r_shadowstage = SHADOWSTAGE_NONE;
127 int r_shadow_reloadlights = false;
128
129 mempool_t *r_shadow_mempool;
130
131 int maxshadowelements;
132 int *shadowelements;
133 int maxtrianglefacinglight;
134 qbyte *trianglefacinglight;
135 int *trianglefacinglightlist;
136
137 int maxvertexupdate;
138 int *vertexupdate;
139 int *vertexremap;
140 int vertexupdatenum;
141
142 rtexturepool_t *r_shadow_texturepool;
143 rtexture_t *r_shadow_normalcubetexture;
144 rtexture_t *r_shadow_attenuation2dtexture;
145 rtexture_t *r_shadow_attenuation3dtexture;
146 rtexture_t *r_shadow_blankbumptexture;
147 rtexture_t *r_shadow_blankglosstexture;
148 rtexture_t *r_shadow_blankwhitetexture;
149
150 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
151 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
152 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
153 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
154 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
155 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
156 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
157 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
158 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
159 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
160 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
161 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
162 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
163 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
164 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
165 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
166 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
167 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
168 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
169 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
170 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
171 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
172
173 int c_rt_lights, c_rt_clears, c_rt_scissored;
174 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
175 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
176
177 void R_Shadow_ClearWorldLights(void);
178 void R_Shadow_SaveWorldLights(void);
179 void R_Shadow_LoadWorldLights(void);
180 void R_Shadow_LoadLightsFile(void);
181 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
182
183 void r_shadow_start(void)
184 {
185         // allocate vertex processing arrays
186         r_shadow_mempool = Mem_AllocPool("R_Shadow");
187         maxshadowelements = 0;
188         shadowelements = NULL;
189         maxvertexupdate = 0;
190         vertexupdate = NULL;
191         vertexremap = NULL;
192         vertexupdatenum = 0;
193         maxtrianglefacinglight = 0;
194         trianglefacinglight = NULL;
195         trianglefacinglightlist = NULL;
196         r_shadow_normalcubetexture = NULL;
197         r_shadow_attenuation2dtexture = NULL;
198         r_shadow_attenuation3dtexture = NULL;
199         r_shadow_blankbumptexture = NULL;
200         r_shadow_blankglosstexture = NULL;
201         r_shadow_blankwhitetexture = NULL;
202         r_shadow_texturepool = NULL;
203         R_Shadow_ClearWorldLights();
204         r_shadow_reloadlights = true;
205 }
206
207 void r_shadow_shutdown(void)
208 {
209         R_Shadow_ClearWorldLights();
210         r_shadow_reloadlights = true;
211         r_shadow_normalcubetexture = NULL;
212         r_shadow_attenuation2dtexture = NULL;
213         r_shadow_attenuation3dtexture = NULL;
214         r_shadow_blankbumptexture = NULL;
215         r_shadow_blankglosstexture = NULL;
216         r_shadow_blankwhitetexture = NULL;
217         R_FreeTexturePool(&r_shadow_texturepool);
218         maxshadowelements = 0;
219         shadowelements = NULL;
220         maxvertexupdate = 0;
221         vertexupdate = NULL;
222         vertexremap = NULL;
223         vertexupdatenum = 0;
224         maxtrianglefacinglight = 0;
225         trianglefacinglight = NULL;
226         trianglefacinglightlist = NULL;
227         Mem_FreePool(&r_shadow_mempool);
228 }
229
230 void r_shadow_newmap(void)
231 {
232         R_Shadow_ClearWorldLights();
233         r_shadow_reloadlights = true;
234 }
235
236 void R_Shadow_Help_f(void)
237 {
238         Con_Printf(
239 "Documentation on r_shadow system:\n"
240 "Settings:\n"
241 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
242 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
243 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
244 "r_shadow_realtime_world : use realtime world light rendering\n"
245 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
246 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
247 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
248 "r_shadow_glossintensity : brightness of textured gloss\n"
249 "r_shadow_gloss2intensity : brightness of forced gloss\n"
250 "r_shadow_debuglight : render only this light number (-1 = all)\n"
251 "r_shadow_scissor : use scissor optimization\n"
252 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
253 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
254 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
255 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
256 "r_shadow_portallight : use portal visibility for static light precomputation\n"
257 "r_shadow_projectdistance : shadow volume projection distance\n"
258 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
259 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
260 "r_shadow_worldshadows : enable world shadows\n"
261 "r_shadow_dlightshadows : enable dlight shadows\n"
262 "Commands:\n"
263 "r_shadow_help : this help\n"
264         );
265 }
266
267 void R_Shadow_Init(void)
268 {
269         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
270         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
271         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
272         Cvar_RegisterVariable(&r_shadow_realtime_world);
273         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
274         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
275         Cvar_RegisterVariable(&r_shadow_gloss);
276         Cvar_RegisterVariable(&r_shadow_glossintensity);
277         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
278         Cvar_RegisterVariable(&r_shadow_debuglight);
279         Cvar_RegisterVariable(&r_shadow_scissor);
280         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
281         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
282         Cvar_RegisterVariable(&r_shadow_polygonfactor);
283         Cvar_RegisterVariable(&r_shadow_polygonoffset);
284         Cvar_RegisterVariable(&r_shadow_portallight);
285         Cvar_RegisterVariable(&r_shadow_projectdistance);
286         Cvar_RegisterVariable(&r_shadow_texture3d);
287         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
288         Cvar_RegisterVariable(&r_shadow_worldshadows);
289         Cvar_RegisterVariable(&r_shadow_dlightshadows);
290         Cvar_RegisterVariable(&r_shadow_showtris);
291         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
292         R_Shadow_EditLights_Init();
293         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
294 }
295
296 void R_Shadow_ResizeTriangleFacingLight(int numtris)
297 {
298         // make sure trianglefacinglight is big enough for this volume
299         // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
300         // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
301         if (maxtrianglefacinglight < numtris)
302         {
303                 maxtrianglefacinglight = numtris;
304                 if (trianglefacinglight)
305                         Mem_Free(trianglefacinglight);
306                 if (trianglefacinglightlist)
307                         Mem_Free(trianglefacinglightlist);
308                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
309                 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
310         }
311 }
312
313 int *R_Shadow_ResizeShadowElements(int numtris)
314 {
315         // make sure shadowelements is big enough for this volume
316         if (maxshadowelements < numtris * 24)
317         {
318                 maxshadowelements = numtris * 24;
319                 if (shadowelements)
320                         Mem_Free(shadowelements);
321                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
322         }
323         return shadowelements;
324 }
325
326 /*
327 // readable version of some code found below
328 //if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
329 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
330 {
331         float dir0[3], dir1[3], normal[3];
332
333         // calculate two mostly perpendicular edge directions
334         VectorSubtract(a, b, dir0);
335         VectorSubtract(c, b, dir1);
336
337         // we have two edge directions, we can calculate a third vector from
338         // them, which is the direction of the surface normal (it's magnitude
339         // is not 1 however)
340         CrossProduct(dir0, dir1, normal);
341
342         // compare distance of light along normal, with distance of any point
343         // of the triangle along the same normal (the triangle is planar,
344         // I.E. flat, so all points give the same answer)
345         return DotProduct(p, normal) > DotProduct(a, normal);
346 }
347 int checkcastshadowfromedge(int t, int i)
348 {
349         int *te;
350         float *v[3];
351         if (t >= trianglerange_start && t < trianglerange_end)
352         {
353                 if (t < i && !trianglefacinglight[t])
354                         return true;
355                 else
356                         return false;
357         }
358         else
359         {
360                 if (t < 0)
361                         return true;
362                 else
363                 {
364                         te = inelement3i + t * 3;
365                         v[0] = invertex3f + te[0] * 3;
366                         v[1] = invertex3f + te[1] * 3;
367                         v[2] = invertex3f + te[2] * 3;
368                         if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
369                                 return true;
370                         else
371                                 return false;
372                 }
373         }
374 }
375 */
376
377 int R_Shadow_ConstructShadowVolume(int innumvertices, int trianglerange_start, int trianglerange_end, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *relativelightorigin, float projectdistance)
378 {
379         int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
380         const float *v[3];
381         const int *e, *n, *te;
382         float f, temp[3];
383
384         // make sure trianglefacinglight is big enough for this volume
385         if (maxtrianglefacinglight < trianglerange_end)
386                 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
387
388         if (maxvertexupdate < innumvertices)
389         {
390                 maxvertexupdate = innumvertices;
391                 if (vertexupdate)
392                         Mem_Free(vertexupdate);
393                 if (vertexremap)
394                         Mem_Free(vertexremap);
395                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
396                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
397         }
398         vertexupdatenum++;
399
400         if (r_shadow_singlepassvolumegeneration.integer)
401         {
402                 // one pass approach (identify lit/dark faces and generate sides while doing so)
403                 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
404                 {
405                         // calculate triangle facing flag
406                         v[0] = invertex3f + e[0] * 3;
407                         v[1] = invertex3f + e[1] * 3;
408                         v[2] = invertex3f + e[2] * 3;
409                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
410                         {
411                                 // make sure the vertices are created
412                                 for (j = 0;j < 3;j++)
413                                 {
414                                         if (vertexupdate[e[j]] != vertexupdatenum)
415                                         {
416                                                 vertexupdate[e[j]] = vertexupdatenum;
417                                                 vertexremap[e[j]] = outvertices;
418                                                 VectorCopy(v[j], outvertex3f);
419                                                 VectorSubtract(v[j], relativelightorigin, temp);
420                                                 f = projectdistance / VectorLength(temp);
421                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
422                                                 outvertex3f += 6;
423                                                 outvertices += 2;
424                                         }
425                                 }
426                                 // output the front and back triangles
427                                 vr[0] = vertexremap[e[0]];
428                                 vr[1] = vertexremap[e[1]];
429                                 vr[2] = vertexremap[e[2]];
430                                 outelement3i[0] = vr[0];
431                                 outelement3i[1] = vr[1];
432                                 outelement3i[2] = vr[2];
433                                 outelement3i[3] = vr[2] + 1;
434                                 outelement3i[4] = vr[1] + 1;
435                                 outelement3i[5] = vr[0] + 1;
436                                 outelement3i += 6;
437                                 tris += 2;
438                                 // output the sides (facing outward from this triangle)
439                                 t = n[0];
440                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
441                                 {
442                                         outelement3i[0] = vr[1];
443                                         outelement3i[1] = vr[0];
444                                         outelement3i[2] = vr[0] + 1;
445                                         outelement3i[3] = vr[1];
446                                         outelement3i[4] = vr[0] + 1;
447                                         outelement3i[5] = vr[1] + 1;
448                                         outelement3i += 6;
449                                         tris += 2;
450                                 }
451                                 t = n[1];
452                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
453                                 {
454                                         outelement3i[0] = vr[2];
455                                         outelement3i[1] = vr[1];
456                                         outelement3i[2] = vr[1] + 1;
457                                         outelement3i[3] = vr[2];
458                                         outelement3i[4] = vr[1] + 1;
459                                         outelement3i[5] = vr[2] + 1;
460                                         outelement3i += 6;
461                                         tris += 2;
462                                 }
463                                 t = n[2];
464                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
465                                 {
466                                         outelement3i[0] = vr[0];
467                                         outelement3i[1] = vr[2];
468                                         outelement3i[2] = vr[2] + 1;
469                                         outelement3i[3] = vr[0];
470                                         outelement3i[4] = vr[2] + 1;
471                                         outelement3i[5] = vr[0] + 1;
472                                         outelement3i += 6;
473                                         tris += 2;
474                                 }
475                         }
476                         else
477                         {
478                                 // this triangle is not facing the light
479                                 // output the sides (facing inward to this triangle)
480                                 t = n[0];
481                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
482                                 {
483                                         vr[0] = vertexremap[e[0]];
484                                         vr[1] = vertexremap[e[1]];
485                                         outelement3i[0] = vr[1];
486                                         outelement3i[1] = vr[0] + 1;
487                                         outelement3i[2] = vr[0];
488                                         outelement3i[3] = vr[1];
489                                         outelement3i[4] = vr[1] + 1;
490                                         outelement3i[5] = vr[0] + 1;
491                                         outelement3i += 6;
492                                         tris += 2;
493                                 }
494                                 t = n[1];
495                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
496                                 {
497                                         vr[1] = vertexremap[e[1]];
498                                         vr[2] = vertexremap[e[2]];
499                                         outelement3i[0] = vr[2];
500                                         outelement3i[1] = vr[1] + 1;
501                                         outelement3i[2] = vr[1];
502                                         outelement3i[3] = vr[2];
503                                         outelement3i[4] = vr[2] + 1;
504                                         outelement3i[5] = vr[1] + 1;
505                                         outelement3i += 6;
506                                         tris += 2;
507                                 }
508                                 t = n[2];
509                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
510                                 {
511                                         vr[0] = vertexremap[e[0]];
512                                         vr[2] = vertexremap[e[2]];
513                                         outelement3i[0] = vr[0];
514                                         outelement3i[1] = vr[2] + 1;
515                                         outelement3i[2] = vr[2];
516                                         outelement3i[3] = vr[0];
517                                         outelement3i[4] = vr[0] + 1;
518                                         outelement3i[5] = vr[2] + 1;
519                                         outelement3i += 6;
520                                         tris += 2;
521                                 }
522                         }
523                 }
524         }
525         else
526         {
527                 // two pass approach (identify lit/dark faces and then generate sides)
528                 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
529                 {
530                         // calculate triangle facing flag
531                         v[0] = invertex3f + e[0] * 3;
532                         v[1] = invertex3f + e[1] * 3;
533                         v[2] = invertex3f + e[2] * 3;
534                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
535                         {
536                                 trianglefacinglightlist[numfacing++] = i;
537                                 // make sure the vertices are created
538                                 for (j = 0;j < 3;j++)
539                                 {
540                                         if (vertexupdate[e[j]] != vertexupdatenum)
541                                         {
542                                                 vertexupdate[e[j]] = vertexupdatenum;
543                                                 vertexremap[e[j]] = outvertices;
544                                                 VectorSubtract(v[j], relativelightorigin, temp);
545                                                 f = projectdistance / VectorLength(temp);
546                                                 VectorCopy(v[j], outvertex3f);
547                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
548                                                 outvertex3f += 6;
549                                                 outvertices += 2;
550                                         }
551                                 }
552                                 // output the front and back triangles
553                                 outelement3i[0] = vertexremap[e[0]];
554                                 outelement3i[1] = vertexremap[e[1]];
555                                 outelement3i[2] = vertexremap[e[2]];
556                                 outelement3i[3] = vertexremap[e[2]] + 1;
557                                 outelement3i[4] = vertexremap[e[1]] + 1;
558                                 outelement3i[5] = vertexremap[e[0]] + 1;
559                                 outelement3i += 6;
560                                 tris += 2;
561                         }
562                 }
563                 for (i = 0;i < numfacing;i++)
564                 {
565                         t = trianglefacinglightlist[i];
566                         e = inelement3i + t * 3;
567                         n = inneighbor3i + t * 3;
568                         // output the sides (facing outward from this triangle)
569                         t = n[0];
570                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
571                         {
572                                 vr[0] = vertexremap[e[0]];
573                                 vr[1] = vertexremap[e[1]];
574                                 outelement3i[0] = vr[1];
575                                 outelement3i[1] = vr[0];
576                                 outelement3i[2] = vr[0] + 1;
577                                 outelement3i[3] = vr[1];
578                                 outelement3i[4] = vr[0] + 1;
579                                 outelement3i[5] = vr[1] + 1;
580                                 outelement3i += 6;
581                                 tris += 2;
582                         }
583                         t = n[1];
584                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
585                         {
586                                 vr[1] = vertexremap[e[1]];
587                                 vr[2] = vertexremap[e[2]];
588                                 outelement3i[0] = vr[2];
589                                 outelement3i[1] = vr[1];
590                                 outelement3i[2] = vr[1] + 1;
591                                 outelement3i[3] = vr[2];
592                                 outelement3i[4] = vr[1] + 1;
593                                 outelement3i[5] = vr[2] + 1;
594                                 outelement3i += 6;
595                                 tris += 2;
596                         }
597                         t = n[2];
598                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
599                         {
600                                 vr[0] = vertexremap[e[0]];
601                                 vr[2] = vertexremap[e[2]];
602                                 outelement3i[0] = vr[0];
603                                 outelement3i[1] = vr[2];
604                                 outelement3i[2] = vr[2] + 1;
605                                 outelement3i[3] = vr[0];
606                                 outelement3i[4] = vr[2] + 1;
607                                 outelement3i[5] = vr[0] + 1;
608                                 outelement3i += 6;
609                                 tris += 2;
610                         }
611                 }
612         }
613         if (outnumvertices)
614                 *outnumvertices = outvertices;
615         return tris;
616 }
617
618 float varray_vertex3f2[65536*3];
619
620 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
621 {
622         int tris, outverts;
623         if (projectdistance < 0.1)
624         {
625                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
626                 return;
627         }
628         if (!numverts)
629                 return;
630
631         // make sure shadowelements is big enough for this volume
632         if (maxshadowelements < numtris * 24)
633                 R_Shadow_ResizeShadowElements(numtris);
634
635         // check which triangles are facing the light, and then output
636         // triangle elements and vertices...  by clever use of elements we
637         // can construct the whole shadow from the unprojected vertices and
638         // the projected vertices
639         if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
640         {
641                 GL_VertexPointer(varray_vertex3f2);
642                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
643                 {
644                         // decrement stencil if frontface is behind depthbuffer
645                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
646                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
647                         R_Mesh_Draw(outverts, tris, shadowelements);
648                         c_rt_shadowmeshes++;
649                         c_rt_shadowtris += numtris;
650                         // increment stencil if backface is behind depthbuffer
651                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
652                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
653                 }
654                 R_Mesh_Draw(outverts, tris, shadowelements);
655                 c_rt_shadowmeshes++;
656                 c_rt_shadowtris += numtris;
657         }
658 }
659
660 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
661 {
662         shadowmesh_t *mesh;
663         if (r_shadowstage == SHADOWSTAGE_STENCIL)
664         {
665                 // decrement stencil if frontface is behind depthbuffer
666                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
667                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
668                 for (mesh = firstmesh;mesh;mesh = mesh->next)
669                 {
670                         GL_VertexPointer(mesh->vertex3f);
671                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
672                         c_rtcached_shadowmeshes++;
673                         c_rtcached_shadowtris += mesh->numtriangles;
674                 }
675                 // increment stencil if backface is behind depthbuffer
676                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
677                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
678         }
679         for (mesh = firstmesh;mesh;mesh = mesh->next)
680         {
681                 GL_VertexPointer(mesh->vertex3f);
682                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
683                 c_rtcached_shadowmeshes++;
684                 c_rtcached_shadowtris += mesh->numtriangles;
685         }
686 }
687
688 float r_shadow_attenpower, r_shadow_attenscale;
689 static void R_Shadow_MakeTextures(void)
690 {
691         int x, y, z, d, side;
692         float v[3], s, t, intensity;
693         qbyte *data;
694         R_FreeTexturePool(&r_shadow_texturepool);
695         r_shadow_texturepool = R_AllocTexturePool();
696         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
697         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
698 #define NORMSIZE 64
699 #define ATTEN2DSIZE 64
700 #define ATTEN3DSIZE 32
701         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
702         data[0] = 128;
703         data[1] = 128;
704         data[2] = 255;
705         data[3] = 255;
706         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
707         data[0] = 255;
708         data[1] = 255;
709         data[2] = 255;
710         data[3] = 255;
711         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
712         data[0] = 255;
713         data[1] = 255;
714         data[2] = 255;
715         data[3] = 255;
716         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
717         if (gl_texturecubemap)
718         {
719                 for (side = 0;side < 6;side++)
720                 {
721                         for (y = 0;y < NORMSIZE;y++)
722                         {
723                                 for (x = 0;x < NORMSIZE;x++)
724                                 {
725                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
726                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
727                                         switch(side)
728                                         {
729                                         case 0:
730                                                 v[0] = 1;
731                                                 v[1] = -t;
732                                                 v[2] = -s;
733                                                 break;
734                                         case 1:
735                                                 v[0] = -1;
736                                                 v[1] = -t;
737                                                 v[2] = s;
738                                                 break;
739                                         case 2:
740                                                 v[0] = s;
741                                                 v[1] = 1;
742                                                 v[2] = t;
743                                                 break;
744                                         case 3:
745                                                 v[0] = s;
746                                                 v[1] = -1;
747                                                 v[2] = -t;
748                                                 break;
749                                         case 4:
750                                                 v[0] = s;
751                                                 v[1] = -t;
752                                                 v[2] = 1;
753                                                 break;
754                                         case 5:
755                                                 v[0] = -s;
756                                                 v[1] = -t;
757                                                 v[2] = -1;
758                                                 break;
759                                         }
760                                         intensity = 127.0f / sqrt(DotProduct(v, v));
761                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
762                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
763                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
764                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
765                                 }
766                         }
767                 }
768                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
769         }
770         else
771                 r_shadow_normalcubetexture = NULL;
772         for (y = 0;y < ATTEN2DSIZE;y++)
773         {
774                 for (x = 0;x < ATTEN2DSIZE;x++)
775                 {
776                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
777                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
778                         v[2] = 0;
779                         intensity = 1.0f - sqrt(DotProduct(v, v));
780                         if (intensity > 0)
781                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
782                         d = bound(0, intensity, 255);
783                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
784                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
785                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
786                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
787                 }
788         }
789         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
790         if (r_shadow_texture3d.integer)
791         {
792                 for (z = 0;z < ATTEN3DSIZE;z++)
793                 {
794                         for (y = 0;y < ATTEN3DSIZE;y++)
795                         {
796                                 for (x = 0;x < ATTEN3DSIZE;x++)
797                                 {
798                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
799                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
800                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
801                                         intensity = 1.0f - sqrt(DotProduct(v, v));
802                                         if (intensity > 0)
803                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
804                                         d = bound(0, intensity, 255);
805                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
806                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
807                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
808                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
809                                 }
810                         }
811                 }
812                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
813         }
814         Mem_Free(data);
815 }
816
817 void R_Shadow_Stage_Begin(void)
818 {
819         rmeshstate_t m;
820
821         if (r_shadow_texture3d.integer && !gl_texture3d)
822                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
823
824         if (!r_shadow_attenuation2dtexture
825          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
826          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
827          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
828                 R_Shadow_MakeTextures();
829
830         memset(&m, 0, sizeof(m));
831         GL_BlendFunc(GL_ONE, GL_ZERO);
832         GL_DepthMask(false);
833         GL_DepthTest(true);
834         R_Mesh_State_Texture(&m);
835         GL_Color(0, 0, 0, 1);
836         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
837         qglDisable(GL_SCISSOR_TEST);
838         r_shadowstage = SHADOWSTAGE_NONE;
839
840         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
841         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
842         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
843 }
844
845 void R_Shadow_LoadWorldLightsIfNeeded(void)
846 {
847         if (r_shadow_reloadlights && cl.worldmodel)
848         {
849                 R_Shadow_ClearWorldLights();
850                 r_shadow_reloadlights = false;
851                 R_Shadow_LoadWorldLights();
852                 if (r_shadow_worldlightchain == NULL)
853                 {
854                         R_Shadow_LoadLightsFile();
855                         if (r_shadow_worldlightchain == NULL)
856                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
857                 }
858         }
859 }
860
861 void R_Shadow_Stage_ShadowVolumes(void)
862 {
863         rmeshstate_t m;
864         memset(&m, 0, sizeof(m));
865         R_Mesh_State_Texture(&m);
866         GL_Color(1, 1, 1, 1);
867         qglColorMask(0, 0, 0, 0);
868         GL_BlendFunc(GL_ONE, GL_ZERO);
869         GL_DepthMask(false);
870         GL_DepthTest(true);
871         qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
872         //if (r_shadow_polygonoffset.value != 0)
873         //{
874         //      qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
875         //      qglEnable(GL_POLYGON_OFFSET_FILL);
876         //}
877         //else
878         //      qglDisable(GL_POLYGON_OFFSET_FILL);
879         qglDepthFunc(GL_LESS);
880         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
881         qglEnable(GL_STENCIL_TEST);
882         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
883         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
884         r_shadowstage = SHADOWSTAGE_STENCIL;
885         qglClear(GL_STENCIL_BUFFER_BIT);
886         c_rt_clears++;
887         // LordHavoc note: many shadow volumes reside entirely inside the world
888         // (that is to say they are entirely bounded by their lit surfaces),
889         // which can be optimized by handling things as an inverted light volume,
890         // with the shadow boundaries of the world being simulated by an altered
891         // (129) bias to stencil clearing on such lights
892         // FIXME: generate inverted light volumes for use as shadow volumes and
893         // optimize for them as noted above
894 }
895
896 void R_Shadow_Stage_LightWithoutShadows(void)
897 {
898         rmeshstate_t m;
899         memset(&m, 0, sizeof(m));
900         R_Mesh_State_Texture(&m);
901         GL_BlendFunc(GL_ONE, GL_ONE);
902         GL_DepthMask(false);
903         GL_DepthTest(true);
904         qglPolygonOffset(0, 0);
905         //qglDisable(GL_POLYGON_OFFSET_FILL);
906         GL_Color(1, 1, 1, 1);
907         qglColorMask(1, 1, 1, 1);
908         qglDepthFunc(GL_EQUAL);
909         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
910         qglDisable(GL_STENCIL_TEST);
911         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
912         qglStencilFunc(GL_EQUAL, 128, 0xFF);
913         r_shadowstage = SHADOWSTAGE_LIGHT;
914         c_rt_lights++;
915 }
916
917 void R_Shadow_Stage_LightWithShadows(void)
918 {
919         rmeshstate_t m;
920         memset(&m, 0, sizeof(m));
921         R_Mesh_State_Texture(&m);
922         GL_BlendFunc(GL_ONE, GL_ONE);
923         GL_DepthMask(false);
924         GL_DepthTest(true);
925         qglPolygonOffset(0, 0);
926         //qglDisable(GL_POLYGON_OFFSET_FILL);
927         GL_Color(1, 1, 1, 1);
928         qglColorMask(1, 1, 1, 1);
929         qglDepthFunc(GL_EQUAL);
930         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
931         qglEnable(GL_STENCIL_TEST);
932         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
933         // only draw light where this geometry was already rendered AND the
934         // stencil is 128 (values other than this mean shadow)
935         qglStencilFunc(GL_EQUAL, 128, 0xFF);
936         r_shadowstage = SHADOWSTAGE_LIGHT;
937         c_rt_lights++;
938 }
939
940 void R_Shadow_Stage_End(void)
941 {
942         rmeshstate_t m;
943         memset(&m, 0, sizeof(m));
944         R_Mesh_State_Texture(&m);
945         GL_BlendFunc(GL_ONE, GL_ZERO);
946         GL_DepthMask(true);
947         GL_DepthTest(true);
948         qglPolygonOffset(0, 0);
949         //qglDisable(GL_POLYGON_OFFSET_FILL);
950         GL_Color(1, 1, 1, 1);
951         qglColorMask(1, 1, 1, 1);
952         qglDisable(GL_SCISSOR_TEST);
953         qglDepthFunc(GL_LEQUAL);
954         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
955         qglDisable(GL_STENCIL_TEST);
956         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
957         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
958         r_shadowstage = SHADOWSTAGE_NONE;
959 }
960
961 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
962 {
963         int i, ix1, iy1, ix2, iy2;
964         float x1, y1, x2, y2, x, y, f;
965         vec3_t smins, smaxs;
966         vec4_t v, v2;
967         if (!r_shadow_scissor.integer)
968                 return false;
969         // if view is inside the box, just say yes it's visible
970         // LordHavoc: for some odd reason scissor seems broken without stencil
971         // (?!?  seems like a driver bug) so abort if gl_stencil is false
972         if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
973         {
974                 qglDisable(GL_SCISSOR_TEST);
975                 return false;
976         }
977         for (i = 0;i < 3;i++)
978         {
979                 if (r_viewforward[i] >= 0)
980                 {
981                         v[i] = mins[i];
982                         v2[i] = maxs[i];
983                 }
984                 else
985                 {
986                         v[i] = maxs[i];
987                         v2[i] = mins[i];
988                 }
989         }
990         f = DotProduct(r_viewforward, r_vieworigin) + 1;
991         if (DotProduct(r_viewforward, v2) <= f)
992         {
993                 // entirely behind nearclip plane
994                 return true;
995         }
996         if (DotProduct(r_viewforward, v) >= f)
997         {
998                 // entirely infront of nearclip plane
999                 x1 = y1 = x2 = y2 = 0;
1000                 for (i = 0;i < 8;i++)
1001                 {
1002                         v[0] = (i & 1) ? mins[0] : maxs[0];
1003                         v[1] = (i & 2) ? mins[1] : maxs[1];
1004                         v[2] = (i & 4) ? mins[2] : maxs[2];
1005                         v[3] = 1.0f;
1006                         GL_TransformToScreen(v, v2);
1007                         //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]);
1008                         x = v2[0];
1009                         y = v2[1];
1010                         if (i)
1011                         {
1012                                 if (x1 > x) x1 = x;
1013                                 if (x2 < x) x2 = x;
1014                                 if (y1 > y) y1 = y;
1015                                 if (y2 < y) y2 = y;
1016                         }
1017                         else
1018                         {
1019                                 x1 = x2 = x;
1020                                 y1 = y2 = y;
1021                         }
1022                 }
1023         }
1024         else
1025         {
1026                 // clipped by nearclip plane
1027                 // this is nasty and crude...
1028                 // create viewspace bbox
1029                 for (i = 0;i < 8;i++)
1030                 {
1031                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1032                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1033                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1034                         v2[0] = -DotProduct(v, r_viewleft);
1035                         v2[1] = DotProduct(v, r_viewup);
1036                         v2[2] = DotProduct(v, r_viewforward);
1037                         if (i)
1038                         {
1039                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1040                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1041                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1042                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1043                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1044                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1045                         }
1046                         else
1047                         {
1048                                 smins[0] = smaxs[0] = v2[0];
1049                                 smins[1] = smaxs[1] = v2[1];
1050                                 smins[2] = smaxs[2] = v2[2];
1051                         }
1052                 }
1053                 // now we have a bbox in viewspace
1054                 // clip it to the view plane
1055                 if (smins[2] < 1)
1056                         smins[2] = 1;
1057                 // return true if that culled the box
1058                 if (smins[2] >= smaxs[2])
1059                         return true;
1060                 // ok some of it is infront of the view, transform each corner back to
1061                 // worldspace and then to screenspace and make screen rect
1062                 // initialize these variables just to avoid compiler warnings
1063                 x1 = y1 = x2 = y2 = 0;
1064                 for (i = 0;i < 8;i++)
1065                 {
1066                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1067                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1068                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1069                         v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1070                         v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1071                         v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1072                         v[3] = 1.0f;
1073                         GL_TransformToScreen(v, v2);
1074                         //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]);
1075                         x = v2[0];
1076                         y = v2[1];
1077                         if (i)
1078                         {
1079                                 if (x1 > x) x1 = x;
1080                                 if (x2 < x) x2 = x;
1081                                 if (y1 > y) y1 = y;
1082                                 if (y2 < y) y2 = y;
1083                         }
1084                         else
1085                         {
1086                                 x1 = x2 = x;
1087                                 y1 = y2 = y;
1088                         }
1089                 }
1090                 /*
1091                 // this code doesn't handle boxes with any points behind view properly
1092                 x1 = 1000;x2 = -1000;
1093                 y1 = 1000;y2 = -1000;
1094                 for (i = 0;i < 8;i++)
1095                 {
1096                         v[0] = (i & 1) ? mins[0] : maxs[0];
1097                         v[1] = (i & 2) ? mins[1] : maxs[1];
1098                         v[2] = (i & 4) ? mins[2] : maxs[2];
1099                         v[3] = 1.0f;
1100                         GL_TransformToScreen(v, v2);
1101                         //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]);
1102                         if (v2[2] > 0)
1103                         {
1104                                 x = v2[0];
1105                                 y = v2[1];
1106
1107                                 if (x1 > x) x1 = x;
1108                                 if (x2 < x) x2 = x;
1109                                 if (y1 > y) y1 = y;
1110                                 if (y2 < y) y2 = y;
1111                         }
1112                 }
1113                 */
1114         }
1115         ix1 = x1 - 1.0f;
1116         iy1 = y1 - 1.0f;
1117         ix2 = x2 + 1.0f;
1118         iy2 = y2 + 1.0f;
1119         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1120         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1121         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1122         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1123         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1124         if (ix2 <= ix1 || iy2 <= iy1)
1125                 return true;
1126         // set up the scissor rectangle
1127         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1128         qglEnable(GL_SCISSOR_TEST);
1129         c_rt_scissored++;
1130         return false;
1131 }
1132
1133 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1134 {
1135         float *color4f = varray_color4f;
1136         float dist, dot, intensity, v[3], n[3];
1137         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1138         {
1139                 Matrix4x4_Transform(m, vertex3f, v);
1140                 if ((dist = DotProduct(v, v)) < 1)
1141                 {
1142                         Matrix4x4_Transform3x3(m, normal3f, n);
1143                         if ((dot = DotProduct(n, v)) > 0)
1144                         {
1145                                 dist = sqrt(dist);
1146                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1147                                 VectorScale(lightcolor, intensity, color4f);
1148                                 color4f[3] = 1;
1149                         }
1150                         else
1151                         {
1152                                 VectorClear(color4f);
1153                                 color4f[3] = 1;
1154                         }
1155                 }
1156                 else
1157                 {
1158                         VectorClear(color4f);
1159                         color4f[3] = 1;
1160                 }
1161         }
1162 }
1163
1164 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1165 {
1166         float *color4f = varray_color4f;
1167         float dist, dot, intensity, v[3], n[3];
1168         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1169         {
1170                 Matrix4x4_Transform(m, vertex3f, v);
1171                 if ((dist = fabs(v[2])) < 1)
1172                 {
1173                         Matrix4x4_Transform3x3(m, normal3f, n);
1174                         if ((dot = DotProduct(n, v)) > 0)
1175                         {
1176                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1177                                 VectorScale(lightcolor, intensity, color4f);
1178                                 color4f[3] = 1;
1179                         }
1180                         else
1181                         {
1182                                 VectorClear(color4f);
1183                                 color4f[3] = 1;
1184                         }
1185                 }
1186                 else
1187                 {
1188                         VectorClear(color4f);
1189                         color4f[3] = 1;
1190                 }
1191         }
1192 }
1193
1194 // FIXME: this should be done in a vertex program when possible
1195 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1196 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1197 {
1198         do
1199         {
1200                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1201                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1202                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1203                 vertex3f += 3;
1204                 tc3f += 3;
1205         }
1206         while (--numverts);
1207 }
1208
1209 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1210 {
1211         do
1212         {
1213                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1214                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1215                 vertex3f += 3;
1216                 tc2f += 2;
1217         }
1218         while (--numverts);
1219 }
1220
1221 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1222 {
1223         int i;
1224         float lightdir[3];
1225         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1226         {
1227                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1228                 // the cubemap normalizes this for us
1229                 out3f[0] = DotProduct(svector3f, lightdir);
1230                 out3f[1] = DotProduct(tvector3f, lightdir);
1231                 out3f[2] = DotProduct(normal3f, lightdir);
1232         }
1233 }
1234
1235 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1236 {
1237         int i;
1238         float lightdir[3], eyedir[3], halfdir[3];
1239         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1240         {
1241                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1242                 VectorNormalizeFast(lightdir);
1243                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1244                 VectorNormalizeFast(eyedir);
1245                 VectorAdd(lightdir, eyedir, halfdir);
1246                 // the cubemap normalizes this for us
1247                 out3f[0] = DotProduct(svector3f, halfdir);
1248                 out3f[1] = DotProduct(tvector3f, halfdir);
1249                 out3f[2] = DotProduct(normal3f, halfdir);
1250         }
1251 }
1252
1253 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, 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)
1254 {
1255         int renders;
1256         float color[3], color2[3];
1257         rmeshstate_t m;
1258         GL_VertexPointer(vertex3f);
1259         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1260         {
1261                 if (!bumptexture)
1262                         bumptexture = r_shadow_blankbumptexture;
1263                 GL_Color(1,1,1,1);
1264                 // colorscale accounts for how much we multiply the brightness during combine
1265                 // mult is how many times the final pass of the lighting will be
1266                 // performed to get more brightness than otherwise possible
1267                 // limit mult to 64 for sanity sake
1268                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1269                 {
1270                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1271                         memset(&m, 0, sizeof(m));
1272                         m.tex[0] = R_GetTexture(bumptexture);
1273                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1274                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1275                         m.texcombinergb[0] = GL_REPLACE;
1276                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1277                         m.pointer_texcoord[0] = texcoord2f;
1278                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1279                         m.pointer_texcoord[2] = varray_texcoord3f[2];
1280                         R_Mesh_State_Texture(&m);
1281                         qglColorMask(0,0,0,1);
1282                         GL_BlendFunc(GL_ONE, GL_ZERO);
1283                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1284                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1285                         R_Mesh_Draw(numverts, numtriangles, elements);
1286                         c_rt_lightmeshes++;
1287                         c_rt_lighttris += numtriangles;
1288
1289                         memset(&m, 0, sizeof(m));
1290                         m.tex[0] = R_GetTexture(basetexture);
1291                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1292                         m.pointer_texcoord[0] = texcoord2f;
1293                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1294                         R_Mesh_State_Texture(&m);
1295                         qglColorMask(1,1,1,0);
1296                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1297                         if (lightcubemap)
1298                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1299                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1300                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1301                         {
1302                                 color[0] = bound(0, color2[0], 1);
1303                                 color[1] = bound(0, color2[1], 1);
1304                                 color[2] = bound(0, color2[2], 1);
1305                                 GL_Color(color[0], color[1], color[2], 1);
1306                                 R_Mesh_Draw(numverts, numtriangles, elements);
1307                                 c_rt_lightmeshes++;
1308                                 c_rt_lighttris += numtriangles;
1309                         }
1310                 }
1311                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1312                 {
1313                         // 1/2/2 3D combine path (original Radeon)
1314                         memset(&m, 0, sizeof(m));
1315                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1316                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1317                         R_Mesh_State_Texture(&m);
1318                         qglColorMask(0,0,0,1);
1319                         GL_BlendFunc(GL_ONE, GL_ZERO);
1320                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1321                         R_Mesh_Draw(numverts, numtriangles, elements);
1322                         c_rt_lightmeshes++;
1323                         c_rt_lighttris += numtriangles;
1324
1325                         memset(&m, 0, sizeof(m));
1326                         m.tex[0] = R_GetTexture(bumptexture);
1327                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1328                         m.texcombinergb[0] = GL_REPLACE;
1329                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1330                         m.pointer_texcoord[0] = texcoord2f;
1331                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1332                         R_Mesh_State_Texture(&m);
1333                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1334                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1335                         R_Mesh_Draw(numverts, numtriangles, elements);
1336                         c_rt_lightmeshes++;
1337                         c_rt_lighttris += numtriangles;
1338
1339                         memset(&m, 0, sizeof(m));
1340                         m.tex[0] = R_GetTexture(basetexture);
1341                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1342                         m.pointer_texcoord[0] = texcoord2f;
1343                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1344                         R_Mesh_State_Texture(&m);
1345                         qglColorMask(1,1,1,0);
1346                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1347                         if (lightcubemap)
1348                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1349                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1350                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1351                         {
1352                                 color[0] = bound(0, color2[0], 1);
1353                                 color[1] = bound(0, color2[1], 1);
1354                                 color[2] = bound(0, color2[2], 1);
1355                                 GL_Color(color[0], color[1], color[2], 1);
1356                                 R_Mesh_Draw(numverts, numtriangles, elements);
1357                                 c_rt_lightmeshes++;
1358                                 c_rt_lighttris += numtriangles;
1359                         }
1360                 }
1361                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1362                 {
1363                         // 2/2 3D combine path (original Radeon)
1364                         memset(&m, 0, sizeof(m));
1365                         m.tex[0] = R_GetTexture(bumptexture);
1366                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1367                         m.texcombinergb[0] = GL_REPLACE;
1368                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1369                         m.pointer_texcoord[0] = texcoord2f;
1370                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1371                         R_Mesh_State_Texture(&m);
1372                         qglColorMask(0,0,0,1);
1373                         GL_BlendFunc(GL_ONE, GL_ZERO);
1374                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1375                         R_Mesh_Draw(numverts, numtriangles, elements);
1376                         c_rt_lightmeshes++;
1377                         c_rt_lighttris += numtriangles;
1378
1379                         memset(&m, 0, sizeof(m));
1380                         m.tex[0] = R_GetTexture(basetexture);
1381                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1382                         m.pointer_texcoord[0] = texcoord2f;
1383                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1384                         R_Mesh_State_Texture(&m);
1385                         qglColorMask(1,1,1,0);
1386                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1387                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1388                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1389                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1390                         {
1391                                 color[0] = bound(0, color2[0], 1);
1392                                 color[1] = bound(0, color2[1], 1);
1393                                 color[2] = bound(0, color2[2], 1);
1394                                 GL_Color(color[0], color[1], color[2], 1);
1395                                 R_Mesh_Draw(numverts, numtriangles, elements);
1396                                 c_rt_lightmeshes++;
1397                                 c_rt_lighttris += numtriangles;
1398                         }
1399                 }
1400                 else if (r_textureunits.integer >= 4)
1401                 {
1402                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1403                         memset(&m, 0, sizeof(m));
1404                         m.tex[0] = R_GetTexture(bumptexture);
1405                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1406                         m.texcombinergb[0] = GL_REPLACE;
1407                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1408                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1409                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1410                         m.pointer_texcoord[0] = texcoord2f;
1411                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1412                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1413                         m.pointer_texcoord[3] = varray_texcoord2f[3];
1414                         R_Mesh_State_Texture(&m);
1415                         qglColorMask(0,0,0,1);
1416                         GL_BlendFunc(GL_ONE, GL_ZERO);
1417                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1418                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1419                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1420                         R_Mesh_Draw(numverts, numtriangles, elements);
1421                         c_rt_lightmeshes++;
1422                         c_rt_lighttris += numtriangles;
1423
1424                         memset(&m, 0, sizeof(m));
1425                         m.tex[0] = R_GetTexture(basetexture);
1426                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1427                         m.pointer_texcoord[0] = texcoord2f;
1428                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1429                         R_Mesh_State_Texture(&m);
1430                         qglColorMask(1,1,1,0);
1431                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1432                         if (lightcubemap)
1433                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1434                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1435                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1436                         {
1437                                 color[0] = bound(0, color2[0], 1);
1438                                 color[1] = bound(0, color2[1], 1);
1439                                 color[2] = bound(0, color2[2], 1);
1440                                 GL_Color(color[0], color[1], color[2], 1);
1441                                 R_Mesh_Draw(numverts, numtriangles, elements);
1442                                 c_rt_lightmeshes++;
1443                                 c_rt_lighttris += numtriangles;
1444                         }
1445                 }
1446                 else
1447                 {
1448                         // 2/2/2 2D combine path (any dot3 card)
1449                         memset(&m, 0, sizeof(m));
1450                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1451                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1452                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1453                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1454                         R_Mesh_State_Texture(&m);
1455                         qglColorMask(0,0,0,1);
1456                         GL_BlendFunc(GL_ONE, GL_ZERO);
1457                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1458                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1459                         R_Mesh_Draw(numverts, numtriangles, elements);
1460                         c_rt_lightmeshes++;
1461                         c_rt_lighttris += numtriangles;
1462
1463                         memset(&m, 0, sizeof(m));
1464                         m.tex[0] = R_GetTexture(bumptexture);
1465                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1466                         m.texcombinergb[0] = GL_REPLACE;
1467                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1468                         m.pointer_texcoord[0] = texcoord2f;
1469                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1470                         R_Mesh_State_Texture(&m);
1471                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1472                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1473                         R_Mesh_Draw(numverts, numtriangles, elements);
1474                         c_rt_lightmeshes++;
1475                         c_rt_lighttris += numtriangles;
1476
1477                         memset(&m, 0, sizeof(m));
1478                         m.tex[0] = R_GetTexture(basetexture);
1479                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1480                         m.pointer_texcoord[0] = texcoord2f;
1481                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1482                         R_Mesh_State_Texture(&m);
1483                         qglColorMask(1,1,1,0);
1484                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1485                         if (lightcubemap)
1486                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1487                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1488                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1489                         {
1490                                 color[0] = bound(0, color2[0], 1);
1491                                 color[1] = bound(0, color2[1], 1);
1492                                 color[2] = bound(0, color2[2], 1);
1493                                 GL_Color(color[0], color[1], color[2], 1);
1494                                 R_Mesh_Draw(numverts, numtriangles, elements);
1495                                 c_rt_lightmeshes++;
1496                                 c_rt_lighttris += numtriangles;
1497                         }
1498                 }
1499         }
1500         else
1501         {
1502                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1503                 GL_DepthMask(false);
1504                 GL_DepthTest(true);
1505                 GL_ColorPointer(varray_color4f);
1506                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1507                 memset(&m, 0, sizeof(m));
1508                 m.tex[0] = R_GetTexture(basetexture);
1509                 m.pointer_texcoord[0] = texcoord2f;
1510                 if (r_textureunits.integer >= 2)
1511                 {
1512                         // voodoo2
1513                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1514                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1515                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1516                 }
1517                 R_Mesh_State_Texture(&m);
1518                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1519                 {
1520                         color[0] = bound(0, color2[0], 1);
1521                         color[1] = bound(0, color2[1], 1);
1522                         color[2] = bound(0, color2[2], 1);
1523                         if (r_textureunits.integer >= 2)
1524                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1525                         else
1526                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1527                         R_Mesh_Draw(numverts, numtriangles, elements);
1528                         c_rt_lightmeshes++;
1529                         c_rt_lighttris += numtriangles;
1530                 }
1531         }
1532 }
1533
1534 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, 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)
1535 {
1536         int renders;
1537         float color[3], color2[3], colorscale;
1538         rmeshstate_t m;
1539         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1540                 return;
1541         if (!glosstexture)
1542                 glosstexture = r_shadow_blankglosstexture;
1543         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1544         {
1545                 colorscale = r_shadow_glossintensity.value;
1546                 if (!bumptexture)
1547                         bumptexture = r_shadow_blankbumptexture;
1548                 if (glosstexture == r_shadow_blankglosstexture)
1549                         colorscale *= r_shadow_gloss2intensity.value;
1550                 GL_VertexPointer(vertex3f);
1551                 GL_Color(1,1,1,1);
1552                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1553                 {
1554                         // 2/0/0/1/2 3D combine blendsquare path
1555                         memset(&m, 0, sizeof(m));
1556                         m.tex[0] = R_GetTexture(bumptexture);
1557                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1558                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1559                         m.pointer_texcoord[0] = texcoord2f;
1560                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1561                         R_Mesh_State_Texture(&m);
1562                         qglColorMask(0,0,0,1);
1563                         // this squares the result
1564                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1565                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1566                         R_Mesh_Draw(numverts, numtriangles, elements);
1567                         c_rt_lightmeshes++;
1568                         c_rt_lighttris += numtriangles;
1569
1570                         memset(&m, 0, sizeof(m));
1571                         R_Mesh_State_Texture(&m);
1572                         // square alpha in framebuffer a few times to make it shiny
1573                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1574                         // these comments are a test run through this math for intensity 0.5
1575                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1576                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1577                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1578                         R_Mesh_Draw(numverts, numtriangles, elements);
1579                         c_rt_lightmeshes++;
1580                         c_rt_lighttris += numtriangles;
1581                         R_Mesh_Draw(numverts, numtriangles, elements);
1582                         c_rt_lightmeshes++;
1583                         c_rt_lighttris += numtriangles;
1584
1585                         memset(&m, 0, sizeof(m));
1586                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1587                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1588                         R_Mesh_State_Texture(&m);
1589                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1590                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1591                         R_Mesh_Draw(numverts, numtriangles, elements);
1592                         c_rt_lightmeshes++;
1593                         c_rt_lighttris += numtriangles;
1594
1595                         memset(&m, 0, sizeof(m));
1596                         m.tex[0] = R_GetTexture(glosstexture);
1597                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1598                         m.pointer_texcoord[0] = texcoord2f;
1599                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1600                         R_Mesh_State_Texture(&m);
1601                         qglColorMask(1,1,1,0);
1602                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1603                         if (lightcubemap)
1604                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1605                         VectorScale(lightcolor, colorscale, color2);
1606                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1607                         {
1608                                 color[0] = bound(0, color2[0], 1);
1609                                 color[1] = bound(0, color2[1], 1);
1610                                 color[2] = bound(0, color2[2], 1);
1611                                 GL_Color(color[0], color[1], color[2], 1);
1612                                 R_Mesh_Draw(numverts, numtriangles, elements);
1613                                 c_rt_lightmeshes++;
1614                                 c_rt_lighttris += numtriangles;
1615                         }
1616                 }
1617                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1618                 {
1619                         // 2/0/0/2 3D combine blendsquare path
1620                         memset(&m, 0, sizeof(m));
1621                         m.tex[0] = R_GetTexture(bumptexture);
1622                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1623                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1624                         m.pointer_texcoord[0] = texcoord2f;
1625                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1626                         R_Mesh_State_Texture(&m);
1627                         qglColorMask(0,0,0,1);
1628                         // this squares the result
1629                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1630                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1631                         R_Mesh_Draw(numverts, numtriangles, elements);
1632                         c_rt_lightmeshes++;
1633                         c_rt_lighttris += numtriangles;
1634
1635                         memset(&m, 0, sizeof(m));
1636                         R_Mesh_State_Texture(&m);
1637                         // square alpha in framebuffer a few times to make it shiny
1638                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1639                         // these comments are a test run through this math for intensity 0.5
1640                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1641                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1642                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1643                         R_Mesh_Draw(numverts, numtriangles, elements);
1644                         c_rt_lightmeshes++;
1645                         c_rt_lighttris += numtriangles;
1646                         R_Mesh_Draw(numverts, numtriangles, elements);
1647                         c_rt_lightmeshes++;
1648                         c_rt_lighttris += numtriangles;
1649
1650                         memset(&m, 0, sizeof(m));
1651                         m.tex[0] = R_GetTexture(glosstexture);
1652                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1653                         m.pointer_texcoord[0] = texcoord2f;
1654                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1655                         R_Mesh_State_Texture(&m);
1656                         qglColorMask(1,1,1,0);
1657                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1658                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1659                         VectorScale(lightcolor, colorscale, color2);
1660                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1661                         {
1662                                 color[0] = bound(0, color2[0], 1);
1663                                 color[1] = bound(0, color2[1], 1);
1664                                 color[2] = bound(0, color2[2], 1);
1665                                 GL_Color(color[0], color[1], color[2], 1);
1666                                 R_Mesh_Draw(numverts, numtriangles, elements);
1667                                 c_rt_lightmeshes++;
1668                                 c_rt_lighttris += numtriangles;
1669                         }
1670                 }
1671                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1672                 {
1673                         // 2/0/0/2/2 2D combine blendsquare path
1674                         memset(&m, 0, sizeof(m));
1675                         m.tex[0] = R_GetTexture(bumptexture);
1676                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1677                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1678                         m.pointer_texcoord[0] = texcoord2f;
1679                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1680                         R_Mesh_State_Texture(&m);
1681                         qglColorMask(0,0,0,1);
1682                         // this squares the result
1683                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1684                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1685                         R_Mesh_Draw(numverts, numtriangles, elements);
1686                         c_rt_lightmeshes++;
1687                         c_rt_lighttris += numtriangles;
1688
1689                         memset(&m, 0, sizeof(m));
1690                         R_Mesh_State_Texture(&m);
1691                         // square alpha in framebuffer a few times to make it shiny
1692                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1693                         // these comments are a test run through this math for intensity 0.5
1694                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1695                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1696                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1697                         R_Mesh_Draw(numverts, numtriangles, elements);
1698                         c_rt_lightmeshes++;
1699                         c_rt_lighttris += numtriangles;
1700                         R_Mesh_Draw(numverts, numtriangles, elements);
1701                         c_rt_lightmeshes++;
1702                         c_rt_lighttris += numtriangles;
1703
1704                         memset(&m, 0, sizeof(m));
1705                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1706                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1707                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1708                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1709                         R_Mesh_State_Texture(&m);
1710                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1711                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1712                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1713                         R_Mesh_Draw(numverts, numtriangles, elements);
1714                         c_rt_lightmeshes++;
1715                         c_rt_lighttris += numtriangles;
1716
1717                         memset(&m, 0, sizeof(m));
1718                         m.tex[0] = R_GetTexture(glosstexture);
1719                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1720                         m.pointer_texcoord[0] = texcoord2f;
1721                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1722                         R_Mesh_State_Texture(&m);
1723                         qglColorMask(1,1,1,0);
1724                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1725                         if (lightcubemap)
1726                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1727                         VectorScale(lightcolor, colorscale, color2);
1728                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1729                         {
1730                                 color[0] = bound(0, color2[0], 1);
1731                                 color[1] = bound(0, color2[1], 1);
1732                                 color[2] = bound(0, color2[2], 1);
1733                                 GL_Color(color[0], color[1], color[2], 1);
1734                                 R_Mesh_Draw(numverts, numtriangles, elements);
1735                                 c_rt_lightmeshes++;
1736                                 c_rt_lighttris += numtriangles;
1737                         }
1738                 }
1739         }
1740 }
1741
1742 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1743 {
1744         R_Mesh_Matrix(matrix);
1745         if (r_shadow_showtris.integer)
1746         {
1747                 shadowmesh_t *mesh;
1748                 rmeshstate_t m;
1749                 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1750                 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1751                 qglDisable(GL_DEPTH_TEST);
1752                 qglDisable(GL_STENCIL_TEST);
1753                 //qglDisable(GL_CULL_FACE);
1754                 qglColorMask(1,1,1,1);
1755                 memset(&m, 0, sizeof(m));
1756                 R_Mesh_State_Texture(&m);
1757                 GL_Color(0,0.1,0,1);
1758                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1759                 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1760                 {
1761                         GL_VertexPointer(mesh->vertex3f);
1762                         R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1763                 }
1764                 //qglEnable(GL_CULL_FACE);
1765                 if (depthenabled)
1766                         qglEnable(GL_DEPTH_TEST);
1767                 if (stencilenabled)
1768                 {
1769                         qglEnable(GL_STENCIL_TEST);
1770                         qglColorMask(0,0,0,0);
1771                 }
1772         }
1773         R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1774 }
1775
1776 void R_Shadow_DrawStaticWorldLight_Light(worldlight_t *light, matrix4x4_t *matrix, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz)
1777 {
1778         shadowmesh_t *mesh;
1779         R_Mesh_Matrix(matrix);
1780         if (r_shadow_showtris.integer)
1781         {
1782                 rmeshstate_t m;
1783                 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1784                 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1785                 qglDisable(GL_DEPTH_TEST);
1786                 qglDisable(GL_STENCIL_TEST);
1787                 //qglDisable(GL_CULL_FACE);
1788                 memset(&m, 0, sizeof(m));
1789                 R_Mesh_State_Texture(&m);
1790                 GL_Color(0.2,0,0,1);
1791                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1792                 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1793                 {
1794                         GL_VertexPointer(mesh->vertex3f);
1795                         R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1796                 }
1797                 //qglEnable(GL_CULL_FACE);
1798                 if (depthenabled)
1799                         qglEnable(GL_DEPTH_TEST);
1800                 if (stencilenabled)
1801                         qglEnable(GL_STENCIL_TEST);
1802         }
1803         for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1804         {
1805                 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, NULL);
1806                 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, NULL);
1807         }
1808 }
1809
1810 cvar_t r_editlights = {0, "r_editlights", "0"};
1811 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1812 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1813 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1814 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1815 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1816 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1817 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1818 worldlight_t *r_shadow_worldlightchain;
1819 worldlight_t *r_shadow_selectedlight;
1820 vec3_t r_editlights_cursorlocation;
1821
1822 static int lightpvsbytes;
1823 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1824
1825 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1826 {
1827         int i, j, k, l, maxverts = 256, tris;
1828         float *vertex3f = NULL, mins[3], maxs[3];
1829         worldlight_t *e;
1830         shadowmesh_t *mesh, *castmesh = NULL;
1831
1832         if (radius < 15 || DotProduct(color, color) < 0.03)
1833         {
1834                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1835                 return;
1836         }
1837
1838         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1839         VectorCopy(origin, e->origin);
1840         VectorCopy(color, e->light);
1841         e->lightradius = radius;
1842         e->style = style;
1843         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1844         {
1845                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1846                 e->style = 0;
1847         }
1848         e->castshadows = castshadow;
1849
1850         e->cullradius = e->lightradius;
1851         for (k = 0;k < 3;k++)
1852         {
1853                 mins[k] = e->origin[k] - e->lightradius;
1854                 maxs[k] = e->origin[k] + e->lightradius;
1855         }
1856
1857         e->next = r_shadow_worldlightchain;
1858         r_shadow_worldlightchain = e;
1859         if (cubemapname && cubemapname[0])
1860         {
1861                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1862                 strcpy(e->cubemapname, cubemapname);
1863                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1864         }
1865         // FIXME: rewrite this to store ALL geometry into a cache in the light
1866         if (e->castshadows)
1867                 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1868         e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1869         if (cl.worldmodel)
1870         {
1871                 if (cl.worldmodel->brushq3.num_leafs)
1872                 {
1873                         q3mleaf_t *leaf;
1874                         q3mface_t *face;
1875                         lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1876                         VectorCopy(e->origin, e->mins);
1877                         VectorCopy(e->origin, e->maxs);
1878                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1879                                 face->lighttemp_castshadow = false;
1880                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1881                         {
1882                                 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1883                                 {
1884                                         for (k = 0;k < 3;k++)
1885                                         {
1886                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1887                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1888                                         }
1889                                         for (j = 0;j < leaf->numleaffaces;j++)
1890                                         {
1891                                                 face = leaf->firstleafface[j];
1892                                                 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1893                                                         face->lighttemp_castshadow = true;
1894                                         }
1895                                 }
1896                         }
1897
1898                         // add surfaces to shadow casting mesh and light mesh
1899                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1900                         {
1901                                 if (face->lighttemp_castshadow)
1902                                 {
1903                                         face->lighttemp_castshadow = false;
1904                                         if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1905                                         {
1906                                                 if (e->castshadows)
1907                                                         if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1908                                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1909                                                 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1910                                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1911                                         }
1912                                 }
1913                         }
1914                 }
1915                 else if (cl.worldmodel->brushq1.numleafs)
1916                 {
1917                         mleaf_t *leaf;
1918                         msurface_t *surf;
1919                         VectorCopy(e->origin, e->mins);
1920                         VectorCopy(e->origin, e->maxs);
1921                         i = CL_PointQ1Contents(e->origin);
1922
1923                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1924                                 surf->lighttemp_castshadow = false;
1925
1926                         if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1927                         {
1928                                 qbyte *byteleafpvs;
1929                                 qbyte *bytesurfacepvs;
1930
1931                                 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1932                                 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1933
1934                                 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1935
1936                                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1937                                 {
1938                                         if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1939                                         {
1940                                                 for (k = 0;k < 3;k++)
1941                                                 {
1942                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1943                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1944                                                 }
1945                                         }
1946                                 }
1947
1948                                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1949                                         if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1950                                                 surf->lighttemp_castshadow = true;
1951
1952                                 Mem_Free(byteleafpvs);
1953                                 Mem_Free(bytesurfacepvs);
1954                         }
1955                         else
1956                         {
1957                                 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1958                                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1959                                 {
1960                                         if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1961                                         {
1962                                                 for (k = 0;k < 3;k++)
1963                                                 {
1964                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1965                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1966                                                 }
1967                                                 for (j = 0;j < leaf->nummarksurfaces;j++)
1968                                                 {
1969                                                         surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1970                                                         if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1971                                                                 surf->lighttemp_castshadow = true;
1972                                                 }
1973                                         }
1974                                 }
1975                         }
1976
1977                         // add surfaces to shadow casting mesh and light mesh
1978                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1979                         {
1980                                 if (surf->lighttemp_castshadow)
1981                                 {
1982                                         surf->lighttemp_castshadow = false;
1983                                         if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1984                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
1985                                         if (!(surf->flags & SURF_DRAWSKY))
1986                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1987                                 }
1988                         }
1989                 }
1990         }
1991
1992         // limit box to light bounds (in case it grew larger)
1993         for (k = 0;k < 3;k++)
1994         {
1995                 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1996                 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1997         }
1998         e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1999
2000         // cast shadow volume from castmesh
2001         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2002         if (castmesh)
2003         {
2004                 maxverts = 0;
2005                 for (mesh = castmesh;mesh;mesh = mesh->next)
2006                 {
2007                         R_Shadow_ResizeShadowElements(mesh->numtriangles);
2008                         maxverts = max(maxverts, mesh->numverts * 2);
2009                 }
2010
2011                 if (maxverts > 0)
2012                 {
2013                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2014                         // now that we have the buffers big enough, construct and add
2015                         // the shadow volume mesh
2016                         if (e->castshadows)
2017                                 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2018                         for (mesh = castmesh;mesh;mesh = mesh->next)
2019                         {
2020                                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2021                                 if ((tris = R_Shadow_ConstructShadowVolume(castmesh->numverts, 0, castmesh->numtriangles, castmesh->element3i, castmesh->neighbor3i, castmesh->vertex3f, NULL, shadowelements, vertex3f, e->origin, r_shadow_projectdistance.value)))
2022                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2023                         }
2024                         Mem_Free(vertex3f);
2025                         vertex3f = NULL;
2026                 }
2027                 // we're done with castmesh now
2028                 Mod_ShadowMesh_Free(castmesh);
2029         }
2030
2031         e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2032         e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2033
2034         k = 0;
2035         if (e->meshchain_shadow)
2036                 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2037                         k += mesh->numtriangles;
2038         l = 0;
2039         if (e->meshchain_light)
2040                 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2041                         l += mesh->numtriangles;
2042         Con_Printf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], k, l);
2043 }
2044
2045 void R_Shadow_FreeWorldLight(worldlight_t *light)
2046 {
2047         worldlight_t **lightpointer;
2048         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2049         if (*lightpointer != light)
2050                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2051         *lightpointer = light->next;
2052         if (light->cubemapname)
2053                 Mem_Free(light->cubemapname);
2054         if (light->meshchain_shadow)
2055                 Mod_ShadowMesh_Free(light->meshchain_shadow);
2056         if (light->meshchain_light)
2057                 Mod_ShadowMesh_Free(light->meshchain_light);
2058         Mem_Free(light);
2059 }
2060
2061 void R_Shadow_ClearWorldLights(void)
2062 {
2063         while (r_shadow_worldlightchain)
2064                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2065         r_shadow_selectedlight = NULL;
2066 }
2067
2068 void R_Shadow_SelectLight(worldlight_t *light)
2069 {
2070         if (r_shadow_selectedlight)
2071                 r_shadow_selectedlight->selected = false;
2072         r_shadow_selectedlight = light;
2073         if (r_shadow_selectedlight)
2074                 r_shadow_selectedlight->selected = true;
2075 }
2076
2077 rtexture_t *lighttextures[5];
2078
2079 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2080 {
2081         float scale = r_editlights_cursorgrid.value * 0.5f;
2082         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2083 }
2084
2085 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2086 {
2087         float intensity;
2088         const worldlight_t *light;
2089         light = calldata1;
2090         intensity = 0.5;
2091         if (light->selected)
2092                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2093         if (!light->meshchain_shadow)
2094                 intensity *= 0.5f;
2095         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2096 }
2097
2098 void R_Shadow_DrawLightSprites(void)
2099 {
2100         int i;
2101         cachepic_t *pic;
2102         worldlight_t *light;
2103
2104         for (i = 0;i < 5;i++)
2105         {
2106                 lighttextures[i] = NULL;
2107                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2108                         lighttextures[i] = pic->tex;
2109         }
2110
2111         for (light = r_shadow_worldlightchain;light;light = light->next)
2112                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2113         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2114 }
2115
2116 void R_Shadow_SelectLightInView(void)
2117 {
2118         float bestrating, rating, temp[3];
2119         worldlight_t *best, *light;
2120         best = NULL;
2121         bestrating = 0;
2122         for (light = r_shadow_worldlightchain;light;light = light->next)
2123         {
2124                 VectorSubtract(light->origin, r_vieworigin, temp);
2125                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2126                 if (rating >= 0.95)
2127                 {
2128                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2129                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2130                         {
2131                                 bestrating = rating;
2132                                 best = light;
2133                         }
2134                 }
2135         }
2136         R_Shadow_SelectLight(best);
2137 }
2138
2139 void R_Shadow_LoadWorldLights(void)
2140 {
2141         int n, a, style, shadow;
2142         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2143         float origin[3], radius, color[3];
2144         if (cl.worldmodel == NULL)
2145         {
2146                 Con_Printf("No map loaded.\n");
2147                 return;
2148         }
2149         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2150         strlcat (name, ".rtlights", sizeof (name));
2151         lightsstring = FS_LoadFile(name, false);
2152         if (lightsstring)
2153         {
2154                 s = lightsstring;
2155                 n = 0;
2156                 while (*s)
2157                 {
2158                         t = s;
2159                         while (*s && *s != '\n')
2160                                 s++;
2161                         if (!*s)
2162                                 break;
2163                         *s = 0;
2164                         shadow = true;
2165                         // check for modifier flags
2166                         if (*t == '!')
2167                         {
2168                                 shadow = false;
2169                                 t++;
2170                         }
2171                         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);
2172                         if (a < 9)
2173                                 cubemapname[0] = 0;
2174                         *s = '\n';
2175                         if (a < 8)
2176                         {
2177                                 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);
2178                                 break;
2179                         }
2180                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2181                         radius *= r_editlights_rtlightssizescale.value;
2182                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2183                         s++;
2184                         n++;
2185                 }
2186                 if (*s)
2187                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2188                 Mem_Free(lightsstring);
2189         }
2190 }
2191
2192 void R_Shadow_SaveWorldLights(void)
2193 {
2194         worldlight_t *light;
2195         int bufchars, bufmaxchars;
2196         char *buf, *oldbuf;
2197         char name[MAX_QPATH];
2198         char line[1024];
2199         if (!r_shadow_worldlightchain)
2200                 return;
2201         if (cl.worldmodel == NULL)
2202         {
2203                 Con_Printf("No map loaded.\n");
2204                 return;
2205         }
2206         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2207         strlcat (name, ".rtlights", sizeof (name));
2208         bufchars = bufmaxchars = 0;
2209         buf = NULL;
2210         for (light = r_shadow_worldlightchain;light;light = light->next)
2211         {
2212                 sprintf(line, "%s%f %f %f %f %f %f %f %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 : "");
2213                 if (bufchars + (int) strlen(line) > bufmaxchars)
2214                 {
2215                         bufmaxchars = bufchars + strlen(line) + 2048;
2216                         oldbuf = buf;
2217                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2218                         if (oldbuf)
2219                         {
2220                                 if (bufchars)
2221                                         memcpy(buf, oldbuf, bufchars);
2222                                 Mem_Free(oldbuf);
2223                         }
2224                 }
2225                 if (strlen(line))
2226                 {
2227                         memcpy(buf + bufchars, line, strlen(line));
2228                         bufchars += strlen(line);
2229                 }
2230         }
2231         if (bufchars)
2232                 FS_WriteFile(name, buf, bufchars);
2233         if (buf)
2234                 Mem_Free(buf);
2235 }
2236
2237 void R_Shadow_LoadLightsFile(void)
2238 {
2239         int n, a, style;
2240         char name[MAX_QPATH], *lightsstring, *s, *t;
2241         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2242         if (cl.worldmodel == NULL)
2243         {
2244                 Con_Printf("No map loaded.\n");
2245                 return;
2246         }
2247         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2248         strlcat (name, ".lights", sizeof (name));
2249         lightsstring = FS_LoadFile(name, false);
2250         if (lightsstring)
2251         {
2252                 s = lightsstring;
2253                 n = 0;
2254                 while (*s)
2255                 {
2256                         t = s;
2257                         while (*s && *s != '\n')
2258                                 s++;
2259                         if (!*s)
2260                                 break;
2261                         *s = 0;
2262                         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);
2263                         *s = '\n';
2264                         if (a < 14)
2265                         {
2266                                 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);
2267                                 break;
2268                         }
2269                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2270                         radius = bound(15, radius, 4096);
2271                         VectorScale(color, (2.0f / (8388608.0f)), color);
2272                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2273                         s++;
2274                         n++;
2275                 }
2276                 if (*s)
2277                         Con_Printf("invalid lights file \"%s\"\n", name);
2278                 Mem_Free(lightsstring);
2279         }
2280 }
2281
2282 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2283 {
2284         int entnum, style, islight;
2285         char key[256], value[1024];
2286         float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2287         const char *data;
2288
2289         if (cl.worldmodel == NULL)
2290         {
2291                 Con_Printf("No map loaded.\n");
2292                 return;
2293         }
2294         data = cl.worldmodel->brush.entities;
2295         if (!data)
2296                 return;
2297         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2298         {
2299                 light = 0;
2300                 origin[0] = origin[1] = origin[2] = 0;
2301                 originhack[0] = originhack[1] = originhack[2] = 0;
2302                 color[0] = color[1] = color[2] = 1;
2303                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2304                 fadescale = 1;
2305                 lightscale = 1;
2306                 style = 0;
2307                 islight = false;
2308                 while (1)
2309                 {
2310                         if (!COM_ParseToken(&data, false))
2311                                 break; // error
2312                         if (com_token[0] == '}')
2313                                 break; // end of entity
2314                         if (com_token[0] == '_')
2315                                 strcpy(key, com_token + 1);
2316                         else
2317                                 strcpy(key, com_token);
2318                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2319                                 key[strlen(key)-1] = 0;
2320                         if (!COM_ParseToken(&data, false))
2321                                 break; // error
2322                         strcpy(value, com_token);
2323
2324                         // now that we have the key pair worked out...
2325                         if (!strcmp("light", key))
2326                                 light = atof(value);
2327                         else if (!strcmp("origin", key))
2328                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2329                         else if (!strcmp("color", key))
2330                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2331                         else if (!strcmp("wait", key))
2332                                 fadescale = atof(value);
2333                         else if (!strcmp("classname", key))
2334                         {
2335                                 if (!strncmp(value, "light", 5))
2336                                 {
2337                                         islight = true;
2338                                         if (!strcmp(value, "light_fluoro"))
2339                                         {
2340                                                 originhack[0] = 0;
2341                                                 originhack[1] = 0;
2342                                                 originhack[2] = 0;
2343                                                 overridecolor[0] = 1;
2344                                                 overridecolor[1] = 1;
2345                                                 overridecolor[2] = 1;
2346                                         }
2347                                         if (!strcmp(value, "light_fluorospark"))
2348                                         {
2349                                                 originhack[0] = 0;
2350                                                 originhack[1] = 0;
2351                                                 originhack[2] = 0;
2352                                                 overridecolor[0] = 1;
2353                                                 overridecolor[1] = 1;
2354                                                 overridecolor[2] = 1;
2355                                         }
2356                                         if (!strcmp(value, "light_globe"))
2357                                         {
2358                                                 originhack[0] = 0;
2359                                                 originhack[1] = 0;
2360                                                 originhack[2] = 0;
2361                                                 overridecolor[0] = 1;
2362                                                 overridecolor[1] = 0.8;
2363                                                 overridecolor[2] = 0.4;
2364                                         }
2365                                         if (!strcmp(value, "light_flame_large_yellow"))
2366                                         {
2367                                                 originhack[0] = 0;
2368                                                 originhack[1] = 0;
2369                                                 originhack[2] = 48;
2370                                                 overridecolor[0] = 1;
2371                                                 overridecolor[1] = 0.5;
2372                                                 overridecolor[2] = 0.1;
2373                                         }
2374                                         if (!strcmp(value, "light_flame_small_yellow"))
2375                                         {
2376                                                 originhack[0] = 0;
2377                                                 originhack[1] = 0;
2378                                                 originhack[2] = 40;
2379                                                 overridecolor[0] = 1;
2380                                                 overridecolor[1] = 0.5;
2381                                                 overridecolor[2] = 0.1;
2382                                         }
2383                                         if (!strcmp(value, "light_torch_small_white"))
2384                                         {
2385                                                 originhack[0] = 0;
2386                                                 originhack[1] = 0;
2387                                                 originhack[2] = 40;
2388                                                 overridecolor[0] = 1;
2389                                                 overridecolor[1] = 0.5;
2390                                                 overridecolor[2] = 0.1;
2391                                         }
2392                                         if (!strcmp(value, "light_torch_small_walltorch"))
2393                                         {
2394                                                 originhack[0] = 0;
2395                                                 originhack[1] = 0;
2396                                                 originhack[2] = 40;
2397                                                 overridecolor[0] = 1;
2398                                                 overridecolor[1] = 0.5;
2399                                                 overridecolor[2] = 0.1;
2400                                         }
2401                                 }
2402                         }
2403                         else if (!strcmp("style", key))
2404                                 style = atoi(value);
2405                         else if (cl.worldmodel->type == mod_brushq3)
2406                         {
2407                                 if (!strcmp("scale", key))
2408                                         lightscale = atof(value);
2409                                 if (!strcmp("fade", key))
2410                                         fadescale = atof(value);
2411                         }
2412                 }
2413                 if (light <= 0 && islight)
2414                         light = 300;
2415                 if (lightscale <= 0)
2416                         lightscale = 1;
2417                 if (fadescale <= 0)
2418                         fadescale = 1;
2419                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2420                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2421                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2422                         VectorCopy(overridecolor, color);
2423                 VectorScale(color, light, color);
2424                 VectorAdd(origin, originhack, origin);
2425                 if (radius >= 15)
2426                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2427         }
2428 }
2429
2430
2431 void R_Shadow_SetCursorLocationForView(void)
2432 {
2433         vec_t dist, push, frac;
2434         vec3_t dest, endpos, normal;
2435         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2436         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2437         if (frac < 1)
2438         {
2439                 dist = frac * r_editlights_cursordistance.value;
2440                 push = r_editlights_cursorpushback.value;
2441                 if (push > dist)
2442                         push = dist;
2443                 push = -push;
2444                 VectorMA(endpos, push, r_viewforward, endpos);
2445                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2446         }
2447         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2448         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2449         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2450 }
2451
2452 void R_Shadow_UpdateWorldLightSelection(void)
2453 {
2454         if (r_editlights.integer)
2455         {
2456                 R_Shadow_SetCursorLocationForView();
2457                 R_Shadow_SelectLightInView();
2458                 R_Shadow_DrawLightSprites();
2459         }
2460         else
2461                 R_Shadow_SelectLight(NULL);
2462 }
2463
2464 void R_Shadow_EditLights_Clear_f(void)
2465 {
2466         R_Shadow_ClearWorldLights();
2467 }
2468
2469 void R_Shadow_EditLights_Reload_f(void)
2470 {
2471         r_shadow_reloadlights = true;
2472 }
2473
2474 void R_Shadow_EditLights_Save_f(void)
2475 {
2476         if (cl.worldmodel)
2477                 R_Shadow_SaveWorldLights();
2478 }
2479
2480 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2481 {
2482         R_Shadow_ClearWorldLights();
2483         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2484 }
2485
2486 void R_Shadow_EditLights_ImportLightsFile_f(void)
2487 {
2488         R_Shadow_ClearWorldLights();
2489         R_Shadow_LoadLightsFile();
2490 }
2491
2492 void R_Shadow_EditLights_Spawn_f(void)
2493 {
2494         vec3_t color;
2495         if (!r_editlights.integer)
2496         {
2497                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2498                 return;
2499         }
2500         if (Cmd_Argc() != 1)
2501         {
2502                 Con_Printf("r_editlights_spawn does not take parameters\n");
2503                 return;
2504         }
2505         color[0] = color[1] = color[2] = 1;
2506         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2507 }
2508
2509 void R_Shadow_EditLights_Edit_f(void)
2510 {
2511         vec3_t origin, color;
2512         vec_t radius;
2513         int style, shadows;
2514         char cubemapname[1024];
2515         if (!r_editlights.integer)
2516         {
2517                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2518                 return;
2519         }
2520         if (!r_shadow_selectedlight)
2521         {
2522                 Con_Printf("No selected light.\n");
2523                 return;
2524         }
2525         VectorCopy(r_shadow_selectedlight->origin, origin);
2526         radius = r_shadow_selectedlight->lightradius;
2527         VectorCopy(r_shadow_selectedlight->light, color);
2528         style = r_shadow_selectedlight->style;
2529         if (r_shadow_selectedlight->cubemapname)
2530                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2531         else
2532                 cubemapname[0] = 0;
2533         shadows = r_shadow_selectedlight->castshadows;
2534         if (!strcmp(Cmd_Argv(1), "origin"))
2535         {
2536                 if (Cmd_Argc() != 5)
2537                 {
2538                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2539                         return;
2540                 }
2541                 origin[0] = atof(Cmd_Argv(2));
2542                 origin[1] = atof(Cmd_Argv(3));
2543                 origin[2] = atof(Cmd_Argv(4));
2544         }
2545         else if (!strcmp(Cmd_Argv(1), "originx"))
2546         {
2547                 if (Cmd_Argc() != 3)
2548                 {
2549                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2550                         return;
2551                 }
2552                 origin[0] = atof(Cmd_Argv(2));
2553         }
2554         else if (!strcmp(Cmd_Argv(1), "originy"))
2555         {
2556                 if (Cmd_Argc() != 3)
2557                 {
2558                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2559                         return;
2560                 }
2561                 origin[1] = atof(Cmd_Argv(2));
2562         }
2563         else if (!strcmp(Cmd_Argv(1), "originz"))
2564         {
2565                 if (Cmd_Argc() != 3)
2566                 {
2567                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2568                         return;
2569                 }
2570                 origin[2] = atof(Cmd_Argv(2));
2571         }
2572         else if (!strcmp(Cmd_Argv(1), "move"))
2573         {
2574                 if (Cmd_Argc() != 5)
2575                 {
2576                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2577                         return;
2578                 }
2579                 origin[0] += atof(Cmd_Argv(2));
2580                 origin[1] += atof(Cmd_Argv(3));
2581                 origin[2] += atof(Cmd_Argv(4));
2582         }
2583         else if (!strcmp(Cmd_Argv(1), "movex"))
2584         {
2585                 if (Cmd_Argc() != 3)
2586                 {
2587                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2588                         return;
2589                 }
2590                 origin[0] += atof(Cmd_Argv(2));
2591         }
2592         else if (!strcmp(Cmd_Argv(1), "movey"))
2593         {
2594                 if (Cmd_Argc() != 3)
2595                 {
2596                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2597                         return;
2598                 }
2599                 origin[1] += atof(Cmd_Argv(2));
2600         }
2601         else if (!strcmp(Cmd_Argv(1), "movez"))
2602         {
2603                 if (Cmd_Argc() != 3)
2604                 {
2605                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2606                         return;
2607                 }
2608                 origin[2] += atof(Cmd_Argv(2));
2609         }
2610         else if (!strcmp(Cmd_Argv(1), "color"))
2611         {
2612                 if (Cmd_Argc() != 5)
2613                 {
2614                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2615                         return;
2616                 }
2617                 color[0] = atof(Cmd_Argv(2));
2618                 color[1] = atof(Cmd_Argv(3));
2619                 color[2] = atof(Cmd_Argv(4));
2620         }
2621         else if (!strcmp(Cmd_Argv(1), "radius"))
2622         {
2623                 if (Cmd_Argc() != 3)
2624                 {
2625                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2626                         return;
2627                 }
2628                 radius = atof(Cmd_Argv(2));
2629         }
2630         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2631         {
2632                 if (Cmd_Argc() != 3)
2633                 {
2634                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2635                         return;
2636                 }
2637                 style = atoi(Cmd_Argv(2));
2638         }
2639         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2640         {
2641                 if (Cmd_Argc() > 3)
2642                 {
2643                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2644                         return;
2645                 }
2646                 if (Cmd_Argc() == 3)
2647                         strcpy(cubemapname, Cmd_Argv(2));
2648                 else
2649                         cubemapname[0] = 0;
2650         }
2651         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2652         {
2653                 if (Cmd_Argc() != 3)
2654                 {
2655                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2656                         return;
2657                 }
2658                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2659         }
2660         else
2661         {
2662                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2663                 Con_Printf("Selected light's properties:\n");
2664                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2665                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2666                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2667                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2668                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2669                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2670                 return;
2671         }
2672         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2673         r_shadow_selectedlight = NULL;
2674         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2675 }
2676
2677 extern int con_vislines;
2678 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2679 {
2680         float x, y;
2681         char temp[256];
2682         if (r_shadow_selectedlight == NULL)
2683                 return;
2684         x = 0;
2685         y = con_vislines;
2686         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2687         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;
2688         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2689         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;
2690         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2691         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2692         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;
2693 }
2694
2695 void R_Shadow_EditLights_ToggleShadow_f(void)
2696 {
2697         if (!r_editlights.integer)
2698         {
2699                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2700                 return;
2701         }
2702         if (!r_shadow_selectedlight)
2703         {
2704                 Con_Printf("No selected light.\n");
2705                 return;
2706         }
2707         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);
2708         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2709         r_shadow_selectedlight = NULL;
2710 }
2711
2712 void R_Shadow_EditLights_Remove_f(void)
2713 {
2714         if (!r_editlights.integer)
2715         {
2716                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2717                 return;
2718         }
2719         if (!r_shadow_selectedlight)
2720         {
2721                 Con_Printf("No selected light.\n");
2722                 return;
2723         }
2724         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2725         r_shadow_selectedlight = NULL;
2726 }
2727
2728 void R_Shadow_EditLights_Help_f(void)
2729 {
2730         Con_Printf(
2731 "Documentation on r_editlights system:\n"
2732 "Settings:\n"
2733 "r_editlights : enable/disable editing mode\n"
2734 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2735 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2736 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2737 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2738 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2739 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2740 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2741 "Commands:\n"
2742 "r_editlights_help : this help\n"
2743 "r_editlights_clear : remove all lights\n"
2744 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2745 "r_editlights_save : save to .rtlights file\n"
2746 "r_editlights_spawn : create a light with default settings\n"
2747 "r_editlights_edit command : edit selected light - more documentation below\n"
2748 "r_editlights_remove : remove selected light\n"
2749 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2750 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2751 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2752 "Edit commands:\n"
2753 "origin x y z : set light location\n"
2754 "originx x: set x component of light location\n"
2755 "originy y: set y component of light location\n"
2756 "originz z: set z component of light location\n"
2757 "move x y z : adjust light location\n"
2758 "movex x: adjust x component of light location\n"
2759 "movey y: adjust y component of light location\n"
2760 "movez z: adjust z component of light location\n"
2761 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2762 "radius radius : set radius (size) of light\n"
2763 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2764 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2765 "shadows 1/0 : turn on/off shadows\n"
2766 "<nothing> : print light properties to console\n"
2767         );
2768 }
2769
2770 void R_Shadow_EditLights_Init(void)
2771 {
2772         Cvar_RegisterVariable(&r_editlights);
2773         Cvar_RegisterVariable(&r_editlights_cursordistance);
2774         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2775         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2776         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2777         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2778         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2779         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2780         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2781         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2782         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2783         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2784         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2785         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2786         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2787         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2788         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2789         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2790 }