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