]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
Adding Sys_GetClipboardData to the SDL driver.
[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 #include "image.h"
119
120 extern void R_Shadow_EditLights_Init(void);
121
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_STENCILTWOSIDE 3
126
127 int r_shadowstage = SHADOWSTAGE_NONE;
128
129 mempool_t *r_shadow_mempool;
130
131 int maxshadowelements;
132 int *shadowelements;
133
134 int maxshadowmark;
135 int numshadowmark;
136 int *shadowmark;
137 int *shadowmarklist;
138 int shadowmarkcount;
139
140 int maxvertexupdate;
141 int *vertexupdate;
142 int *vertexremap;
143 int vertexupdatenum;
144
145 int r_shadow_buffer_numclusterpvsbytes;
146 qbyte *r_shadow_buffer_clusterpvs;
147 int *r_shadow_buffer_clusterlist;
148
149 int r_shadow_buffer_numsurfacepvsbytes;
150 qbyte *r_shadow_buffer_surfacepvs;
151 int *r_shadow_buffer_surfacelist;
152
153 rtexturepool_t *r_shadow_texturepool;
154 rtexture_t *r_shadow_normalcubetexture;
155 rtexture_t *r_shadow_attenuation2dtexture;
156 rtexture_t *r_shadow_attenuation3dtexture;
157 rtexture_t *r_shadow_blankbumptexture;
158 rtexture_t *r_shadow_blankglosstexture;
159 rtexture_t *r_shadow_blankwhitetexture;
160
161 // lights are reloaded when this changes
162 char r_shadow_mapname[MAX_QPATH];
163
164 // used only for light filters (cubemaps)
165 rtexturepool_t *r_shadow_filters_texturepool;
166
167 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
168 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
169 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
170 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
171 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
172 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
173 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
174 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
175 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
176 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
177 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
178 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
179 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "1"};
180 cvar_t r_shadow_realtime_dlight_shadows = {0, "r_shadow_realtime_dlight_shadows", "0"};
181 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
182 cvar_t r_shadow_realtime_world_dlightshadows = {0, "r_shadow_realtime_world_dlightshadows", "1"};
183 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
184 cvar_t r_shadow_realtime_world_shadows = {0, "r_shadow_realtime_world_shadows", "1"};
185 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
186 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
187 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
188 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
189 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
190 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
191 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
192 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
193 cvar_t r_editlights = {0, "r_editlights", "0"};
194 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
195 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
196 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
197 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
198 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
199 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
200 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
201
202 int c_rt_lights, c_rt_clears, c_rt_scissored;
203 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
204 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
205
206 float r_shadow_attenpower, r_shadow_attenscale;
207
208 rtlight_t *r_shadow_compilingrtlight;
209 dlight_t *r_shadow_worldlightchain;
210 dlight_t *r_shadow_selectedlight;
211 vec3_t r_editlights_cursorlocation;
212
213 rtexture_t *lighttextures[5];
214
215 extern int con_vislines;
216
217 typedef struct cubemapinfo_s
218 {
219         char basename[64];
220         rtexture_t *texture;
221 }
222 cubemapinfo_t;
223
224 #define MAX_CUBEMAPS 256
225 static int numcubemaps;
226 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
227
228 void R_Shadow_UncompileWorldLights(void);
229 void R_Shadow_ClearWorldLights(void);
230 void R_Shadow_SaveWorldLights(void);
231 void R_Shadow_LoadWorldLights(void);
232 void R_Shadow_LoadLightsFile(void);
233 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
234 void R_Shadow_EditLights_Reload_f(void);
235 void R_Shadow_ValidateCvars(void);
236 static void R_Shadow_MakeTextures(void);
237 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
238
239 void r_shadow_start(void)
240 {
241         // allocate vertex processing arrays
242         numcubemaps = 0;
243         r_shadow_normalcubetexture = NULL;
244         r_shadow_attenuation2dtexture = NULL;
245         r_shadow_attenuation3dtexture = NULL;
246         r_shadow_blankbumptexture = NULL;
247         r_shadow_blankglosstexture = NULL;
248         r_shadow_blankwhitetexture = NULL;
249         r_shadow_texturepool = NULL;
250         r_shadow_filters_texturepool = NULL;
251         R_Shadow_ValidateCvars();
252         R_Shadow_MakeTextures();
253         maxshadowelements = 0;
254         shadowelements = NULL;
255         maxvertexupdate = 0;
256         vertexupdate = NULL;
257         vertexremap = NULL;
258         vertexupdatenum = 0;
259         maxshadowmark = 0;
260         numshadowmark = 0;
261         shadowmark = NULL;
262         shadowmarklist = NULL;
263         shadowmarkcount = 0;
264         r_shadow_buffer_numclusterpvsbytes = 0;
265         r_shadow_buffer_clusterpvs = NULL;
266         r_shadow_buffer_clusterlist = NULL;
267         r_shadow_buffer_numsurfacepvsbytes = 0;
268         r_shadow_buffer_surfacepvs = NULL;
269         r_shadow_buffer_surfacelist = NULL;
270 }
271
272 void r_shadow_shutdown(void)
273 {
274         R_Shadow_UncompileWorldLights();
275         numcubemaps = 0;
276         r_shadow_normalcubetexture = NULL;
277         r_shadow_attenuation2dtexture = NULL;
278         r_shadow_attenuation3dtexture = NULL;
279         r_shadow_blankbumptexture = NULL;
280         r_shadow_blankglosstexture = NULL;
281         r_shadow_blankwhitetexture = NULL;
282         R_FreeTexturePool(&r_shadow_texturepool);
283         R_FreeTexturePool(&r_shadow_filters_texturepool);
284         maxshadowelements = 0;
285         if (shadowelements)
286                 Mem_Free(shadowelements);
287         shadowelements = NULL;
288         maxvertexupdate = 0;
289         if (vertexupdate)
290                 Mem_Free(vertexupdate);
291         vertexupdate = NULL;
292         if (vertexremap)
293                 Mem_Free(vertexremap);
294         vertexremap = NULL;
295         vertexupdatenum = 0;
296         maxshadowmark = 0;
297         numshadowmark = 0;
298         if (shadowmark)
299                 Mem_Free(shadowmark);
300         shadowmark = NULL;
301         if (shadowmarklist)
302                 Mem_Free(shadowmarklist);
303         shadowmarklist = NULL;
304         shadowmarkcount = 0;
305         r_shadow_buffer_numclusterpvsbytes = 0;
306         if (r_shadow_buffer_clusterpvs)
307                 Mem_Free(r_shadow_buffer_clusterpvs);
308         r_shadow_buffer_clusterpvs = NULL;
309         if (r_shadow_buffer_clusterlist)
310                 Mem_Free(r_shadow_buffer_clusterlist);
311         r_shadow_buffer_clusterlist = NULL;
312         r_shadow_buffer_numsurfacepvsbytes = 0;
313         if (r_shadow_buffer_surfacepvs)
314                 Mem_Free(r_shadow_buffer_surfacepvs);
315         r_shadow_buffer_surfacepvs = NULL;
316         if (r_shadow_buffer_surfacelist)
317                 Mem_Free(r_shadow_buffer_surfacelist);
318         r_shadow_buffer_surfacelist = NULL;
319 }
320
321 void r_shadow_newmap(void)
322 {
323 }
324
325 void R_Shadow_Help_f(void)
326 {
327         Con_Printf(
328 "Documentation on r_shadow system:\n"
329 "Settings:\n"
330 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
331 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
332 "r_shadow_debuglight : render only this light number (-1 = all)\n"
333 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
334 "r_shadow_gloss2intensity : brightness of forced gloss\n"
335 "r_shadow_glossintensity : brightness of textured gloss\n"
336 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
337 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
338 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
339 "r_shadow_portallight : use portal visibility for static light precomputation\n"
340 "r_shadow_projectdistance : shadow volume projection distance\n"
341 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
342 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
343 "r_shadow_realtime_world : use high quality world lighting mode\n"
344 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
345 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
346 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
347 "r_shadow_scissor : use scissor optimization\n"
348 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
349 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
350 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
351 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
352 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
353 "Commands:\n"
354 "r_shadow_help : this help\n"
355         );
356 }
357
358 void R_Shadow_Init(void)
359 {
360         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
361         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
362         Cvar_RegisterVariable(&r_shadow_cull);
363         Cvar_RegisterVariable(&r_shadow_debuglight);
364         Cvar_RegisterVariable(&r_shadow_gloss);
365         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
366         Cvar_RegisterVariable(&r_shadow_glossintensity);
367         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
368         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
369         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
370         Cvar_RegisterVariable(&r_shadow_portallight);
371         Cvar_RegisterVariable(&r_shadow_projectdistance);
372         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
373         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
374         Cvar_RegisterVariable(&r_shadow_realtime_world);
375         Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
376         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
377         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
378         Cvar_RegisterVariable(&r_shadow_scissor);
379         Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
380         Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
381         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
382         Cvar_RegisterVariable(&r_shadow_staticworldlights);
383         Cvar_RegisterVariable(&r_shadow_texture3d);
384         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
385         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
386         if (gamemode == GAME_TENEBRAE)
387         {
388                 Cvar_SetValue("r_shadow_gloss", 2);
389                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
390         }
391         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
392         R_Shadow_EditLights_Init();
393         r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
394         r_shadow_worldlightchain = NULL;
395         maxshadowelements = 0;
396         shadowelements = NULL;
397         maxvertexupdate = 0;
398         vertexupdate = NULL;
399         vertexremap = NULL;
400         vertexupdatenum = 0;
401         maxshadowmark = 0;
402         numshadowmark = 0;
403         shadowmark = NULL;
404         shadowmarklist = NULL;
405         shadowmarkcount = 0;
406         r_shadow_buffer_numclusterpvsbytes = 0;
407         r_shadow_buffer_clusterpvs = NULL;
408         r_shadow_buffer_clusterlist = NULL;
409         r_shadow_buffer_numsurfacepvsbytes = 0;
410         r_shadow_buffer_surfacepvs = NULL;
411         r_shadow_buffer_surfacelist = NULL;
412         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
413 }
414
415 matrix4x4_t matrix_attenuationxyz =
416 {
417         {
418                 {0.5, 0.0, 0.0, 0.5},
419                 {0.0, 0.5, 0.0, 0.5},
420                 {0.0, 0.0, 0.5, 0.5},
421                 {0.0, 0.0, 0.0, 1.0}
422         }
423 };
424
425 matrix4x4_t matrix_attenuationz =
426 {
427         {
428                 {0.0, 0.0, 0.5, 0.5},
429                 {0.0, 0.0, 0.0, 0.5},
430                 {0.0, 0.0, 0.0, 0.5},
431                 {0.0, 0.0, 0.0, 1.0}
432         }
433 };
434
435 int *R_Shadow_ResizeShadowElements(int numtris)
436 {
437         // make sure shadowelements is big enough for this volume
438         if (maxshadowelements < numtris * 24)
439         {
440                 maxshadowelements = numtris * 24;
441                 if (shadowelements)
442                         Mem_Free(shadowelements);
443                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
444         }
445         return shadowelements;
446 }
447
448 void R_Shadow_EnlargeClusterBuffer(int numclusters)
449 {
450         int numclusterpvsbytes = (((numclusters + 7) >> 3) + 255) & ~255;
451         if (r_shadow_buffer_numclusterpvsbytes < numclusterpvsbytes)
452         {
453                 if (r_shadow_buffer_clusterpvs)
454                         Mem_Free(r_shadow_buffer_clusterpvs);
455                 if (r_shadow_buffer_clusterlist)
456                         Mem_Free(r_shadow_buffer_clusterlist);
457                 r_shadow_buffer_numclusterpvsbytes = numclusterpvsbytes;
458                 r_shadow_buffer_clusterpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes);
459                 r_shadow_buffer_clusterlist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes * 8 * sizeof(*r_shadow_buffer_clusterlist));
460         }
461 }
462
463 void R_Shadow_EnlargeSurfaceBuffer(int numsurfaces)
464 {
465         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
466         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
467         {
468                 if (r_shadow_buffer_surfacepvs)
469                         Mem_Free(r_shadow_buffer_surfacepvs);
470                 if (r_shadow_buffer_surfacelist)
471                         Mem_Free(r_shadow_buffer_surfacelist);
472                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
473                 r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
474                 r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
475         }
476 }
477
478 void R_Shadow_PrepareShadowMark(int numtris)
479 {
480         // make sure shadowmark is big enough for this volume
481         if (maxshadowmark < numtris)
482         {
483                 maxshadowmark = numtris;
484                 if (shadowmark)
485                         Mem_Free(shadowmark);
486                 if (shadowmarklist)
487                         Mem_Free(shadowmarklist);
488                 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
489                 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
490                 shadowmarkcount = 0;
491         }
492         shadowmarkcount++;
493         // if shadowmarkcount wrapped we clear the array and adjust accordingly
494         if (shadowmarkcount == 0)
495         {
496                 shadowmarkcount = 1;
497                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
498         }
499         numshadowmark = 0;
500 }
501
502 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
503 {
504         int i, j, tris = 0, vr[3], t, outvertices = 0;
505         const int *e, *n;
506         float f, temp[3];
507
508         if (maxvertexupdate < innumvertices)
509         {
510                 maxvertexupdate = innumvertices;
511                 if (vertexupdate)
512                         Mem_Free(vertexupdate);
513                 if (vertexremap)
514                         Mem_Free(vertexremap);
515                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
516                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
517                 vertexupdatenum = 0;
518         }
519         vertexupdatenum++;
520         if (vertexupdatenum == 0)
521         {
522                 vertexupdatenum = 1;
523                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
524                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
525         }
526         
527         for (i = 0;i < numshadowmarktris;i++)
528         {
529                 t = shadowmarktris[i];
530                 shadowmark[t] = shadowmarkcount;
531                 e = inelement3i + t * 3;
532                 // make sure the vertices are created
533                 for (j = 0;j < 3;j++)
534                 {
535                         if (vertexupdate[e[j]] != vertexupdatenum)
536                         {
537                                 vertexupdate[e[j]] = vertexupdatenum;
538                                 vertexremap[e[j]] = outvertices;
539                                 VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
540                                 f = projectdistance / VectorLength(temp);
541                                 VectorCopy(invertex3f + e[j] * 3, outvertex3f);
542                                 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
543                                 outvertex3f += 6;
544                                 outvertices += 2;
545                         }
546                 }
547                 // output the front and back triangles
548                 outelement3i[0] = vertexremap[e[0]];
549                 outelement3i[1] = vertexremap[e[1]];
550                 outelement3i[2] = vertexremap[e[2]];
551                 outelement3i[3] = vertexremap[e[2]] + 1;
552                 outelement3i[4] = vertexremap[e[1]] + 1;
553                 outelement3i[5] = vertexremap[e[0]] + 1;
554                 outelement3i += 6;
555                 tris += 2;
556         }
557
558         for (i = 0;i < numshadowmarktris;i++)
559         {
560                 t = shadowmarktris[i];
561                 e = inelement3i + t * 3;
562                 n = inneighbor3i + t * 3;
563                 // output the sides (facing outward from this triangle)
564                 if (shadowmark[n[0]] != shadowmarkcount)
565                 {
566                         vr[0] = vertexremap[e[0]];
567                         vr[1] = vertexremap[e[1]];
568                         outelement3i[0] = vr[1];
569                         outelement3i[1] = vr[0];
570                         outelement3i[2] = vr[0] + 1;
571                         outelement3i[3] = vr[1];
572                         outelement3i[4] = vr[0] + 1;
573                         outelement3i[5] = vr[1] + 1;
574                         outelement3i += 6;
575                         tris += 2;
576                 }
577                 if (shadowmark[n[1]] != shadowmarkcount)
578                 {
579                         vr[1] = vertexremap[e[1]];
580                         vr[2] = vertexremap[e[2]];
581                         outelement3i[0] = vr[2];
582                         outelement3i[1] = vr[1];
583                         outelement3i[2] = vr[1] + 1;
584                         outelement3i[3] = vr[2];
585                         outelement3i[4] = vr[1] + 1;
586                         outelement3i[5] = vr[2] + 1;
587                         outelement3i += 6;
588                         tris += 2;
589                 }
590                 if (shadowmark[n[2]] != shadowmarkcount)
591                 {
592                         vr[0] = vertexremap[e[0]];
593                         vr[2] = vertexremap[e[2]];
594                         outelement3i[0] = vr[0];
595                         outelement3i[1] = vr[2];
596                         outelement3i[2] = vr[2] + 1;
597                         outelement3i[3] = vr[0];
598                         outelement3i[4] = vr[2] + 1;
599                         outelement3i[5] = vr[0] + 1;
600                         outelement3i += 6;
601                         tris += 2;
602                 }
603         }
604         if (outnumvertices)
605                 *outnumvertices = outvertices;
606         return tris;
607 }
608
609 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
610 {
611         int tris, outverts;
612         if (projectdistance < 0.1)
613         {
614                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
615                 return;
616         }
617         if (!numverts || !nummarktris)
618                 return;
619         // make sure shadowelements is big enough for this volume
620         if (maxshadowelements < nummarktris * 24)
621                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
622         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
623         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
624 }
625
626 void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs)
627 {
628         int i;
629         const float *v[3];
630
631         // check which triangles are facing the , and then output
632         // triangle elements and vertices...  by clever use of elements we
633         // can construct the whole shadow from the unprojected vertices and
634         // the projected vertices
635
636         // identify lit faces within the bounding box
637         R_Shadow_PrepareShadowMark(numtris);
638         for (i = 0;i < numtris;i++)
639         {
640                 v[0] = invertex3f + elements[i*3+0] * 3;
641                 v[1] = invertex3f + elements[i*3+1] * 3;
642                 v[2] = invertex3f + elements[i*3+2] * 3;
643                 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2])))
644                         shadowmarklist[numshadowmark++] = i;
645         }
646         R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
647 }
648
649 void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius)
650 {
651         vec3_t mins, maxs;
652         mins[0] = projectorigin[0] - radius;
653         mins[1] = projectorigin[1] - radius;
654         mins[2] = projectorigin[2] - radius;
655         maxs[0] = projectorigin[0] + radius;
656         maxs[1] = projectorigin[1] + radius;
657         maxs[2] = projectorigin[2] + radius;
658         R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
659 }
660
661 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
662 {
663         rmeshstate_t m;
664         if (r_shadow_compilingrtlight)
665         {
666                 // if we're compiling an rtlight, capture the mesh
667                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
668                 return;
669         }
670         memset(&m, 0, sizeof(m));
671         m.pointer_vertex = vertex3f;
672         R_Mesh_State(&m);
673         GL_LockArrays(0, numvertices);
674         if (r_shadowstage == SHADOWSTAGE_STENCIL)
675         {
676                 // increment stencil if backface is behind depthbuffer
677                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
678                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
679                 R_Mesh_Draw(numvertices, numtriangles, element3i);
680                 c_rt_shadowmeshes++;
681                 c_rt_shadowtris += numtriangles;
682                 // decrement stencil if frontface is behind depthbuffer
683                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
684                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
685         }
686         R_Mesh_Draw(numvertices, numtriangles, element3i);
687         c_rt_shadowmeshes++;
688         c_rt_shadowtris += numtriangles;
689         GL_LockArrays(0, 0);
690 }
691
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_ValidateCvars(void)
821 {
822         if (r_shadow_texture3d.integer && !gl_texture3d)
823                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
824         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
825                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
826 }
827
828 void R_Shadow_Stage_Begin(void)
829 {
830         rmeshstate_t m;
831
832         R_Shadow_ValidateCvars();
833
834         if (!r_shadow_attenuation2dtexture
835          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
836          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
837          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
838                 R_Shadow_MakeTextures();
839
840         memset(&m, 0, sizeof(m));
841         GL_BlendFunc(GL_ONE, GL_ZERO);
842         GL_DepthMask(false);
843         GL_DepthTest(true);
844         R_Mesh_State(&m);
845         GL_Color(0, 0, 0, 1);
846         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
847         qglEnable(GL_CULL_FACE);
848         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
849         r_shadowstage = SHADOWSTAGE_NONE;
850
851         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
852         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
853         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
854 }
855
856 void R_Shadow_Stage_ShadowVolumes(void)
857 {
858         rmeshstate_t m;
859         memset(&m, 0, sizeof(m));
860         R_Mesh_State(&m);
861         GL_Color(1, 1, 1, 1);
862         GL_ColorMask(0, 0, 0, 0);
863         GL_BlendFunc(GL_ONE, GL_ZERO);
864         GL_DepthMask(false);
865         GL_DepthTest(true);
866         qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
867         //if (r_shadow_shadow_polygonoffset.value != 0)
868         //{
869         //      qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
870         //      qglEnable(GL_POLYGON_OFFSET_FILL);
871         //}
872         //else
873         //      qglDisable(GL_POLYGON_OFFSET_FILL);
874         qglDepthFunc(GL_LESS);
875         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
876         qglEnable(GL_STENCIL_TEST);
877         qglStencilFunc(GL_ALWAYS, 128, ~0);
878         if (gl_ext_stenciltwoside.integer)
879         {
880                 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
881                 qglDisable(GL_CULL_FACE);
882                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
883                 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
884                 qglStencilMask(~0);
885                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
886                 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
887                 qglStencilMask(~0);
888                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
889         }
890         else
891         {
892                 r_shadowstage = SHADOWSTAGE_STENCIL;
893                 qglEnable(GL_CULL_FACE);
894                 qglStencilMask(~0);
895                 // this is changed by every shadow render so its value here is unimportant
896                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
897         }
898         GL_Clear(GL_STENCIL_BUFFER_BIT);
899         c_rt_clears++;
900         // LordHavoc note: many shadow volumes reside entirely inside the world
901         // (that is to say they are entirely bounded by their lit surfaces),
902         // which can be optimized by handling things as an inverted light volume,
903         // with the shadow boundaries of the world being simulated by an altered
904         // (129) bias to stencil clearing on such lights
905         // FIXME: generate inverted light volumes for use as shadow volumes and
906         // optimize for them as noted above
907 }
908
909 void R_Shadow_Stage_Light(int shadowtest)
910 {
911         rmeshstate_t m;
912         memset(&m, 0, sizeof(m));
913         R_Mesh_State(&m);
914         GL_BlendFunc(GL_ONE, GL_ONE);
915         GL_DepthMask(false);
916         GL_DepthTest(true);
917         qglPolygonOffset(0, 0);
918         //qglDisable(GL_POLYGON_OFFSET_FILL);
919         GL_Color(1, 1, 1, 1);
920         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
921         qglDepthFunc(GL_EQUAL);
922         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
923         qglEnable(GL_CULL_FACE);
924         if (shadowtest)
925                 qglEnable(GL_STENCIL_TEST);
926         else
927                 qglDisable(GL_STENCIL_TEST);
928         if (gl_support_stenciltwoside)
929                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
930         qglStencilMask(~0);
931         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
932         // only draw light where this geometry was already rendered AND the
933         // stencil is 128 (values other than this mean shadow)
934         qglStencilFunc(GL_EQUAL, 128, ~0);
935         r_shadowstage = SHADOWSTAGE_LIGHT;
936         c_rt_lights++;
937 }
938
939 void R_Shadow_Stage_End(void)
940 {
941         rmeshstate_t m;
942         memset(&m, 0, sizeof(m));
943         R_Mesh_State(&m);
944         GL_BlendFunc(GL_ONE, GL_ZERO);
945         GL_DepthMask(true);
946         GL_DepthTest(true);
947         qglPolygonOffset(0, 0);
948         //qglDisable(GL_POLYGON_OFFSET_FILL);
949         GL_Color(1, 1, 1, 1);
950         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
951         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
952         qglDepthFunc(GL_LEQUAL);
953         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
954         qglDisable(GL_STENCIL_TEST);
955         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
956         if (gl_support_stenciltwoside)
957                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
958         qglStencilMask(~0);
959         qglStencilFunc(GL_ALWAYS, 128, ~0);
960         r_shadowstage = SHADOWSTAGE_NONE;
961 }
962
963 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
964 {
965         int i, ix1, iy1, ix2, iy2;
966         float x1, y1, x2, y2, x, y, f;
967         vec3_t smins, smaxs;
968         vec4_t v, v2;
969         if (!r_shadow_scissor.integer)
970                 return false;
971         // if view is inside the box, just say yes it's visible
972         if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
973         {
974                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
975                 return false;
976         }
977         for (i = 0;i < 3;i++)
978         {
979                 if (r_viewforward[i] >= 0)
980                 {
981                         v[i] = mins[i];
982                         v2[i] = maxs[i];
983                 }
984                 else
985                 {
986                         v[i] = maxs[i];
987                         v2[i] = mins[i];
988                 }
989         }
990         f = DotProduct(r_viewforward, r_vieworigin) + 1;
991         if (DotProduct(r_viewforward, v2) <= f)
992         {
993                 // entirely behind nearclip plane
994                 return true;
995         }
996         if (DotProduct(r_viewforward, v) >= f)
997         {
998                 // entirely infront of nearclip plane
999                 x1 = y1 = x2 = y2 = 0;
1000                 for (i = 0;i < 8;i++)
1001                 {
1002                         v[0] = (i & 1) ? mins[0] : maxs[0];
1003                         v[1] = (i & 2) ? mins[1] : maxs[1];
1004                         v[2] = (i & 4) ? mins[2] : maxs[2];
1005                         v[3] = 1.0f;
1006                         GL_TransformToScreen(v, v2);
1007                         //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1008                         x = v2[0];
1009                         y = v2[1];
1010                         if (i)
1011                         {
1012                                 if (x1 > x) x1 = x;
1013                                 if (x2 < x) x2 = x;
1014                                 if (y1 > y) y1 = y;
1015                                 if (y2 < y) y2 = y;
1016                         }
1017                         else
1018                         {
1019                                 x1 = x2 = x;
1020                                 y1 = y2 = y;
1021                         }
1022                 }
1023         }
1024         else
1025         {
1026                 // clipped by nearclip plane
1027                 // this is nasty and crude...
1028                 // create viewspace bbox
1029                 for (i = 0;i < 8;i++)
1030                 {
1031                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1032                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1033                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1034                         v2[0] = -DotProduct(v, r_viewleft);
1035                         v2[1] = DotProduct(v, r_viewup);
1036                         v2[2] = DotProduct(v, r_viewforward);
1037                         if (i)
1038                         {
1039                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1040                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1041                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1042                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1043                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1044                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1045                         }
1046                         else
1047                         {
1048                                 smins[0] = smaxs[0] = v2[0];
1049                                 smins[1] = smaxs[1] = v2[1];
1050                                 smins[2] = smaxs[2] = v2[2];
1051                         }
1052                 }
1053                 // now we have a bbox in viewspace
1054                 // clip it to the view plane
1055                 if (smins[2] < 1)
1056                         smins[2] = 1;
1057                 // return true if that culled the box
1058                 if (smins[2] >= smaxs[2])
1059                         return true;
1060                 // ok some of it is infront of the view, transform each corner back to
1061                 // worldspace and then to screenspace and make screen rect
1062                 // initialize these variables just to avoid compiler warnings
1063                 x1 = y1 = x2 = y2 = 0;
1064                 for (i = 0;i < 8;i++)
1065                 {
1066                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1067                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1068                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1069                         v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1070                         v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1071                         v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1072                         v[3] = 1.0f;
1073                         GL_TransformToScreen(v, v2);
1074                         //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1075                         x = v2[0];
1076                         y = v2[1];
1077                         if (i)
1078                         {
1079                                 if (x1 > x) x1 = x;
1080                                 if (x2 < x) x2 = x;
1081                                 if (y1 > y) y1 = y;
1082                                 if (y2 < y) y2 = y;
1083                         }
1084                         else
1085                         {
1086                                 x1 = x2 = x;
1087                                 y1 = y2 = y;
1088                         }
1089                 }
1090                 /*
1091                 // this code doesn't handle boxes with any points behind view properly
1092                 x1 = 1000;x2 = -1000;
1093                 y1 = 1000;y2 = -1000;
1094                 for (i = 0;i < 8;i++)
1095                 {
1096                         v[0] = (i & 1) ? mins[0] : maxs[0];
1097                         v[1] = (i & 2) ? mins[1] : maxs[1];
1098                         v[2] = (i & 4) ? mins[2] : maxs[2];
1099                         v[3] = 1.0f;
1100                         GL_TransformToScreen(v, v2);
1101                         //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1102                         if (v2[2] > 0)
1103                         {
1104                                 x = v2[0];
1105                                 y = v2[1];
1106
1107                                 if (x1 > x) x1 = x;
1108                                 if (x2 < x) x2 = x;
1109                                 if (y1 > y) y1 = y;
1110                                 if (y2 < y) y2 = y;
1111                         }
1112                 }
1113                 */
1114         }
1115         ix1 = x1 - 1.0f;
1116         iy1 = y1 - 1.0f;
1117         ix2 = x2 + 1.0f;
1118         iy2 = y2 + 1.0f;
1119         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1120         if (ix1 < r_view_x) ix1 = r_view_x;
1121         if (iy1 < r_view_y) iy1 = r_view_y;
1122         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1123         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1124         if (ix2 <= ix1 || iy2 <= iy1)
1125                 return true;
1126         // set up the scissor rectangle
1127         GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1128         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1129         //qglEnable(GL_SCISSOR_TEST);
1130         c_rt_scissored++;
1131         return false;
1132 }
1133
1134 static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1135 {
1136         float *color4f = varray_color4f;
1137         float dist, dot, intensity, v[3], n[3];
1138         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1139         {
1140                 Matrix4x4_Transform(m, vertex3f, v);
1141                 if ((dist = DotProduct(v, v)) < 1)
1142                 {
1143                         Matrix4x4_Transform3x3(m, normal3f, n);
1144                         if ((dot = DotProduct(n, v)) > 0)
1145                         {
1146                                 dist = sqrt(dist);
1147                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1148                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1149                                 VectorScale(lightcolor, intensity, color4f);
1150                                 color4f[3] = 1;
1151                         }
1152                         else
1153                         {
1154                                 VectorClear(color4f);
1155                                 color4f[3] = 1;
1156                         }
1157                 }
1158                 else
1159                 {
1160                         VectorClear(color4f);
1161                         color4f[3] = 1;
1162                 }
1163         }
1164 }
1165
1166 static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1167 {
1168         float *color4f = varray_color4f;
1169         float dist, dot, intensity, v[3], n[3];
1170         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1171         {
1172                 Matrix4x4_Transform(m, vertex3f, v);
1173                 if ((dist = fabs(v[2])) < 1)
1174                 {
1175                         Matrix4x4_Transform3x3(m, normal3f, n);
1176                         if ((dot = DotProduct(n, v)) > 0)
1177                         {
1178                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1179                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
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 static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1198 {
1199         float *color4f = varray_color4f;
1200         float dot, intensity, v[3], n[3];
1201         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1202         {
1203                 Matrix4x4_Transform(m, vertex3f, v);
1204                 Matrix4x4_Transform3x3(m, normal3f, n);
1205                 if ((dot = DotProduct(n, v)) > 0)
1206                 {
1207                         intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1208                         VectorScale(lightcolor, intensity, color4f);
1209                         color4f[3] = 1;
1210                 }
1211                 else
1212                 {
1213                         VectorClear(color4f);
1214                         color4f[3] = 1;
1215                 }
1216         }
1217 }
1218
1219 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1220 #define USETEXMATRIX
1221
1222 #ifndef USETEXMATRIX
1223 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1224 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1225 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1226 {
1227         do
1228         {
1229                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1230                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1231                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1232                 vertex3f += 3;
1233                 tc3f += 3;
1234         }
1235         while (--numverts);
1236 }
1237
1238 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1239 {
1240         do
1241         {
1242                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1243                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1244                 vertex3f += 3;
1245                 tc2f += 2;
1246         }
1247         while (--numverts);
1248 }
1249 #endif
1250
1251 static 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)
1252 {
1253         int i;
1254         float lightdir[3];
1255         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1256         {
1257                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1258                 // the cubemap normalizes this for us
1259                 out3f[0] = DotProduct(svector3f, lightdir);
1260                 out3f[1] = DotProduct(tvector3f, lightdir);
1261                 out3f[2] = DotProduct(normal3f, lightdir);
1262         }
1263 }
1264
1265 static 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)
1266 {
1267         int i;
1268         float lightdir[3], eyedir[3], halfdir[3];
1269         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1270         {
1271                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1272                 VectorNormalizeFast(lightdir);
1273                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1274                 VectorNormalizeFast(eyedir);
1275                 VectorAdd(lightdir, eyedir, halfdir);
1276                 // the cubemap normalizes this for us
1277                 out3f[0] = DotProduct(svector3f, halfdir);
1278                 out3f[1] = DotProduct(tvector3f, halfdir);
1279                 out3f[2] = DotProduct(normal3f, halfdir);
1280         }
1281 }
1282
1283 void R_Shadow_RenderLighting(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, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, int lighting)
1284 {
1285         int renders;
1286         float color[3], color2[3], colorscale;
1287         rmeshstate_t m;
1288         if (!bumptexture)
1289                 bumptexture = r_shadow_blankbumptexture;
1290         if (!glosstexture)
1291                 glosstexture = r_shadow_blankglosstexture;
1292         GL_DepthMask(false);
1293         GL_DepthTest(true);
1294         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1295         {
1296                 if (lighting & LIGHTING_DIFFUSE)
1297                 {
1298                         GL_Color(1,1,1,1);
1299                         colorscale = r_shadow_lightintensityscale.value;
1300                         // colorscale accounts for how much we multiply the brightness
1301                         // during combine.
1302                         //
1303                         // mult is how many times the final pass of the lighting will be
1304                         // performed to get more brightness than otherwise possible.
1305                         //
1306                         // Limit mult to 64 for sanity sake.
1307                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1308                         {
1309                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1310                                 memset(&m, 0, sizeof(m));
1311                                 m.pointer_vertex = vertex3f;
1312                                 m.tex[0] = R_GetTexture(bumptexture);
1313                                 m.texcombinergb[0] = GL_REPLACE;
1314                                 m.pointer_texcoord[0] = texcoord2f;
1315                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1316                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1317                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1318                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1319                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1320 #ifdef USETEXMATRIX
1321                                 m.pointer_texcoord3f[2] = vertex3f;
1322                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1323 #else
1324                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1325                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1326 #endif
1327                                 R_Mesh_State(&m);
1328                                 GL_ColorMask(0,0,0,1);
1329                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1330                                 GL_LockArrays(0, numverts);
1331                                 R_Mesh_Draw(numverts, numtriangles, elements);
1332                                 GL_LockArrays(0, 0);
1333                                 c_rt_lightmeshes++;
1334                                 c_rt_lighttris += numtriangles;
1335         
1336                                 memset(&m, 0, sizeof(m));
1337                                 m.pointer_vertex = vertex3f;
1338                                 m.tex[0] = R_GetTexture(basetexture);
1339                                 m.pointer_texcoord[0] = texcoord2f;
1340                                 if (lightcubemap)
1341                                 {
1342                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1343 #ifdef USETEXMATRIX
1344                                         m.pointer_texcoord3f[1] = vertex3f;
1345                                         m.texmatrix[1] = *matrix_modeltolight;
1346 #else
1347                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1348                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1349 #endif
1350                                 }
1351                         }
1352                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1353                         {
1354                                 // 1/2/2 3D combine path (original Radeon)
1355                                 memset(&m, 0, sizeof(m));
1356                                 m.pointer_vertex = vertex3f;
1357                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1358 #ifdef USETEXMATRIX
1359                                 m.pointer_texcoord3f[0] = vertex3f;
1360                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1361 #else
1362                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1363                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1364 #endif
1365                                 R_Mesh_State(&m);
1366                                 GL_ColorMask(0,0,0,1);
1367                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1368                                 GL_LockArrays(0, numverts);
1369                                 R_Mesh_Draw(numverts, numtriangles, elements);
1370                                 GL_LockArrays(0, 0);
1371                                 c_rt_lightmeshes++;
1372                                 c_rt_lighttris += numtriangles;
1373         
1374                                 memset(&m, 0, sizeof(m));
1375                                 m.pointer_vertex = vertex3f;
1376                                 m.tex[0] = R_GetTexture(bumptexture);
1377                                 m.texcombinergb[0] = GL_REPLACE;
1378                                 m.pointer_texcoord[0] = texcoord2f;
1379                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1380                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1381                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1382                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1383                                 R_Mesh_State(&m);
1384                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1385                                 GL_LockArrays(0, numverts);
1386                                 R_Mesh_Draw(numverts, numtriangles, elements);
1387                                 GL_LockArrays(0, 0);
1388                                 c_rt_lightmeshes++;
1389                                 c_rt_lighttris += numtriangles;
1390         
1391                                 memset(&m, 0, sizeof(m));
1392                                 m.pointer_vertex = vertex3f;
1393                                 m.tex[0] = R_GetTexture(basetexture);
1394                                 m.pointer_texcoord[0] = texcoord2f;
1395                                 if (lightcubemap)
1396                                 {
1397                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1398 #ifdef USETEXMATRIX
1399                                         m.pointer_texcoord3f[1] = vertex3f;
1400                                         m.texmatrix[1] = *matrix_modeltolight;
1401 #else
1402                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1403                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1404 #endif
1405                                 }
1406                         }
1407                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1408                         {
1409                                 // 2/2 3D combine path (original Radeon)
1410                                 memset(&m, 0, sizeof(m));
1411                                 m.pointer_vertex = vertex3f;
1412                                 m.tex[0] = R_GetTexture(bumptexture);
1413                                 m.texcombinergb[0] = GL_REPLACE;
1414                                 m.pointer_texcoord[0] = texcoord2f;
1415                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1416                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1417                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1418                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1419                                 R_Mesh_State(&m);
1420                                 GL_ColorMask(0,0,0,1);
1421                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1422                                 GL_LockArrays(0, numverts);
1423                                 R_Mesh_Draw(numverts, numtriangles, elements);
1424                                 GL_LockArrays(0, 0);
1425                                 c_rt_lightmeshes++;
1426                                 c_rt_lighttris += numtriangles;
1427         
1428                                 memset(&m, 0, sizeof(m));
1429                                 m.pointer_vertex = vertex3f;
1430                                 m.tex[0] = R_GetTexture(basetexture);
1431                                 m.pointer_texcoord[0] = texcoord2f;
1432                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1433 #ifdef USETEXMATRIX
1434                                 m.pointer_texcoord3f[1] = vertex3f;
1435                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1436 #else
1437                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1438                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1439 #endif
1440                         }
1441                         else if (r_textureunits.integer >= 4)
1442                         {
1443                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1444                                 memset(&m, 0, sizeof(m));
1445                                 m.pointer_vertex = vertex3f;
1446                                 m.tex[0] = R_GetTexture(bumptexture);
1447                                 m.texcombinergb[0] = GL_REPLACE;
1448                                 m.pointer_texcoord[0] = texcoord2f;
1449                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1450                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1451                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1452                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1453                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1454 #ifdef USETEXMATRIX
1455                                 m.pointer_texcoord3f[2] = vertex3f;
1456                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1457 #else
1458                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
1459                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1460 #endif
1461                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1462 #ifdef USETEXMATRIX
1463                                 m.pointer_texcoord3f[3] = vertex3f;
1464                                 m.texmatrix[3] = *matrix_modeltoattenuationz;
1465 #else
1466                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
1467                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1468 #endif
1469                                 R_Mesh_State(&m);
1470                                 GL_ColorMask(0,0,0,1);
1471                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1472                                 GL_LockArrays(0, numverts);
1473                                 R_Mesh_Draw(numverts, numtriangles, elements);
1474                                 GL_LockArrays(0, 0);
1475                                 c_rt_lightmeshes++;
1476                                 c_rt_lighttris += numtriangles;
1477         
1478                                 memset(&m, 0, sizeof(m));
1479                                 m.pointer_vertex = vertex3f;
1480                                 m.tex[0] = R_GetTexture(basetexture);
1481                                 m.pointer_texcoord[0] = texcoord2f;
1482                                 if (lightcubemap)
1483                                 {
1484                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1485 #ifdef USETEXMATRIX
1486                                         m.pointer_texcoord3f[1] = vertex3f;
1487                                         m.texmatrix[1] = *matrix_modeltolight;
1488 #else
1489                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1490                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1491 #endif
1492                                 }
1493                         }
1494                         else
1495                         {
1496                                 // 2/2/2 2D combine path (any dot3 card)
1497                                 memset(&m, 0, sizeof(m));
1498                                 m.pointer_vertex = vertex3f;
1499                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1500 #ifdef USETEXMATRIX
1501                                 m.pointer_texcoord3f[0] = vertex3f;
1502                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1503 #else
1504                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1505                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1506 #endif
1507                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1508 #ifdef USETEXMATRIX
1509                                 m.pointer_texcoord3f[1] = vertex3f;
1510                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1511 #else
1512                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1513                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1514 #endif
1515                                 R_Mesh_State(&m);
1516                                 GL_ColorMask(0,0,0,1);
1517                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1518                                 GL_LockArrays(0, numverts);
1519                                 R_Mesh_Draw(numverts, numtriangles, elements);
1520                                 GL_LockArrays(0, 0);
1521                                 c_rt_lightmeshes++;
1522                                 c_rt_lighttris += numtriangles;
1523         
1524                                 memset(&m, 0, sizeof(m));
1525                                 m.pointer_vertex = vertex3f;
1526                                 m.tex[0] = R_GetTexture(bumptexture);
1527                                 m.texcombinergb[0] = GL_REPLACE;
1528                                 m.pointer_texcoord[0] = texcoord2f;
1529                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1530                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1531                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1532                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1533                                 R_Mesh_State(&m);
1534                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1535                                 GL_LockArrays(0, numverts);
1536                                 R_Mesh_Draw(numverts, numtriangles, elements);
1537                                 GL_LockArrays(0, 0);
1538                                 c_rt_lightmeshes++;
1539                                 c_rt_lighttris += numtriangles;
1540         
1541                                 memset(&m, 0, sizeof(m));
1542                                 m.pointer_vertex = vertex3f;
1543                                 m.tex[0] = R_GetTexture(basetexture);
1544                                 m.pointer_texcoord[0] = texcoord2f;
1545                                 if (lightcubemap)
1546                                 {
1547                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1548 #ifdef USETEXMATRIX
1549                                         m.pointer_texcoord3f[1] = vertex3f;
1550                                         m.texmatrix[1] = *matrix_modeltolight;
1551 #else
1552                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1553                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1554 #endif
1555                                 }
1556                         }
1557                         // this final code is shared
1558                         R_Mesh_State(&m);
1559                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1560                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1561                         VectorScale(lightcolor, colorscale, color2);
1562                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1563                         {
1564                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1565                                 GL_LockArrays(0, numverts);
1566                                 R_Mesh_Draw(numverts, numtriangles, elements);
1567                                 GL_LockArrays(0, 0);
1568                                 c_rt_lightmeshes++;
1569                                 c_rt_lighttris += numtriangles;
1570                         }
1571                 }
1572                 if ((lighting & LIGHTING_SPECULAR) && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1573                 {
1574                         // FIXME: detect blendsquare!
1575                         //if (gl_support_blendsquare)
1576                         {
1577                                 colorscale = r_shadow_lightintensityscale.value * r_shadow_glossintensity.value;
1578                                 if (glosstexture == r_shadow_blankglosstexture)
1579                                         colorscale *= r_shadow_gloss2intensity.value;
1580                                 GL_Color(1,1,1,1);
1581                                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1582                                 {
1583                                         // 2/0/0/1/2 3D combine blendsquare path
1584                                         memset(&m, 0, sizeof(m));
1585                                         m.pointer_vertex = vertex3f;
1586                                         m.tex[0] = R_GetTexture(bumptexture);
1587                                         m.pointer_texcoord[0] = texcoord2f;
1588                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1589                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1590                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1591                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1592                                         R_Mesh_State(&m);
1593                                         GL_ColorMask(0,0,0,1);
1594                                         // this squares the result
1595                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1596                                         GL_LockArrays(0, numverts);
1597                                         R_Mesh_Draw(numverts, numtriangles, elements);
1598                                         GL_LockArrays(0, 0);
1599                                         c_rt_lightmeshes++;
1600                                         c_rt_lighttris += numtriangles;
1601                 
1602                                         memset(&m, 0, sizeof(m));
1603                                         m.pointer_vertex = vertex3f;
1604                                         R_Mesh_State(&m);
1605                                         GL_LockArrays(0, numverts);
1606                                         // square alpha in framebuffer a few times to make it shiny
1607                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1608                                         // these comments are a test run through this math for intensity 0.5
1609                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1610                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1611                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1612                                         R_Mesh_Draw(numverts, numtriangles, elements);
1613                                         c_rt_lightmeshes++;
1614                                         c_rt_lighttris += numtriangles;
1615                                         R_Mesh_Draw(numverts, numtriangles, elements);
1616                                         c_rt_lightmeshes++;
1617                                         c_rt_lighttris += numtriangles;
1618                                         GL_LockArrays(0, 0);
1619                 
1620                                         memset(&m, 0, sizeof(m));
1621                                         m.pointer_vertex = vertex3f;
1622                                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1623 #ifdef USETEXMATRIX
1624                                         m.pointer_texcoord3f[0] = vertex3f;
1625                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1626 #else
1627                                         m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1628                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1629 #endif
1630                                         R_Mesh_State(&m);
1631                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1632                                         GL_LockArrays(0, numverts);
1633                                         R_Mesh_Draw(numverts, numtriangles, elements);
1634                                         GL_LockArrays(0, 0);
1635                                         c_rt_lightmeshes++;
1636                                         c_rt_lighttris += numtriangles;
1637                 
1638                                         memset(&m, 0, sizeof(m));
1639                                         m.pointer_vertex = vertex3f;
1640                                         m.tex[0] = R_GetTexture(glosstexture);
1641                                         m.pointer_texcoord[0] = texcoord2f;
1642                                         if (lightcubemap)
1643                                         {
1644                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1645 #ifdef USETEXMATRIX
1646                                                 m.pointer_texcoord3f[1] = vertex3f;
1647                                                 m.texmatrix[1] = *matrix_modeltolight;
1648 #else
1649                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1650                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1651 #endif
1652                                         }
1653                                 }
1654                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1655                                 {
1656                                         // 2/0/0/2 3D combine blendsquare path
1657                                         memset(&m, 0, sizeof(m));
1658                                         m.pointer_vertex = vertex3f;
1659                                         m.tex[0] = R_GetTexture(bumptexture);
1660                                         m.pointer_texcoord[0] = texcoord2f;
1661                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1662                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1663                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1664                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1665                                         R_Mesh_State(&m);
1666                                         GL_ColorMask(0,0,0,1);
1667                                         // this squares the result
1668                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1669                                         GL_LockArrays(0, numverts);
1670                                         R_Mesh_Draw(numverts, numtriangles, elements);
1671                                         GL_LockArrays(0, 0);
1672                                         c_rt_lightmeshes++;
1673                                         c_rt_lighttris += numtriangles;
1674                 
1675                                         memset(&m, 0, sizeof(m));
1676                                         m.pointer_vertex = vertex3f;
1677                                         R_Mesh_State(&m);
1678                                         GL_LockArrays(0, numverts);
1679                                         // square alpha in framebuffer a few times to make it shiny
1680                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1681                                         // these comments are a test run through this math for intensity 0.5
1682                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1683                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1684                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1685                                         R_Mesh_Draw(numverts, numtriangles, elements);
1686                                         c_rt_lightmeshes++;
1687                                         c_rt_lighttris += numtriangles;
1688                                         R_Mesh_Draw(numverts, numtriangles, elements);
1689                                         c_rt_lightmeshes++;
1690                                         c_rt_lighttris += numtriangles;
1691                                         GL_LockArrays(0, 0);
1692                 
1693                                         memset(&m, 0, sizeof(m));
1694                                         m.pointer_vertex = vertex3f;
1695                                         m.tex[0] = R_GetTexture(glosstexture);
1696                                         m.pointer_texcoord[0] = texcoord2f;
1697                                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1698 #ifdef USETEXMATRIX
1699                                         m.pointer_texcoord3f[1] = vertex3f;
1700                                         m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1701 #else
1702                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1703                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1704 #endif
1705                                 }
1706                                 else
1707                                 {
1708                                         // 2/0/0/2/2 2D combine blendsquare path
1709                                         memset(&m, 0, sizeof(m));
1710                                         m.pointer_vertex = vertex3f;
1711                                         m.tex[0] = R_GetTexture(bumptexture);
1712                                         m.pointer_texcoord[0] = texcoord2f;
1713                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1714                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1715                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1716                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1717                                         R_Mesh_State(&m);
1718                                         GL_ColorMask(0,0,0,1);
1719                                         // this squares the result
1720                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1721                                         GL_LockArrays(0, numverts);
1722                                         R_Mesh_Draw(numverts, numtriangles, elements);
1723                                         GL_LockArrays(0, 0);
1724                                         c_rt_lightmeshes++;
1725                                         c_rt_lighttris += numtriangles;
1726                 
1727                                         memset(&m, 0, sizeof(m));
1728                                         m.pointer_vertex = vertex3f;
1729                                         R_Mesh_State(&m);
1730                                         GL_LockArrays(0, numverts);
1731                                         // square alpha in framebuffer a few times to make it shiny
1732                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1733                                         // these comments are a test run through this math for intensity 0.5
1734                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1735                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1736                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1737                                         R_Mesh_Draw(numverts, numtriangles, elements);
1738                                         c_rt_lightmeshes++;
1739                                         c_rt_lighttris += numtriangles;
1740                                         R_Mesh_Draw(numverts, numtriangles, elements);
1741                                         c_rt_lightmeshes++;
1742                                         c_rt_lighttris += numtriangles;
1743                                         GL_LockArrays(0, 0);
1744                 
1745                                         memset(&m, 0, sizeof(m));
1746                                         m.pointer_vertex = vertex3f;
1747                                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1748 #ifdef USETEXMATRIX
1749                                         m.pointer_texcoord3f[0] = vertex3f;
1750                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1751 #else
1752                                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1753                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1754 #endif
1755                                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1756 #ifdef USETEXMATRIX
1757                                         m.pointer_texcoord3f[1] = vertex3f;
1758                                         m.texmatrix[1] = *matrix_modeltoattenuationz;
1759 #else
1760                                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1761                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1762 #endif
1763                                         R_Mesh_State(&m);
1764                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1765                                         GL_LockArrays(0, numverts);
1766                                         R_Mesh_Draw(numverts, numtriangles, elements);
1767                                         GL_LockArrays(0, 0);
1768                                         c_rt_lightmeshes++;
1769                                         c_rt_lighttris += numtriangles;
1770                 
1771                                         memset(&m, 0, sizeof(m));
1772                                         m.pointer_vertex = vertex3f;
1773                                         m.tex[0] = R_GetTexture(glosstexture);
1774                                         m.pointer_texcoord[0] = texcoord2f;
1775                                         if (lightcubemap)
1776                                         {
1777                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1778 #ifdef USETEXMATRIX
1779                                                 m.pointer_texcoord3f[1] = vertex3f;
1780                                                 m.texmatrix[1] = *matrix_modeltolight;
1781 #else
1782                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1783                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1784 #endif
1785                                         }
1786                                 }
1787                         }
1788                         R_Mesh_State(&m);
1789                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1790                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1791                         VectorScale(lightcolor, colorscale, color2);
1792                         GL_LockArrays(0, numverts);
1793                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1794                         {
1795                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1796                                 R_Mesh_Draw(numverts, numtriangles, elements);
1797                                 c_rt_lightmeshes++;
1798                                 c_rt_lighttris += numtriangles;
1799                         }
1800                         GL_LockArrays(0, 0);
1801                 }
1802         }
1803         else
1804         {
1805                 if (lighting & LIGHTING_DIFFUSE)
1806                 {
1807                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1808                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1809                         memset(&m, 0, sizeof(m));
1810                         m.pointer_vertex = vertex3f;
1811                         m.pointer_color = varray_color4f;
1812                         m.tex[0] = R_GetTexture(basetexture);
1813                         m.pointer_texcoord[0] = texcoord2f;
1814                         if (r_textureunits.integer >= 2)
1815                         {
1816                                 // voodoo2
1817                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1818 #ifdef USETEXMATRIX
1819                                 m.pointer_texcoord3f[1] = vertex3f;
1820                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1821 #else
1822                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1823                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1824 #endif
1825                                 if (r_textureunits.integer >= 3)
1826                                 {
1827                                         // Geforce3/Radeon class but not using dot3
1828                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1829 #ifdef USETEXMATRIX
1830                                         m.pointer_texcoord3f[2] = vertex3f;
1831                                         m.texmatrix[2] = *matrix_modeltoattenuationz;
1832 #else
1833                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1834                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
1835 #endif
1836                                 }
1837                         }
1838                         R_Mesh_State(&m);
1839                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1840                         {
1841                                 color[0] = bound(0, color2[0], 1);
1842                                 color[1] = bound(0, color2[1], 1);
1843                                 color[2] = bound(0, color2[2], 1);
1844                                 if (r_textureunits.integer >= 3)
1845                                         R_Shadow_VertexShading(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1846                                 else if (r_textureunits.integer >= 2)
1847                                         R_Shadow_VertexShadingWithZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1848                                 else
1849                                         R_Shadow_VertexShadingWithXYZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1850                                 GL_LockArrays(0, numverts);
1851                                 R_Mesh_Draw(numverts, numtriangles, elements);
1852                                 GL_LockArrays(0, 0);
1853                                 c_rt_lightmeshes++;
1854                                 c_rt_lighttris += numtriangles;
1855                         }
1856                 }
1857         }
1858 }
1859
1860 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1861 {
1862         int j, k;
1863         float scale;
1864         R_RTLight_Uncompile(rtlight);
1865         memset(rtlight, 0, sizeof(*rtlight));
1866
1867         VectorCopy(light->origin, rtlight->shadoworigin);
1868         VectorCopy(light->color, rtlight->color);
1869         rtlight->radius = light->radius;
1870         //rtlight->cullradius = rtlight->radius;
1871         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
1872         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1873         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1874         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1875         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1876         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1877         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1878         rtlight->cubemapname[0] = 0;
1879         if (light->cubemapname[0])
1880                 strcpy(rtlight->cubemapname, light->cubemapname);
1881         else if (light->cubemapnum > 0)
1882                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1883         rtlight->shadow = light->shadow;
1884         rtlight->corona = light->corona;
1885         rtlight->style = light->style;
1886         rtlight->isstatic = isstatic;
1887         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1888         // ConcatScale won't work here because this needs to scale rotate and
1889         // translate, not just rotate
1890         scale = 1.0f / rtlight->radius;
1891         for (k = 0;k < 3;k++)
1892                 for (j = 0;j < 4;j++)
1893                         rtlight->matrix_worldtolight.m[k][j] *= scale;
1894         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1895         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1896
1897         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1898         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1899         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.125f, rtlight->lightmap_light);
1900         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1901 }
1902
1903 // compiles rtlight geometry
1904 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1905 void R_RTLight_Compile(rtlight_t *rtlight)
1906 {
1907         int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
1908         entity_render_t *ent = &cl_entities[0].render;
1909         model_t *model = ent->model;
1910
1911         // compile the light
1912         rtlight->compiled = true;
1913         rtlight->static_numclusters = 0;
1914         rtlight->static_numclusterpvsbytes = 0;
1915         rtlight->static_clusterlist = NULL;
1916         rtlight->static_clusterpvs = NULL;
1917         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1918         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1919         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1920         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1921         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1922         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1923
1924         if (model && model->GetLightInfo)
1925         {
1926                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
1927                 r_shadow_compilingrtlight = rtlight;
1928                 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
1929                 R_Shadow_EnlargeSurfaceBuffer(model->nummodelsurfaces); 
1930                 model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
1931                 if (numclusters)
1932                 {
1933                         rtlight->static_numclusters = numclusters;
1934                         rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
1935                         rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1936                         rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
1937                         memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1938                         memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
1939                 }
1940                 if (model->DrawShadowVolume && rtlight->shadow)
1941                 {
1942                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1943                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
1944                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1945                 }
1946                 if (model->DrawLight)
1947                 {
1948                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1949                         model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, numsurfaces, r_shadow_buffer_surfacelist);
1950                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1951                 }
1952                 // switch back to rendering when DrawShadowVolume or DrawLight is called
1953                 r_shadow_compilingrtlight = NULL;
1954         }
1955
1956
1957         // use smallest available cullradius - box radius or light radius
1958         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1959         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1960
1961         shadowmeshes = 0;
1962         shadowtris = 0;
1963         if (rtlight->static_meshchain_shadow)
1964         {
1965                 shadowmesh_t *mesh;
1966                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1967                 {
1968                         shadowmeshes++;
1969                         shadowtris += mesh->numtriangles;
1970                 }
1971         }
1972
1973         lightmeshes = 0;
1974         lighttris = 0;
1975         if (rtlight->static_meshchain_light)
1976         {
1977                 shadowmesh_t *mesh;
1978                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
1979                 {
1980                         lightmeshes++;
1981                         lighttris += mesh->numtriangles;
1982                 }
1983         }
1984
1985         Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
1986 }
1987
1988 void R_RTLight_Uncompile(rtlight_t *rtlight)
1989 {
1990         if (rtlight->compiled)
1991         {
1992                 if (rtlight->static_meshchain_shadow)
1993                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
1994                 rtlight->static_meshchain_shadow = NULL;
1995                 if (rtlight->static_meshchain_light)
1996                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
1997                 rtlight->static_meshchain_light = NULL;
1998                 if (rtlight->static_clusterlist)
1999                         Mem_Free(rtlight->static_clusterlist);
2000                 rtlight->static_clusterlist = NULL;
2001                 if (rtlight->static_clusterpvs)
2002                         Mem_Free(rtlight->static_clusterpvs);
2003                 rtlight->static_clusterpvs = NULL;
2004                 rtlight->static_numclusters = 0;
2005                 rtlight->static_numclusterpvsbytes = 0;
2006                 rtlight->compiled = false;
2007         }
2008 }
2009
2010 void R_Shadow_UncompileWorldLights(void)
2011 {
2012         dlight_t *light;
2013         for (light = r_shadow_worldlightchain;light;light = light->next)
2014                 R_RTLight_Uncompile(&light->rtlight);
2015 }
2016
2017 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2018 {
2019         int i, shadow;
2020         entity_render_t *ent;
2021         float f;
2022         vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2023         rtexture_t *cubemaptexture;
2024         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2025         int numclusters, numsurfaces;
2026         int *clusterlist, *surfacelist;
2027         qbyte *clusterpvs;
2028         vec3_t cullmins, cullmaxs;
2029         shadowmesh_t *mesh;
2030         rmeshstate_t m;
2031
2032         // loading is done before visibility checks because loading should happen
2033         // all at once at the start of a level, not when it stalls gameplay.
2034         // (especially important to benchmarks)
2035         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2036                 R_RTLight_Compile(rtlight);
2037         if (rtlight->cubemapname[0])
2038                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2039         else
2040                 cubemaptexture = NULL;
2041
2042         cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2043         cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2044         cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2045         cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2046         cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2047         cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2048         if (d_lightstylevalue[rtlight->style] <= 0)
2049                 return;
2050         numclusters = 0;
2051         clusterlist = NULL;
2052         clusterpvs = NULL;
2053         numsurfaces = 0;
2054         surfacelist = NULL;
2055         if (rtlight->compiled && r_shadow_staticworldlights.integer)
2056         {
2057                 // compiled light, world available and can receive realtime lighting
2058                 // retrieve cluster information
2059                 numclusters = rtlight->static_numclusters;
2060                 clusterlist = rtlight->static_clusterlist;
2061                 clusterpvs = rtlight->static_clusterpvs;
2062                 VectorCopy(rtlight->cullmins, cullmins);
2063                 VectorCopy(rtlight->cullmaxs, cullmaxs);
2064         }
2065         else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
2066         {
2067                 // dynamic light, world available and can receive realtime lighting
2068                 // if the light box is offscreen, skip it right away
2069                 if (R_CullBox(cullmins, cullmaxs))
2070                         return;
2071                 // calculate lit surfaces and clusters
2072                 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
2073                 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->nummodelsurfaces); 
2074                 cl.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2075                 clusterlist = r_shadow_buffer_clusterlist;
2076                 clusterpvs = r_shadow_buffer_clusterpvs;
2077                 surfacelist = r_shadow_buffer_surfacelist;
2078         }
2079         // if the reduced cluster bounds are offscreen, skip it
2080         if (R_CullBox(cullmins, cullmaxs))
2081                 return;
2082         // check if light is illuminating any visible clusters
2083         if (numclusters)
2084         {
2085                 for (i = 0;i < numclusters;i++)
2086                         if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
2087                                 break;
2088                 if (i == numclusters)
2089                         return;
2090         }
2091         // set up a scissor rectangle for this light
2092         if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
2093                 return;
2094
2095         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2096         VectorScale(rtlight->color, f, lightcolor);
2097         /*
2098         if (rtlight->selected)
2099         {
2100                 f = 2 + sin(realtime * M_PI * 4.0);
2101                 VectorScale(lightcolor, f, lightcolor);
2102         }
2103         */
2104
2105 #if 1
2106         shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_realtime_world_shadows.integer : (r_shadow_realtime_world.integer ? r_shadow_realtime_world_dlightshadows.integer : r_shadow_realtime_dlight_shadows.integer));
2107 #else
2108         shadow = false;
2109         if (rtlight->shadow)
2110         {
2111                 if (rtlight->isstatic)
2112                         shadow = r_shadow_realtime_world_shadows.integer;
2113                 else
2114                 {
2115                         if (r_shadow_realtime_world.integer)
2116                                 shadow = r_shadow_realtime_world_dlightshadows.integer;
2117                         else
2118                                 shadow = r_shadow_realtime_dlight_shadows.integer;
2119                 }
2120         }
2121 #endif
2122
2123         if (shadow && (gl_stencil || visiblevolumes))
2124         {
2125                 if (!visiblevolumes)
2126                         R_Shadow_Stage_ShadowVolumes();
2127                 ent = &cl_entities[0].render;
2128                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2129                 {
2130                         memset(&m, 0, sizeof(m));
2131                         R_Mesh_Matrix(&ent->matrix);
2132                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2133                         {
2134                                 m.pointer_vertex = mesh->vertex3f;
2135                                 R_Mesh_State(&m);
2136                                 GL_LockArrays(0, mesh->numverts);
2137                                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2138                                 {
2139                                         // decrement stencil if frontface is behind depthbuffer
2140                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2141                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2142                                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2143                                         c_rtcached_shadowmeshes++;
2144                                         c_rtcached_shadowtris += mesh->numtriangles;
2145                                         // increment stencil if backface is behind depthbuffer
2146                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2147                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2148                                 }
2149                                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2150                                 c_rtcached_shadowmeshes++;
2151                                 c_rtcached_shadowtris += mesh->numtriangles;
2152                                 GL_LockArrays(0, 0);
2153                         }
2154                 }
2155                 else
2156                 {
2157                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2158                         ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2159                 }
2160                 if (r_drawentities.integer)
2161                 {
2162                         for (i = 0;i < r_refdef.numentities;i++)
2163                         {
2164                                 ent = r_refdef.entities[i];
2165                                 // rough checks
2166                                 if (r_shadow_cull.integer)
2167                                 {
2168                                         if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2169                                                 continue;
2170                                         if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2171                                                 continue;
2172                                 }
2173                                 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2174                                         continue;
2175                                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2176                                 // light emitting entities should not cast their own shadow
2177                                 if (VectorLength2(relativelightorigin) < 0.1)
2178                                         continue;
2179                                 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist);
2180                         }
2181                 }
2182         }
2183
2184         if (!visiblevolumes)
2185         {
2186                 R_Shadow_Stage_Light(shadow && gl_stencil);
2187
2188                 ent = &cl_entities[0].render;
2189                 if (ent->model && ent->model->DrawLight)
2190                 {
2191                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2192                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2193                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2194                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2195                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2196                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2197                         {
2198                                 R_Mesh_Matrix(&ent->matrix);
2199                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2200                                         R_Shadow_RenderLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, mesh->map_specular, cubemaptexture, LIGHTING_DIFFUSE | LIGHTING_SPECULAR);
2201                         }
2202                         else
2203                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, numsurfaces, surfacelist);
2204                 }
2205                 if (r_drawentities.integer)
2206                 {
2207                         for (i = 0;i < r_refdef.numentities;i++)
2208                         {
2209                                 ent = r_refdef.entities[i];
2210                                 if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2211                                 {
2212                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2213                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2214                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2215                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2216                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2217                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist);
2218                                 }
2219                         }
2220                 }
2221         }
2222 }
2223
2224 void R_ShadowVolumeLighting(int visiblevolumes)
2225 {
2226         int lnum;
2227         dlight_t *light;
2228         rmeshstate_t m;
2229
2230         if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2231                 R_Shadow_EditLights_Reload_f();
2232
2233         if (visiblevolumes)
2234         {
2235                 memset(&m, 0, sizeof(m));
2236                 R_Mesh_State(&m);
2237
2238                 GL_BlendFunc(GL_ONE, GL_ONE);
2239                 GL_DepthMask(false);
2240                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2241                 qglDisable(GL_CULL_FACE);
2242                 GL_Color(0.0, 0.0125, 0.1, 1);
2243         }
2244         else
2245                 R_Shadow_Stage_Begin();
2246         if (r_shadow_realtime_world.integer)
2247         {
2248                 if (r_shadow_debuglight.integer >= 0)
2249                 {
2250                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2251                                 if (lnum == r_shadow_debuglight.integer)
2252                                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2253                 }
2254                 else
2255                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2256                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2257         }
2258         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2259                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2260                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2261
2262         if (visiblevolumes)
2263         {
2264                 qglEnable(GL_CULL_FACE);
2265                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2266         }
2267         else
2268                 R_Shadow_Stage_End();
2269 }
2270
2271 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2272 typedef struct suffixinfo_s
2273 {
2274         char *suffix;
2275         qboolean flipx, flipy, flipdiagonal;
2276 }
2277 suffixinfo_t;
2278 static suffixinfo_t suffix[3][6] =
2279 {
2280         {
2281                 {"px",   false, false, false},
2282                 {"nx",   false, false, false},
2283                 {"py",   false, false, false},
2284                 {"ny",   false, false, false},
2285                 {"pz",   false, false, false},
2286                 {"nz",   false, false, false}
2287         },
2288         {
2289                 {"posx", false, false, false},
2290                 {"negx", false, false, false},
2291                 {"posy", false, false, false},
2292                 {"negy", false, false, false},
2293                 {"posz", false, false, false},
2294                 {"negz", false, false, false}
2295         },
2296         {
2297                 {"rt",    true, false,  true},
2298                 {"lf",   false,  true,  true},
2299                 {"ft",    true,  true, false},
2300                 {"bk",   false, false, false},
2301                 {"up",    true, false,  true},
2302                 {"dn",    true, false,  true}
2303         }
2304 };
2305
2306 static int componentorder[4] = {0, 1, 2, 3};
2307
2308 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2309 {
2310         int i, j, cubemapsize;
2311         qbyte *cubemappixels, *image_rgba;
2312         rtexture_t *cubemaptexture;
2313         char name[256];
2314         // must start 0 so the first loadimagepixels has no requested width/height
2315         cubemapsize = 0;
2316         cubemappixels = NULL;
2317         cubemaptexture = NULL;
2318         // keep trying different suffix groups (posx, px, rt) until one loads
2319         for (j = 0;j < 3 && !cubemappixels;j++)
2320         {
2321                 // load the 6 images in the suffix group
2322                 for (i = 0;i < 6;i++)
2323                 {
2324                         // generate an image name based on the base and and suffix
2325                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2326                         // load it
2327                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2328                         {
2329                                 // an image loaded, make sure width and height are equal
2330                                 if (image_width == image_height)
2331                                 {
2332                                         // if this is the first image to load successfully, allocate the cubemap memory
2333                                         if (!cubemappixels && image_width >= 1)
2334                                         {
2335                                                 cubemapsize = image_width;
2336                                                 // note this clears to black, so unavailable sides are black
2337                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2338                                         }
2339                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2340                                         if (cubemappixels)
2341                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
2342                                 }
2343                                 else
2344                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2345                                 // free the image
2346                                 Mem_Free(image_rgba);
2347                         }
2348                 }
2349         }
2350         // if a cubemap loaded, upload it
2351         if (cubemappixels)
2352         {
2353                 if (!r_shadow_filters_texturepool)
2354                         r_shadow_filters_texturepool = R_AllocTexturePool();
2355                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2356                 Mem_Free(cubemappixels);
2357         }
2358         else
2359         {
2360                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2361                 for (j = 0;j < 3;j++)
2362                         for (i = 0;i < 6;i++)
2363                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2364                 Con_Print(" and was unable to find any of them.\n");
2365         }
2366         return cubemaptexture;
2367 }
2368
2369 rtexture_t *R_Shadow_Cubemap(const char *basename)
2370 {
2371         int i;
2372         for (i = 0;i < numcubemaps;i++)
2373                 if (!strcasecmp(cubemaps[i].basename, basename))
2374                         return cubemaps[i].texture;
2375         if (i >= MAX_CUBEMAPS)
2376                 return NULL;
2377         numcubemaps++;
2378         strcpy(cubemaps[i].basename, basename);
2379         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2380         return cubemaps[i].texture;
2381 }
2382
2383 void R_Shadow_FreeCubemaps(void)
2384 {
2385         numcubemaps = 0;
2386         R_FreeTexturePool(&r_shadow_filters_texturepool);
2387 }
2388
2389 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
2390 {
2391         dlight_t *light;
2392
2393         if (radius < 15 || DotProduct(color, color) < 0.03)
2394         {
2395                 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2396                 return;
2397         }
2398
2399         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2400         VectorCopy(origin, light->origin);
2401         VectorCopy(angles, light->angles);
2402         VectorCopy(color, light->color);
2403         light->radius = radius;
2404         light->style = style;
2405         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2406         {
2407                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2408                 light->style = 0;
2409         }
2410         light->shadow = shadowenable;
2411         light->corona = corona;
2412         if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2413                 strcpy(light->cubemapname, cubemapname);
2414         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2415         light->next = r_shadow_worldlightchain;
2416         r_shadow_worldlightchain = light;
2417
2418         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2419 }
2420
2421 void R_Shadow_FreeWorldLight(dlight_t *light)
2422 {
2423         dlight_t **lightpointer;
2424         R_RTLight_Uncompile(&light->rtlight);
2425         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2426         if (*lightpointer != light)
2427                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2428         *lightpointer = light->next;
2429         Mem_Free(light);
2430 }
2431
2432 void R_Shadow_ClearWorldLights(void)
2433 {
2434         while (r_shadow_worldlightchain)
2435                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2436         r_shadow_selectedlight = NULL;
2437         R_Shadow_FreeCubemaps();
2438 }
2439
2440 void R_Shadow_SelectLight(dlight_t *light)
2441 {
2442         if (r_shadow_selectedlight)
2443                 r_shadow_selectedlight->selected = false;
2444         r_shadow_selectedlight = light;
2445         if (r_shadow_selectedlight)
2446                 r_shadow_selectedlight->selected = true;
2447 }
2448
2449 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2450 {
2451         float scale = r_editlights_cursorgrid.value * 0.5f;
2452         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);
2453 }
2454
2455 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2456 {
2457         float intensity;
2458         const dlight_t *light;
2459         light = calldata1;
2460         intensity = 0.5;
2461         if (light->selected)
2462                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2463         if (!light->shadow)
2464                 intensity *= 0.5f;
2465         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);
2466 }
2467
2468 void R_Shadow_DrawLightSprites(void)
2469 {
2470         int i;
2471         cachepic_t *pic;
2472         dlight_t *light;
2473
2474         for (i = 0;i < 5;i++)
2475         {
2476                 lighttextures[i] = NULL;
2477                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2478                         lighttextures[i] = pic->tex;
2479         }
2480
2481         for (light = r_shadow_worldlightchain;light;light = light->next)
2482                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2483         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2484 }
2485
2486 void R_Shadow_SelectLightInView(void)
2487 {
2488         float bestrating, rating, temp[3];
2489         dlight_t *best, *light;
2490         best = NULL;
2491         bestrating = 0;
2492         for (light = r_shadow_worldlightchain;light;light = light->next)
2493         {
2494                 VectorSubtract(light->origin, r_vieworigin, temp);
2495                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2496                 if (rating >= 0.95)
2497                 {
2498                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2499                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2500                         {
2501                                 bestrating = rating;
2502                                 best = light;
2503                         }
2504                 }
2505         }
2506         R_Shadow_SelectLight(best);
2507 }
2508
2509 void R_Shadow_LoadWorldLights(void)
2510 {
2511         int n, a, style, shadow;
2512         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2513         float origin[3], radius, color[3], angles[3], corona;
2514         if (cl.worldmodel == NULL)
2515         {
2516                 Con_Print("No map loaded.\n");
2517                 return;
2518         }
2519         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2520         strlcat (name, ".rtlights", sizeof (name));
2521         lightsstring = FS_LoadFile(name, tempmempool, false);
2522         if (lightsstring)
2523         {
2524                 s = lightsstring;
2525                 n = 0;
2526                 while (*s)
2527                 {
2528                         t = s;
2529                         /*
2530                         shadow = true;
2531                         for (;COM_Parse(t, true) && strcmp(
2532                         if (COM_Parse(t, true))
2533                         {
2534                                 if (com_token[0] == '!')
2535                                 {
2536                                         shadow = false;
2537                                         origin[0] = atof(com_token+1);
2538                                 }
2539                                 else
2540                                         origin[0] = atof(com_token);
2541                                 if (Com_Parse(t
2542                         }
2543                         */
2544                         t = s;
2545                         while (*s && *s != '\n')
2546                                 s++;
2547                         if (!*s)
2548                                 break;
2549                         *s = 0;
2550                         shadow = true;
2551                         // check for modifier flags
2552                         if (*t == '!')
2553                         {
2554                                 shadow = false;
2555                                 t++;
2556                         }
2557                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2558                         if (a < 13)
2559                                 VectorClear(angles);
2560                         if (a < 10)
2561                                 corona = 0;
2562                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2563                                 cubemapname[0] = 0;
2564                         *s = '\n';
2565                         if (a < 8)
2566                         {
2567                                 Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2])\n", a, n + 1);
2568                                 break;
2569                         }
2570                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2571                         radius *= r_editlights_rtlightssizescale.value;
2572                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2573                         s++;
2574                         n++;
2575                 }
2576                 if (*s)
2577                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2578                 Mem_Free(lightsstring);
2579         }
2580 }
2581
2582 void R_Shadow_SaveWorldLights(void)
2583 {
2584         dlight_t *light;
2585         int bufchars, bufmaxchars;
2586         char *buf, *oldbuf;
2587         char name[MAX_QPATH];
2588         char line[1024];
2589         if (!r_shadow_worldlightchain)
2590                 return;
2591         if (cl.worldmodel == NULL)
2592         {
2593                 Con_Print("No map loaded.\n");
2594                 return;
2595         }
2596         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2597         strlcat (name, ".rtlights", sizeof (name));
2598         bufchars = bufmaxchars = 0;
2599         buf = NULL;
2600         for (light = r_shadow_worldlightchain;light;light = light->next)
2601         {
2602                 sprintf(line, "%s%f %f %f %f %f %f %f %d %s %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]);
2603                 if (bufchars + (int) strlen(line) > bufmaxchars)
2604                 {
2605                         bufmaxchars = bufchars + strlen(line) + 2048;
2606                         oldbuf = buf;
2607                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2608                         if (oldbuf)
2609                         {
2610                                 if (bufchars)
2611                                         memcpy(buf, oldbuf, bufchars);
2612                                 Mem_Free(oldbuf);
2613                         }
2614                 }
2615                 if (strlen(line))
2616                 {
2617                         memcpy(buf + bufchars, line, strlen(line));
2618                         bufchars += strlen(line);
2619                 }
2620         }
2621         if (bufchars)
2622                 FS_WriteFile(name, buf, bufchars);
2623         if (buf)
2624                 Mem_Free(buf);
2625 }
2626
2627 void R_Shadow_LoadLightsFile(void)
2628 {
2629         int n, a, style;
2630         char name[MAX_QPATH], *lightsstring, *s, *t;
2631         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2632         if (cl.worldmodel == NULL)
2633         {
2634                 Con_Print("No map loaded.\n");
2635                 return;
2636         }
2637         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2638         strlcat (name, ".lights", sizeof (name));
2639         lightsstring = FS_LoadFile(name, tempmempool, false);
2640         if (lightsstring)
2641         {
2642                 s = lightsstring;
2643                 n = 0;
2644                 while (*s)
2645                 {
2646                         t = s;
2647                         while (*s && *s != '\n')
2648                                 s++;
2649                         if (!*s)
2650                                 break;
2651                         *s = 0;
2652                         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);
2653                         *s = '\n';
2654                         if (a < 14)
2655                         {
2656                                 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);
2657                                 break;
2658                         }
2659                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2660                         radius = bound(15, radius, 4096);
2661                         VectorScale(color, (2.0f / (8388608.0f)), color);
2662                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2663                         s++;
2664                         n++;
2665                 }
2666                 if (*s)
2667                         Con_Printf("invalid lights file \"%s\"\n", name);
2668                 Mem_Free(lightsstring);
2669         }
2670 }
2671
2672 // tyrlite/hmap2 light types in the delay field
2673 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
2674
2675 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2676 {
2677         int entnum, style, islight, skin, pflags, effects, type, n;
2678         char key[256], value[1024];
2679         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
2680         const char *data;
2681
2682         if (cl.worldmodel == NULL)
2683         {
2684                 Con_Print("No map loaded.\n");
2685                 return;
2686         }
2687         data = cl.worldmodel->brush.entities;
2688         if (!data)
2689                 return;
2690         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2691         {
2692                 type = LIGHTTYPE_MINUSX;
2693                 origin[0] = origin[1] = origin[2] = 0;
2694                 originhack[0] = originhack[1] = originhack[2] = 0;
2695                 angles[0] = angles[1] = angles[2] = 0;
2696                 color[0] = color[1] = color[2] = 1;
2697                 light[0] = light[1] = light[2] = 1;light[3] = 300;
2698                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2699                 fadescale = 1;
2700                 lightscale = 1;
2701                 style = 0;
2702                 skin = 0;
2703                 pflags = 0;
2704                 effects = 0;
2705                 islight = false;
2706                 while (1)
2707                 {
2708                         if (!COM_ParseToken(&data, false))
2709                                 break; // error
2710                         if (com_token[0] == '}')
2711                                 break; // end of entity
2712                         if (com_token[0] == '_')
2713                                 strcpy(key, com_token + 1);
2714                         else
2715                                 strcpy(key, com_token);
2716                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2717                                 key[strlen(key)-1] = 0;
2718                         if (!COM_ParseToken(&data, false))
2719                                 break; // error
2720                         strcpy(value, com_token);
2721
2722                         // now that we have the key pair worked out...
2723                         if (!strcmp("light", key))
2724                         {
2725                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
2726                                 if (n == 1)
2727                                 {
2728                                         // quake
2729                                         light[0] = vec[0] * (1.0f / 256.0f);
2730                                         light[1] = vec[0] * (1.0f / 256.0f);
2731                                         light[2] = vec[0] * (1.0f / 256.0f);
2732                                         light[3] = vec[0];
2733                                 }
2734                                 else if (n == 4)
2735                                 {
2736                                         // halflife
2737                                         light[0] = vec[0] * (1.0f / 255.0f);
2738                                         light[1] = vec[1] * (1.0f / 255.0f);
2739                                         light[2] = vec[2] * (1.0f / 255.0f);
2740                                         light[3] = vec[3];
2741                                 }
2742                         }
2743                         else if (!strcmp("delay", key))
2744                                 type = atoi(value);
2745                         else if (!strcmp("origin", key))
2746                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2747                         else if (!strcmp("angle", key))
2748                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2749                         else if (!strcmp("angles", key))
2750                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2751                         else if (!strcmp("color", key))
2752                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2753                         else if (!strcmp("wait", key))
2754                                 fadescale = atof(value);
2755                         else if (!strcmp("classname", key))
2756                         {
2757                                 if (!strncmp(value, "light", 5))
2758                                 {
2759                                         islight = true;
2760                                         if (!strcmp(value, "light_fluoro"))
2761                                         {
2762                                                 originhack[0] = 0;
2763                                                 originhack[1] = 0;
2764                                                 originhack[2] = 0;
2765                                                 overridecolor[0] = 1;
2766                                                 overridecolor[1] = 1;
2767                                                 overridecolor[2] = 1;
2768                                         }
2769                                         if (!strcmp(value, "light_fluorospark"))
2770                                         {
2771                                                 originhack[0] = 0;
2772                                                 originhack[1] = 0;
2773                                                 originhack[2] = 0;
2774                                                 overridecolor[0] = 1;
2775                                                 overridecolor[1] = 1;
2776                                                 overridecolor[2] = 1;
2777                                         }
2778                                         if (!strcmp(value, "light_globe"))
2779                                         {
2780                                                 originhack[0] = 0;
2781                                                 originhack[1] = 0;
2782                                                 originhack[2] = 0;
2783                                                 overridecolor[0] = 1;
2784                                                 overridecolor[1] = 0.8;
2785                                                 overridecolor[2] = 0.4;
2786                                         }
2787                                         if (!strcmp(value, "light_flame_large_yellow"))
2788                                         {
2789                                                 originhack[0] = 0;
2790                                                 originhack[1] = 0;
2791                                                 originhack[2] = 48;
2792                                                 overridecolor[0] = 1;
2793                                                 overridecolor[1] = 0.5;
2794                                                 overridecolor[2] = 0.1;
2795                                         }
2796                                         if (!strcmp(value, "light_flame_small_yellow"))
2797                                         {
2798                                                 originhack[0] = 0;
2799                                                 originhack[1] = 0;
2800                                                 originhack[2] = 40;
2801                                                 overridecolor[0] = 1;
2802                                                 overridecolor[1] = 0.5;
2803                                                 overridecolor[2] = 0.1;
2804                                         }
2805                                         if (!strcmp(value, "light_torch_small_white"))
2806                                         {
2807                                                 originhack[0] = 0;
2808                                                 originhack[1] = 0;
2809                                                 originhack[2] = 40;
2810                                                 overridecolor[0] = 1;
2811                                                 overridecolor[1] = 0.5;
2812                                                 overridecolor[2] = 0.1;
2813                                         }
2814                                         if (!strcmp(value, "light_torch_small_walltorch"))
2815                                         {
2816                                                 originhack[0] = 0;
2817                                                 originhack[1] = 0;
2818                                                 originhack[2] = 40;
2819                                                 overridecolor[0] = 1;
2820                                                 overridecolor[1] = 0.5;
2821                                                 overridecolor[2] = 0.1;
2822                                         }
2823                                 }
2824                         }
2825                         else if (!strcmp("style", key))
2826                                 style = atoi(value);
2827                         else if (cl.worldmodel->type == mod_brushq3)
2828                         {
2829                                 if (!strcmp("scale", key))
2830                                         lightscale = atof(value);
2831                                 if (!strcmp("fade", key))
2832                                         fadescale = atof(value);
2833                         }
2834                         else if (!strcmp("skin", key))
2835                                 skin = (int)atof(value);
2836                         else if (!strcmp("pflags", key))
2837                                 pflags = (int)atof(value);
2838                         else if (!strcmp("effects", key))
2839                                 effects = (int)atof(value);
2840                 }
2841                 if (!islight)
2842                         continue;
2843                 if (lightscale <= 0)
2844                         lightscale = 1;
2845                 if (fadescale <= 0)
2846                         fadescale = 1;
2847                 if (color[0] == color[1] && color[0] == color[2])
2848                 {
2849                         color[0] *= overridecolor[0];
2850                         color[1] *= overridecolor[1];
2851                         color[2] *= overridecolor[2];
2852                 }
2853                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
2854                 color[0] = color[0] * light[0];
2855                 color[1] = color[1] * light[1];
2856                 color[2] = color[2] * light[2];
2857                 switch (type)
2858                 {
2859                 case LIGHTTYPE_MINUSX:
2860                         break;
2861                 case LIGHTTYPE_RECIPX:
2862                         radius *= 2;
2863                         VectorScale(color, (1.0f / 16.0f), color);
2864                         break;
2865                 case LIGHTTYPE_RECIPXX:
2866                         radius *= 2;
2867                         VectorScale(color, (1.0f / 16.0f), color);
2868                         break;
2869                 default:
2870                 case LIGHTTYPE_NONE:
2871                         break;
2872                 case LIGHTTYPE_SUN:
2873                         break;
2874                 case LIGHTTYPE_MINUSXX:
2875                         break;
2876                 }
2877                 VectorAdd(origin, originhack, origin);
2878                 if (radius >= 1)
2879                         R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2880         }
2881 }
2882
2883
2884 void R_Shadow_SetCursorLocationForView(void)
2885 {
2886         vec_t dist, push, frac;
2887         vec3_t dest, endpos, normal;
2888         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2889         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2890         if (frac < 1)
2891         {
2892                 dist = frac * r_editlights_cursordistance.value;
2893                 push = r_editlights_cursorpushback.value;
2894                 if (push > dist)
2895                         push = dist;
2896                 push = -push;
2897                 VectorMA(endpos, push, r_viewforward, endpos);
2898                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2899         }
2900         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2901         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2902         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2903 }
2904
2905 void R_Shadow_UpdateWorldLightSelection(void)
2906 {
2907         if (r_editlights.integer)
2908         {
2909                 R_Shadow_SetCursorLocationForView();
2910                 R_Shadow_SelectLightInView();
2911                 R_Shadow_DrawLightSprites();
2912         }
2913         else
2914                 R_Shadow_SelectLight(NULL);
2915 }
2916
2917 void R_Shadow_EditLights_Clear_f(void)
2918 {
2919         R_Shadow_ClearWorldLights();
2920 }
2921
2922 void R_Shadow_EditLights_Reload_f(void)
2923 {
2924         if (!cl.worldmodel)
2925                 return;
2926         strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
2927         R_Shadow_ClearWorldLights();
2928         R_Shadow_LoadWorldLights();
2929         if (r_shadow_worldlightchain == NULL)
2930         {
2931                 R_Shadow_LoadLightsFile();
2932                 if (r_shadow_worldlightchain == NULL)
2933                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2934         }
2935 }
2936
2937 void R_Shadow_EditLights_Save_f(void)
2938 {
2939         if (!cl.worldmodel)
2940                 return;
2941         R_Shadow_SaveWorldLights();
2942 }
2943
2944 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2945 {
2946         R_Shadow_ClearWorldLights();
2947         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2948 }
2949
2950 void R_Shadow_EditLights_ImportLightsFile_f(void)
2951 {
2952         R_Shadow_ClearWorldLights();
2953         R_Shadow_LoadLightsFile();
2954 }
2955
2956 void R_Shadow_EditLights_Spawn_f(void)
2957 {
2958         vec3_t color;
2959         if (!r_editlights.integer)
2960         {
2961                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2962                 return;
2963         }
2964         if (Cmd_Argc() != 1)
2965         {
2966                 Con_Print("r_editlights_spawn does not take parameters\n");
2967                 return;
2968         }
2969         color[0] = color[1] = color[2] = 1;
2970         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2971 }
2972
2973 void R_Shadow_EditLights_Edit_f(void)
2974 {
2975         vec3_t origin, angles, color;
2976         vec_t radius, corona;
2977         int style, shadows;
2978         char cubemapname[1024];
2979         if (!r_editlights.integer)
2980         {
2981                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2982                 return;
2983         }
2984         if (!r_shadow_selectedlight)
2985         {
2986                 Con_Print("No selected light.\n");
2987                 return;
2988         }
2989         VectorCopy(r_shadow_selectedlight->origin, origin);
2990         VectorCopy(r_shadow_selectedlight->angles, angles);
2991         VectorCopy(r_shadow_selectedlight->color, color);
2992         radius = r_shadow_selectedlight->radius;
2993         style = r_shadow_selectedlight->style;
2994         if (r_shadow_selectedlight->cubemapname)
2995                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2996         else
2997                 cubemapname[0] = 0;
2998         shadows = r_shadow_selectedlight->shadow;
2999         corona = r_shadow_selectedlight->corona;
3000         if (!strcmp(Cmd_Argv(1), "origin"))
3001         {
3002                 if (Cmd_Argc() != 5)
3003                 {
3004                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3005                         return;
3006                 }
3007                 origin[0] = atof(Cmd_Argv(2));
3008                 origin[1] = atof(Cmd_Argv(3));
3009                 origin[2] = atof(Cmd_Argv(4));
3010         }
3011         else if (!strcmp(Cmd_Argv(1), "originx"))
3012         {
3013                 if (Cmd_Argc() != 3)
3014                 {
3015                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3016                         return;
3017                 }
3018                 origin[0] = atof(Cmd_Argv(2));
3019         }
3020         else if (!strcmp(Cmd_Argv(1), "originy"))
3021         {
3022                 if (Cmd_Argc() != 3)
3023                 {
3024                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3025                         return;
3026                 }
3027                 origin[1] = atof(Cmd_Argv(2));
3028         }
3029         else if (!strcmp(Cmd_Argv(1), "originz"))
3030         {
3031                 if (Cmd_Argc() != 3)
3032                 {
3033                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3034                         return;
3035                 }
3036                 origin[2] = atof(Cmd_Argv(2));
3037         }
3038         else if (!strcmp(Cmd_Argv(1), "move"))
3039         {
3040                 if (Cmd_Argc() != 5)
3041                 {
3042                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3043                         return;
3044                 }
3045                 origin[0] += atof(Cmd_Argv(2));
3046                 origin[1] += atof(Cmd_Argv(3));
3047                 origin[2] += atof(Cmd_Argv(4));
3048         }
3049         else if (!strcmp(Cmd_Argv(1), "movex"))
3050         {
3051                 if (Cmd_Argc() != 3)
3052                 {
3053                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3054                         return;
3055                 }
3056                 origin[0] += atof(Cmd_Argv(2));
3057         }
3058         else if (!strcmp(Cmd_Argv(1), "movey"))
3059         {
3060                 if (Cmd_Argc() != 3)
3061                 {
3062                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3063                         return;
3064                 }
3065                 origin[1] += atof(Cmd_Argv(2));
3066         }
3067         else if (!strcmp(Cmd_Argv(1), "movez"))
3068         {
3069                 if (Cmd_Argc() != 3)
3070                 {
3071                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3072                         return;
3073                 }
3074                 origin[2] += atof(Cmd_Argv(2));
3075         }
3076         else if (!strcmp(Cmd_Argv(1), "angles"))
3077         {
3078                 if (Cmd_Argc() != 5)
3079                 {
3080                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3081                         return;
3082                 }
3083                 angles[0] = atof(Cmd_Argv(2));
3084                 angles[1] = atof(Cmd_Argv(3));
3085                 angles[2] = atof(Cmd_Argv(4));
3086         }
3087         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3088         {
3089                 if (Cmd_Argc() != 3)
3090                 {
3091                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3092                         return;
3093                 }
3094                 angles[0] = atof(Cmd_Argv(2));
3095         }
3096         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3097         {
3098                 if (Cmd_Argc() != 3)
3099                 {
3100                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3101                         return;
3102                 }
3103                 angles[1] = atof(Cmd_Argv(2));
3104         }
3105         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3106         {
3107                 if (Cmd_Argc() != 3)
3108                 {
3109                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3110                         return;
3111                 }
3112                 angles[2] = atof(Cmd_Argv(2));
3113         }
3114         else if (!strcmp(Cmd_Argv(1), "color"))
3115         {
3116                 if (Cmd_Argc() != 5)
3117                 {
3118                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3119                         return;
3120                 }
3121                 color[0] = atof(Cmd_Argv(2));
3122                 color[1] = atof(Cmd_Argv(3));
3123                 color[2] = atof(Cmd_Argv(4));
3124         }
3125         else if (!strcmp(Cmd_Argv(1), "radius"))
3126         {
3127                 if (Cmd_Argc() != 3)
3128                 {
3129                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3130                         return;
3131                 }
3132                 radius = atof(Cmd_Argv(2));
3133         }
3134         else if (!strcmp(Cmd_Argv(1), "style"))
3135         {
3136                 if (Cmd_Argc() != 3)
3137                 {
3138                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3139                         return;
3140                 }
3141                 style = atoi(Cmd_Argv(2));
3142         }
3143         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3144         {
3145                 if (Cmd_Argc() > 3)
3146                 {
3147                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3148                         return;
3149                 }
3150                 if (Cmd_Argc() == 3)
3151                         strcpy(cubemapname, Cmd_Argv(2));
3152                 else
3153                         cubemapname[0] = 0;
3154         }
3155         else if (!strcmp(Cmd_Argv(1), "shadows"))
3156         {
3157                 if (Cmd_Argc() != 3)
3158                 {
3159                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3160                         return;
3161                 }
3162                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3163         }
3164         else if (!strcmp(Cmd_Argv(1), "corona"))
3165         {
3166                 if (Cmd_Argc() != 3)
3167                 {
3168                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3169                         return;
3170                 }
3171                 corona = atof(Cmd_Argv(2));
3172         }
3173         else
3174         {
3175                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3176                 Con_Print("Selected light's properties:\n");
3177                 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3178                 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3179                 Con_Printf("Color  : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3180                 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3181                 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3182                 Con_Printf("Style  : %i\n", r_shadow_selectedlight->style);
3183                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3184                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3185                 return;
3186         }
3187         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3188         r_shadow_selectedlight = NULL;
3189         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3190 }
3191
3192 void R_Shadow_EditLights_EditAll_f(void)
3193 {
3194         dlight_t *light;
3195
3196         for (light = r_shadow_worldlightchain;light;light = light->next)
3197         {
3198                 R_Shadow_SelectLight(light);
3199                 R_Shadow_EditLights_Edit_f();
3200         }
3201 }
3202
3203 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3204 {
3205         float x, y;
3206         char temp[256];
3207         if (!r_editlights.integer)
3208                 return;
3209         x = 0;
3210         y = con_vislines;
3211         sprintf(temp, "Cursor  %f %f %f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3212         if (r_shadow_selectedlight == NULL)
3213                 return;
3214         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3215         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;
3216         sprintf(temp, "Angles  %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3217         sprintf(temp, "Color   %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3218         sprintf(temp, "Radius  %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3219         sprintf(temp, "Corona  %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3220         sprintf(temp, "Style   %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3221         sprintf(temp, "Shadows %s", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3222         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3223 }
3224
3225 void R_Shadow_EditLights_ToggleShadow_f(void)
3226 {
3227         if (!r_editlights.integer)
3228         {
3229                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3230                 return;
3231         }
3232         if (!r_shadow_selectedlight)
3233         {
3234                 Con_Print("No selected light.\n");
3235                 return;
3236         }
3237         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3238         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3239         r_shadow_selectedlight = NULL;
3240 }
3241
3242 void R_Shadow_EditLights_ToggleCorona_f(void)
3243 {
3244         if (!r_editlights.integer)
3245         {
3246                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3247                 return;
3248         }
3249         if (!r_shadow_selectedlight)
3250         {
3251                 Con_Print("No selected light.\n");
3252                 return;
3253         }
3254         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3255         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3256         r_shadow_selectedlight = NULL;
3257 }
3258
3259 void R_Shadow_EditLights_Remove_f(void)
3260 {
3261         if (!r_editlights.integer)
3262         {
3263                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3264                 return;
3265         }
3266         if (!r_shadow_selectedlight)
3267         {
3268                 Con_Print("No selected light.\n");
3269                 return;
3270         }
3271         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3272         r_shadow_selectedlight = NULL;
3273 }
3274
3275 void R_Shadow_EditLights_Help_f(void)
3276 {
3277         Con_Print(
3278 "Documentation on r_editlights system:\n"
3279 "Settings:\n"
3280 "r_editlights : enable/disable editing mode\n"
3281 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3282 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3283 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3284 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3285 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3286 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3287 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3288 "Commands:\n"
3289 "r_editlights_help : this help\n"
3290 "r_editlights_clear : remove all lights\n"
3291 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3292 "r_editlights_save : save to .rtlights file\n"
3293 "r_editlights_spawn : create a light with default settings\n"
3294 "r_editlights_edit command : edit selected light - more documentation below\n"
3295 "r_editlights_remove : remove selected light\n"
3296 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3297 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3298 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3299 "Edit commands:\n"
3300 "origin x y z : set light location\n"
3301 "originx x: set x component of light location\n"
3302 "originy y: set y component of light location\n"
3303 "originz z: set z component of light location\n"
3304 "move x y z : adjust light location\n"
3305 "movex x: adjust x component of light location\n"
3306 "movey y: adjust y component of light location\n"
3307 "movez z: adjust z component of light location\n"
3308 "angles x y z : set light angles\n"
3309 "anglesx x: set x component of light angles\n"
3310 "anglesy y: set y component of light angles\n"
3311 "anglesz z: set z component of light angles\n"
3312 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3313 "radius radius : set radius (size) of light\n"
3314 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3315 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3316 "shadows 1/0 : turn on/off shadows\n"
3317 "corona n : set corona intensity\n"
3318 "<nothing> : print light properties to console\n"
3319         );
3320 }
3321
3322 void R_Shadow_EditLights_Init(void)
3323 {
3324         Cvar_RegisterVariable(&r_editlights);
3325         Cvar_RegisterVariable(&r_editlights_cursordistance);
3326         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3327         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3328         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3329         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3330         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3331         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3332         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3333         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3334         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3335         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3336         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3337         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3338         Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
3339         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3340         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3341         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3342         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3343         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3344 }
3345