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