]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
Fixed the missing colorlength param on te_explosion2
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
20
21 Patent warning:
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
26 shadows do not lie.
27
28
29
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
36
37
38
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
44 in some ideal cases).
45
46
47
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however.  Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
57
58
59
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
65
66
67
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
76
77
78
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
82
83
84
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
89
90
91
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
96
97
98
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
105
106
107
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
112 */
113
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
117 #include "portals.h"
118 #include "image.h"
119
120 extern void R_Shadow_EditLights_Init(void);
121
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_STENCILTWOSIDE 3
126
127 int r_shadowstage = SHADOWSTAGE_NONE;
128
129 mempool_t *r_shadow_mempool;
130
131 int maxshadowelements;
132 int *shadowelements;
133
134 int maxshadowmark;
135 int numshadowmark;
136 int *shadowmark;
137 int *shadowmarklist;
138 int shadowmarkcount;
139
140 int maxvertexupdate;
141 int *vertexupdate;
142 int *vertexremap;
143 int vertexupdatenum;
144
145 int r_shadow_buffer_numclusterpvsbytes;
146 qbyte *r_shadow_buffer_clusterpvs;
147 int *r_shadow_buffer_clusterlist;
148
149 int r_shadow_buffer_numsurfacepvsbytes;
150 qbyte *r_shadow_buffer_surfacepvs;
151 int *r_shadow_buffer_surfacelist;
152
153 rtexturepool_t *r_shadow_texturepool;
154 rtexture_t *r_shadow_normalcubetexture;
155 rtexture_t *r_shadow_attenuation2dtexture;
156 rtexture_t *r_shadow_attenuation3dtexture;
157 rtexture_t *r_shadow_blankbumptexture;
158 rtexture_t *r_shadow_blankglosstexture;
159 rtexture_t *r_shadow_blankwhitetexture;
160
161 // lights are reloaded when this changes
162 char r_shadow_mapname[MAX_QPATH];
163
164 // used only for light filters (cubemaps)
165 rtexturepool_t *r_shadow_filters_texturepool;
166
167 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
168 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
169 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
170 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
171 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
172 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
173 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
174 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
175 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
176 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
177 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
178 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
179 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "1"};
180 cvar_t r_shadow_realtime_dlight_shadows = {0, "r_shadow_realtime_dlight_shadows", "0"};
181 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
182 cvar_t r_shadow_realtime_world_dlightshadows = {0, "r_shadow_realtime_world_dlightshadows", "1"};
183 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
184 cvar_t r_shadow_realtime_world_shadows = {0, "r_shadow_realtime_world_shadows", "1"};
185 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
186 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
187 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
188 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
189 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
190 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
191 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
192 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
193 cvar_t r_editlights = {0, "r_editlights", "0"};
194 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
195 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
196 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
197 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
198 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
199 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
200 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
201
202 int c_rt_lights, c_rt_clears, c_rt_scissored;
203 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
204 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
205
206 float r_shadow_attenpower, r_shadow_attenscale;
207
208 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_LightWithoutShadows(void)
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         qglDisable(GL_STENCIL_TEST);
927         if (gl_support_stenciltwoside)
928                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
929         qglStencilMask(~0);
930         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
931         qglStencilFunc(GL_EQUAL, 128, ~0);
932         r_shadowstage = SHADOWSTAGE_LIGHT;
933         c_rt_lights++;
934 }
935
936 void R_Shadow_Stage_LightWithShadows(void)
937 {
938         rmeshstate_t m;
939         memset(&m, 0, sizeof(m));
940         R_Mesh_State(&m);
941         GL_BlendFunc(GL_ONE, GL_ONE);
942         GL_DepthMask(false);
943         GL_DepthTest(true);
944         qglPolygonOffset(0, 0);
945         //qglDisable(GL_POLYGON_OFFSET_FILL);
946         GL_Color(1, 1, 1, 1);
947         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
948         qglDepthFunc(GL_EQUAL);
949         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
950         qglEnable(GL_STENCIL_TEST);
951         if (gl_support_stenciltwoside)
952                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
953         qglStencilMask(~0);
954         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
955         // only draw light where this geometry was already rendered AND the
956         // stencil is 128 (values other than this mean shadow)
957         qglStencilFunc(GL_EQUAL, 128, ~0);
958         r_shadowstage = SHADOWSTAGE_LIGHT;
959         c_rt_lights++;
960 }
961
962 void R_Shadow_Stage_End(void)
963 {
964         rmeshstate_t m;
965         memset(&m, 0, sizeof(m));
966         R_Mesh_State(&m);
967         GL_BlendFunc(GL_ONE, GL_ZERO);
968         GL_DepthMask(true);
969         GL_DepthTest(true);
970         qglPolygonOffset(0, 0);
971         //qglDisable(GL_POLYGON_OFFSET_FILL);
972         GL_Color(1, 1, 1, 1);
973         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
974         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
975         qglDepthFunc(GL_LEQUAL);
976         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
977         qglDisable(GL_STENCIL_TEST);
978         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
979         if (gl_support_stenciltwoside)
980                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
981         qglStencilMask(~0);
982         qglStencilFunc(GL_ALWAYS, 128, ~0);
983         r_shadowstage = SHADOWSTAGE_NONE;
984 }
985
986 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
987 {
988         int i, ix1, iy1, ix2, iy2;
989         float x1, y1, x2, y2, x, y, f;
990         vec3_t smins, smaxs;
991         vec4_t v, v2;
992         if (!r_shadow_scissor.integer)
993                 return false;
994         // if view is inside the box, just say yes it's visible
995         if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
996         {
997                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
998                 return false;
999         }
1000         for (i = 0;i < 3;i++)
1001         {
1002                 if (r_viewforward[i] >= 0)
1003                 {
1004                         v[i] = mins[i];
1005                         v2[i] = maxs[i];
1006                 }
1007                 else
1008                 {
1009                         v[i] = maxs[i];
1010                         v2[i] = mins[i];
1011                 }
1012         }
1013         f = DotProduct(r_viewforward, r_vieworigin) + 1;
1014         if (DotProduct(r_viewforward, v2) <= f)
1015         {
1016                 // entirely behind nearclip plane
1017                 return true;
1018         }
1019         if (DotProduct(r_viewforward, v) >= f)
1020         {
1021                 // entirely infront of nearclip plane
1022                 x1 = y1 = x2 = y2 = 0;
1023                 for (i = 0;i < 8;i++)
1024                 {
1025                         v[0] = (i & 1) ? mins[0] : maxs[0];
1026                         v[1] = (i & 2) ? mins[1] : maxs[1];
1027                         v[2] = (i & 4) ? mins[2] : maxs[2];
1028                         v[3] = 1.0f;
1029                         GL_TransformToScreen(v, v2);
1030                         //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]);
1031                         x = v2[0];
1032                         y = v2[1];
1033                         if (i)
1034                         {
1035                                 if (x1 > x) x1 = x;
1036                                 if (x2 < x) x2 = x;
1037                                 if (y1 > y) y1 = y;
1038                                 if (y2 < y) y2 = y;
1039                         }
1040                         else
1041                         {
1042                                 x1 = x2 = x;
1043                                 y1 = y2 = y;
1044                         }
1045                 }
1046         }
1047         else
1048         {
1049                 // clipped by nearclip plane
1050                 // this is nasty and crude...
1051                 // create viewspace bbox
1052                 for (i = 0;i < 8;i++)
1053                 {
1054                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1055                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1056                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1057                         v2[0] = -DotProduct(v, r_viewleft);
1058                         v2[1] = DotProduct(v, r_viewup);
1059                         v2[2] = DotProduct(v, r_viewforward);
1060                         if (i)
1061                         {
1062                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1063                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1064                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1065                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1066                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1067                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1068                         }
1069                         else
1070                         {
1071                                 smins[0] = smaxs[0] = v2[0];
1072                                 smins[1] = smaxs[1] = v2[1];
1073                                 smins[2] = smaxs[2] = v2[2];
1074                         }
1075                 }
1076                 // now we have a bbox in viewspace
1077                 // clip it to the view plane
1078                 if (smins[2] < 1)
1079                         smins[2] = 1;
1080                 // return true if that culled the box
1081                 if (smins[2] >= smaxs[2])
1082                         return true;
1083                 // ok some of it is infront of the view, transform each corner back to
1084                 // worldspace and then to screenspace and make screen rect
1085                 // initialize these variables just to avoid compiler warnings
1086                 x1 = y1 = x2 = y2 = 0;
1087                 for (i = 0;i < 8;i++)
1088                 {
1089                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1090                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1091                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1092                         v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1093                         v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1094                         v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1095                         v[3] = 1.0f;
1096                         GL_TransformToScreen(v, v2);
1097                         //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]);
1098                         x = v2[0];
1099                         y = v2[1];
1100                         if (i)
1101                         {
1102                                 if (x1 > x) x1 = x;
1103                                 if (x2 < x) x2 = x;
1104                                 if (y1 > y) y1 = y;
1105                                 if (y2 < y) y2 = y;
1106                         }
1107                         else
1108                         {
1109                                 x1 = x2 = x;
1110                                 y1 = y2 = y;
1111                         }
1112                 }
1113                 /*
1114                 // this code doesn't handle boxes with any points behind view properly
1115                 x1 = 1000;x2 = -1000;
1116                 y1 = 1000;y2 = -1000;
1117                 for (i = 0;i < 8;i++)
1118                 {
1119                         v[0] = (i & 1) ? mins[0] : maxs[0];
1120                         v[1] = (i & 2) ? mins[1] : maxs[1];
1121                         v[2] = (i & 4) ? mins[2] : maxs[2];
1122                         v[3] = 1.0f;
1123                         GL_TransformToScreen(v, v2);
1124                         //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]);
1125                         if (v2[2] > 0)
1126                         {
1127                                 x = v2[0];
1128                                 y = v2[1];
1129
1130                                 if (x1 > x) x1 = x;
1131                                 if (x2 < x) x2 = x;
1132                                 if (y1 > y) y1 = y;
1133                                 if (y2 < y) y2 = y;
1134                         }
1135                 }
1136                 */
1137         }
1138         ix1 = x1 - 1.0f;
1139         iy1 = y1 - 1.0f;
1140         ix2 = x2 + 1.0f;
1141         iy2 = y2 + 1.0f;
1142         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1143         if (ix1 < r_view_x) ix1 = r_view_x;
1144         if (iy1 < r_view_y) iy1 = r_view_y;
1145         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1146         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1147         if (ix2 <= ix1 || iy2 <= iy1)
1148                 return true;
1149         // set up the scissor rectangle
1150         GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1151         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1152         //qglEnable(GL_SCISSOR_TEST);
1153         c_rt_scissored++;
1154         return false;
1155 }
1156
1157 static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1158 {
1159         float *color4f = varray_color4f;
1160         float dist, dot, intensity, v[3], n[3];
1161         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1162         {
1163                 Matrix4x4_Transform(m, vertex3f, v);
1164                 if ((dist = DotProduct(v, v)) < 1)
1165                 {
1166                         Matrix4x4_Transform3x3(m, normal3f, n);
1167                         if ((dot = DotProduct(n, v)) > 0)
1168                         {
1169                                 dist = sqrt(dist);
1170                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1171                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1172                                 VectorScale(lightcolor, intensity, color4f);
1173                                 color4f[3] = 1;
1174                         }
1175                         else
1176                         {
1177                                 VectorClear(color4f);
1178                                 color4f[3] = 1;
1179                         }
1180                 }
1181                 else
1182                 {
1183                         VectorClear(color4f);
1184                         color4f[3] = 1;
1185                 }
1186         }
1187 }
1188
1189 static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1190 {
1191         float *color4f = varray_color4f;
1192         float dist, dot, intensity, v[3], n[3];
1193         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1194         {
1195                 Matrix4x4_Transform(m, vertex3f, v);
1196                 if ((dist = fabs(v[2])) < 1)
1197                 {
1198                         Matrix4x4_Transform3x3(m, normal3f, n);
1199                         if ((dot = DotProduct(n, v)) > 0)
1200                         {
1201                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1202                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1203                                 VectorScale(lightcolor, intensity, color4f);
1204                                 color4f[3] = 1;
1205                         }
1206                         else
1207                         {
1208                                 VectorClear(color4f);
1209                                 color4f[3] = 1;
1210                         }
1211                 }
1212                 else
1213                 {
1214                         VectorClear(color4f);
1215                         color4f[3] = 1;
1216                 }
1217         }
1218 }
1219
1220 static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1221 {
1222         float *color4f = varray_color4f;
1223         float dot, intensity, v[3], n[3];
1224         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1225         {
1226                 Matrix4x4_Transform(m, vertex3f, v);
1227                 Matrix4x4_Transform3x3(m, normal3f, n);
1228                 if ((dot = DotProduct(n, v)) > 0)
1229                 {
1230                         intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1231                         VectorScale(lightcolor, intensity, color4f);
1232                         color4f[3] = 1;
1233                 }
1234                 else
1235                 {
1236                         VectorClear(color4f);
1237                         color4f[3] = 1;
1238                 }
1239         }
1240 }
1241
1242 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1243 #define USETEXMATRIX
1244
1245 #ifndef USETEXMATRIX
1246 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1247 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1248 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1249 {
1250         do
1251         {
1252                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1253                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1254                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1255                 vertex3f += 3;
1256                 tc3f += 3;
1257         }
1258         while (--numverts);
1259 }
1260
1261 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1262 {
1263         do
1264         {
1265                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1266                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1267                 vertex3f += 3;
1268                 tc2f += 2;
1269         }
1270         while (--numverts);
1271 }
1272 #endif
1273
1274 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)
1275 {
1276         int i;
1277         float lightdir[3];
1278         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1279         {
1280                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1281                 // the cubemap normalizes this for us
1282                 out3f[0] = DotProduct(svector3f, lightdir);
1283                 out3f[1] = DotProduct(tvector3f, lightdir);
1284                 out3f[2] = DotProduct(normal3f, lightdir);
1285         }
1286 }
1287
1288 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)
1289 {
1290         int i;
1291         float lightdir[3], eyedir[3], halfdir[3];
1292         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1293         {
1294                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1295                 VectorNormalizeFast(lightdir);
1296                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1297                 VectorNormalizeFast(eyedir);
1298                 VectorAdd(lightdir, eyedir, halfdir);
1299                 // the cubemap normalizes this for us
1300                 out3f[0] = DotProduct(svector3f, halfdir);
1301                 out3f[1] = DotProduct(tvector3f, halfdir);
1302                 out3f[2] = DotProduct(normal3f, halfdir);
1303         }
1304 }
1305
1306 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)
1307 {
1308         int renders;
1309         float color[3], color2[3], colorscale;
1310         rmeshstate_t m;
1311         if (!bumptexture)
1312                 bumptexture = r_shadow_blankbumptexture;
1313         if (!glosstexture)
1314                 glosstexture = r_shadow_blankglosstexture;
1315         GL_DepthMask(false);
1316         GL_DepthTest(true);
1317         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1318         {
1319                 if (lighting & LIGHTING_DIFFUSE)
1320                 {
1321                         GL_Color(1,1,1,1);
1322                         colorscale = r_shadow_lightintensityscale.value;
1323                         // colorscale accounts for how much we multiply the brightness
1324                         // during combine.
1325                         //
1326                         // mult is how many times the final pass of the lighting will be
1327                         // performed to get more brightness than otherwise possible.
1328                         //
1329                         // Limit mult to 64 for sanity sake.
1330                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1331                         {
1332                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1333                                 memset(&m, 0, sizeof(m));
1334                                 m.pointer_vertex = vertex3f;
1335                                 m.tex[0] = R_GetTexture(bumptexture);
1336                                 m.texcombinergb[0] = GL_REPLACE;
1337                                 m.pointer_texcoord[0] = texcoord2f;
1338                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1339                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1340                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1341                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1342                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1343 #ifdef USETEXMATRIX
1344                                 m.pointer_texcoord3f[2] = vertex3f;
1345                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1346 #else
1347                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1348                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1349 #endif
1350                                 R_Mesh_State(&m);
1351                                 GL_ColorMask(0,0,0,1);
1352                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1353                                 GL_LockArrays(0, numverts);
1354                                 R_Mesh_Draw(numverts, numtriangles, elements);
1355                                 GL_LockArrays(0, 0);
1356                                 c_rt_lightmeshes++;
1357                                 c_rt_lighttris += numtriangles;
1358         
1359                                 memset(&m, 0, sizeof(m));
1360                                 m.pointer_vertex = vertex3f;
1361                                 m.tex[0] = R_GetTexture(basetexture);
1362                                 m.pointer_texcoord[0] = texcoord2f;
1363                                 if (lightcubemap)
1364                                 {
1365                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1366 #ifdef USETEXMATRIX
1367                                         m.pointer_texcoord3f[1] = vertex3f;
1368                                         m.texmatrix[1] = *matrix_modeltolight;
1369 #else
1370                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1371                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1372 #endif
1373                                 }
1374                         }
1375                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1376                         {
1377                                 // 1/2/2 3D combine path (original Radeon)
1378                                 memset(&m, 0, sizeof(m));
1379                                 m.pointer_vertex = vertex3f;
1380                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1381 #ifdef USETEXMATRIX
1382                                 m.pointer_texcoord3f[0] = vertex3f;
1383                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1384 #else
1385                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1386                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1387 #endif
1388                                 R_Mesh_State(&m);
1389                                 GL_ColorMask(0,0,0,1);
1390                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1391                                 GL_LockArrays(0, numverts);
1392                                 R_Mesh_Draw(numverts, numtriangles, elements);
1393                                 GL_LockArrays(0, 0);
1394                                 c_rt_lightmeshes++;
1395                                 c_rt_lighttris += numtriangles;
1396         
1397                                 memset(&m, 0, sizeof(m));
1398                                 m.pointer_vertex = vertex3f;
1399                                 m.tex[0] = R_GetTexture(bumptexture);
1400                                 m.texcombinergb[0] = GL_REPLACE;
1401                                 m.pointer_texcoord[0] = texcoord2f;
1402                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1403                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1404                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1405                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1406                                 R_Mesh_State(&m);
1407                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1408                                 GL_LockArrays(0, numverts);
1409                                 R_Mesh_Draw(numverts, numtriangles, elements);
1410                                 GL_LockArrays(0, 0);
1411                                 c_rt_lightmeshes++;
1412                                 c_rt_lighttris += numtriangles;
1413         
1414                                 memset(&m, 0, sizeof(m));
1415                                 m.pointer_vertex = vertex3f;
1416                                 m.tex[0] = R_GetTexture(basetexture);
1417                                 m.pointer_texcoord[0] = texcoord2f;
1418                                 if (lightcubemap)
1419                                 {
1420                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1421 #ifdef USETEXMATRIX
1422                                         m.pointer_texcoord3f[1] = vertex3f;
1423                                         m.texmatrix[1] = *matrix_modeltolight;
1424 #else
1425                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1426                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1427 #endif
1428                                 }
1429                         }
1430                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1431                         {
1432                                 // 2/2 3D combine path (original Radeon)
1433                                 memset(&m, 0, sizeof(m));
1434                                 m.pointer_vertex = vertex3f;
1435                                 m.tex[0] = R_GetTexture(bumptexture);
1436                                 m.texcombinergb[0] = GL_REPLACE;
1437                                 m.pointer_texcoord[0] = texcoord2f;
1438                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1439                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1440                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1441                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1442                                 R_Mesh_State(&m);
1443                                 GL_ColorMask(0,0,0,1);
1444                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1445                                 GL_LockArrays(0, numverts);
1446                                 R_Mesh_Draw(numverts, numtriangles, elements);
1447                                 GL_LockArrays(0, 0);
1448                                 c_rt_lightmeshes++;
1449                                 c_rt_lighttris += numtriangles;
1450         
1451                                 memset(&m, 0, sizeof(m));
1452                                 m.pointer_vertex = vertex3f;
1453                                 m.tex[0] = R_GetTexture(basetexture);
1454                                 m.pointer_texcoord[0] = texcoord2f;
1455                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1456 #ifdef USETEXMATRIX
1457                                 m.pointer_texcoord3f[1] = vertex3f;
1458                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1459 #else
1460                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1461                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1462 #endif
1463                         }
1464                         else if (r_textureunits.integer >= 4)
1465                         {
1466                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1467                                 memset(&m, 0, sizeof(m));
1468                                 m.pointer_vertex = vertex3f;
1469                                 m.tex[0] = R_GetTexture(bumptexture);
1470                                 m.texcombinergb[0] = GL_REPLACE;
1471                                 m.pointer_texcoord[0] = texcoord2f;
1472                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1473                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1474                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1475                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1476                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1477 #ifdef USETEXMATRIX
1478                                 m.pointer_texcoord3f[2] = vertex3f;
1479                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1480 #else
1481                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
1482                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1483 #endif
1484                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1485 #ifdef USETEXMATRIX
1486                                 m.pointer_texcoord3f[3] = vertex3f;
1487                                 m.texmatrix[3] = *matrix_modeltoattenuationz;
1488 #else
1489                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
1490                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1491 #endif
1492                                 R_Mesh_State(&m);
1493                                 GL_ColorMask(0,0,0,1);
1494                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1495                                 GL_LockArrays(0, numverts);
1496                                 R_Mesh_Draw(numverts, numtriangles, elements);
1497                                 GL_LockArrays(0, 0);
1498                                 c_rt_lightmeshes++;
1499                                 c_rt_lighttris += numtriangles;
1500         
1501                                 memset(&m, 0, sizeof(m));
1502                                 m.pointer_vertex = vertex3f;
1503                                 m.tex[0] = R_GetTexture(basetexture);
1504                                 m.pointer_texcoord[0] = texcoord2f;
1505                                 if (lightcubemap)
1506                                 {
1507                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1508 #ifdef USETEXMATRIX
1509                                         m.pointer_texcoord3f[1] = vertex3f;
1510                                         m.texmatrix[1] = *matrix_modeltolight;
1511 #else
1512                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1513                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1514 #endif
1515                                 }
1516                         }
1517                         else
1518                         {
1519                                 // 2/2/2 2D combine path (any dot3 card)
1520                                 memset(&m, 0, sizeof(m));
1521                                 m.pointer_vertex = vertex3f;
1522                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1523 #ifdef USETEXMATRIX
1524                                 m.pointer_texcoord3f[0] = vertex3f;
1525                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1526 #else
1527                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1528                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1529 #endif
1530                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1531 #ifdef USETEXMATRIX
1532                                 m.pointer_texcoord3f[1] = vertex3f;
1533                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1534 #else
1535                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1536                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1537 #endif
1538                                 R_Mesh_State(&m);
1539                                 GL_ColorMask(0,0,0,1);
1540                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1541                                 GL_LockArrays(0, numverts);
1542                                 R_Mesh_Draw(numverts, numtriangles, elements);
1543                                 GL_LockArrays(0, 0);
1544                                 c_rt_lightmeshes++;
1545                                 c_rt_lighttris += numtriangles;
1546         
1547                                 memset(&m, 0, sizeof(m));
1548                                 m.pointer_vertex = vertex3f;
1549                                 m.tex[0] = R_GetTexture(bumptexture);
1550                                 m.texcombinergb[0] = GL_REPLACE;
1551                                 m.pointer_texcoord[0] = texcoord2f;
1552                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1553                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1554                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1555                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1556                                 R_Mesh_State(&m);
1557                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1558                                 GL_LockArrays(0, numverts);
1559                                 R_Mesh_Draw(numverts, numtriangles, elements);
1560                                 GL_LockArrays(0, 0);
1561                                 c_rt_lightmeshes++;
1562                                 c_rt_lighttris += numtriangles;
1563         
1564                                 memset(&m, 0, sizeof(m));
1565                                 m.pointer_vertex = vertex3f;
1566                                 m.tex[0] = R_GetTexture(basetexture);
1567                                 m.pointer_texcoord[0] = texcoord2f;
1568                                 if (lightcubemap)
1569                                 {
1570                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1571 #ifdef USETEXMATRIX
1572                                         m.pointer_texcoord3f[1] = vertex3f;
1573                                         m.texmatrix[1] = *matrix_modeltolight;
1574 #else
1575                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1576                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1577 #endif
1578                                 }
1579                         }
1580                         // this final code is shared
1581                         R_Mesh_State(&m);
1582                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1583                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1584                         VectorScale(lightcolor, colorscale, color2);
1585                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1586                         {
1587                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1588                                 GL_LockArrays(0, numverts);
1589                                 R_Mesh_Draw(numverts, numtriangles, elements);
1590                                 GL_LockArrays(0, 0);
1591                                 c_rt_lightmeshes++;
1592                                 c_rt_lighttris += numtriangles;
1593                         }
1594                 }
1595                 if ((lighting & LIGHTING_SPECULAR) && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1596                 {
1597                         // FIXME: detect blendsquare!
1598                         //if (gl_support_blendsquare)
1599                         {
1600                                 colorscale = r_shadow_lightintensityscale.value * r_shadow_glossintensity.value;
1601                                 if (glosstexture == r_shadow_blankglosstexture)
1602                                         colorscale *= r_shadow_gloss2intensity.value;
1603                                 GL_Color(1,1,1,1);
1604                                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1605                                 {
1606                                         // 2/0/0/1/2 3D combine blendsquare path
1607                                         memset(&m, 0, sizeof(m));
1608                                         m.pointer_vertex = vertex3f;
1609                                         m.tex[0] = R_GetTexture(bumptexture);
1610                                         m.pointer_texcoord[0] = texcoord2f;
1611                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1612                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1613                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1614                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1615                                         R_Mesh_State(&m);
1616                                         GL_ColorMask(0,0,0,1);
1617                                         // this squares the result
1618                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1619                                         GL_LockArrays(0, numverts);
1620                                         R_Mesh_Draw(numverts, numtriangles, elements);
1621                                         GL_LockArrays(0, 0);
1622                                         c_rt_lightmeshes++;
1623                                         c_rt_lighttris += numtriangles;
1624                 
1625                                         memset(&m, 0, sizeof(m));
1626                                         m.pointer_vertex = vertex3f;
1627                                         R_Mesh_State(&m);
1628                                         GL_LockArrays(0, numverts);
1629                                         // square alpha in framebuffer a few times to make it shiny
1630                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1631                                         // these comments are a test run through this math for intensity 0.5
1632                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1633                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1634                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1635                                         R_Mesh_Draw(numverts, numtriangles, elements);
1636                                         c_rt_lightmeshes++;
1637                                         c_rt_lighttris += numtriangles;
1638                                         R_Mesh_Draw(numverts, numtriangles, elements);
1639                                         c_rt_lightmeshes++;
1640                                         c_rt_lighttris += numtriangles;
1641                                         GL_LockArrays(0, 0);
1642                 
1643                                         memset(&m, 0, sizeof(m));
1644                                         m.pointer_vertex = vertex3f;
1645                                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1646 #ifdef USETEXMATRIX
1647                                         m.pointer_texcoord3f[0] = vertex3f;
1648                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1649 #else
1650                                         m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1651                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1652 #endif
1653                                         R_Mesh_State(&m);
1654                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1655                                         GL_LockArrays(0, numverts);
1656                                         R_Mesh_Draw(numverts, numtriangles, elements);
1657                                         GL_LockArrays(0, 0);
1658                                         c_rt_lightmeshes++;
1659                                         c_rt_lighttris += numtriangles;
1660                 
1661                                         memset(&m, 0, sizeof(m));
1662                                         m.pointer_vertex = vertex3f;
1663                                         m.tex[0] = R_GetTexture(glosstexture);
1664                                         m.pointer_texcoord[0] = texcoord2f;
1665                                         if (lightcubemap)
1666                                         {
1667                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1668 #ifdef USETEXMATRIX
1669                                                 m.pointer_texcoord3f[1] = vertex3f;
1670                                                 m.texmatrix[1] = *matrix_modeltolight;
1671 #else
1672                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1673                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1674 #endif
1675                                         }
1676                                 }
1677                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1678                                 {
1679                                         // 2/0/0/2 3D combine blendsquare path
1680                                         memset(&m, 0, sizeof(m));
1681                                         m.pointer_vertex = vertex3f;
1682                                         m.tex[0] = R_GetTexture(bumptexture);
1683                                         m.pointer_texcoord[0] = texcoord2f;
1684                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1685                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1686                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1687                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1688                                         R_Mesh_State(&m);
1689                                         GL_ColorMask(0,0,0,1);
1690                                         // this squares the result
1691                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1692                                         GL_LockArrays(0, numverts);
1693                                         R_Mesh_Draw(numverts, numtriangles, elements);
1694                                         GL_LockArrays(0, 0);
1695                                         c_rt_lightmeshes++;
1696                                         c_rt_lighttris += numtriangles;
1697                 
1698                                         memset(&m, 0, sizeof(m));
1699                                         m.pointer_vertex = vertex3f;
1700                                         R_Mesh_State(&m);
1701                                         GL_LockArrays(0, numverts);
1702                                         // square alpha in framebuffer a few times to make it shiny
1703                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1704                                         // these comments are a test run through this math for intensity 0.5
1705                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1706                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1707                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1708                                         R_Mesh_Draw(numverts, numtriangles, elements);
1709                                         c_rt_lightmeshes++;
1710                                         c_rt_lighttris += numtriangles;
1711                                         R_Mesh_Draw(numverts, numtriangles, elements);
1712                                         c_rt_lightmeshes++;
1713                                         c_rt_lighttris += numtriangles;
1714                                         GL_LockArrays(0, 0);
1715                 
1716                                         memset(&m, 0, sizeof(m));
1717                                         m.pointer_vertex = vertex3f;
1718                                         m.tex[0] = R_GetTexture(glosstexture);
1719                                         m.pointer_texcoord[0] = texcoord2f;
1720                                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1721 #ifdef USETEXMATRIX
1722                                         m.pointer_texcoord3f[1] = vertex3f;
1723                                         m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1724 #else
1725                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1726                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1727 #endif
1728                                 }
1729                                 else
1730                                 {
1731                                         // 2/0/0/2/2 2D combine blendsquare path
1732                                         memset(&m, 0, sizeof(m));
1733                                         m.pointer_vertex = vertex3f;
1734                                         m.tex[0] = R_GetTexture(bumptexture);
1735                                         m.pointer_texcoord[0] = texcoord2f;
1736                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1737                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1738                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1739                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1740                                         R_Mesh_State(&m);
1741                                         GL_ColorMask(0,0,0,1);
1742                                         // this squares the result
1743                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1744                                         GL_LockArrays(0, numverts);
1745                                         R_Mesh_Draw(numverts, numtriangles, elements);
1746                                         GL_LockArrays(0, 0);
1747                                         c_rt_lightmeshes++;
1748                                         c_rt_lighttris += numtriangles;
1749                 
1750                                         memset(&m, 0, sizeof(m));
1751                                         m.pointer_vertex = vertex3f;
1752                                         R_Mesh_State(&m);
1753                                         GL_LockArrays(0, numverts);
1754                                         // square alpha in framebuffer a few times to make it shiny
1755                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1756                                         // these comments are a test run through this math for intensity 0.5
1757                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1758                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1759                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1760                                         R_Mesh_Draw(numverts, numtriangles, elements);
1761                                         c_rt_lightmeshes++;
1762                                         c_rt_lighttris += numtriangles;
1763                                         R_Mesh_Draw(numverts, numtriangles, elements);
1764                                         c_rt_lightmeshes++;
1765                                         c_rt_lighttris += numtriangles;
1766                                         GL_LockArrays(0, 0);
1767                 
1768                                         memset(&m, 0, sizeof(m));
1769                                         m.pointer_vertex = vertex3f;
1770                                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1771 #ifdef USETEXMATRIX
1772                                         m.pointer_texcoord3f[0] = vertex3f;
1773                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1774 #else
1775                                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1776                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1777 #endif
1778                                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1779 #ifdef USETEXMATRIX
1780                                         m.pointer_texcoord3f[1] = vertex3f;
1781                                         m.texmatrix[1] = *matrix_modeltoattenuationz;
1782 #else
1783                                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1784                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1785 #endif
1786                                         R_Mesh_State(&m);
1787                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1788                                         GL_LockArrays(0, numverts);
1789                                         R_Mesh_Draw(numverts, numtriangles, elements);
1790                                         GL_LockArrays(0, 0);
1791                                         c_rt_lightmeshes++;
1792                                         c_rt_lighttris += numtriangles;
1793                 
1794                                         memset(&m, 0, sizeof(m));
1795                                         m.pointer_vertex = vertex3f;
1796                                         m.tex[0] = R_GetTexture(glosstexture);
1797                                         m.pointer_texcoord[0] = texcoord2f;
1798                                         if (lightcubemap)
1799                                         {
1800                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1801 #ifdef USETEXMATRIX
1802                                                 m.pointer_texcoord3f[1] = vertex3f;
1803                                                 m.texmatrix[1] = *matrix_modeltolight;
1804 #else
1805                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1806                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1807 #endif
1808                                         }
1809                                 }
1810                         }
1811                         R_Mesh_State(&m);
1812                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1813                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1814                         VectorScale(lightcolor, colorscale, color2);
1815                         GL_LockArrays(0, numverts);
1816                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1817                         {
1818                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1819                                 R_Mesh_Draw(numverts, numtriangles, elements);
1820                                 c_rt_lightmeshes++;
1821                                 c_rt_lighttris += numtriangles;
1822                         }
1823                         GL_LockArrays(0, 0);
1824                 }
1825         }
1826         else
1827         {
1828                 if (lighting & LIGHTING_DIFFUSE)
1829                 {
1830                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1831                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1832                         memset(&m, 0, sizeof(m));
1833                         m.pointer_vertex = vertex3f;
1834                         m.pointer_color = varray_color4f;
1835                         m.tex[0] = R_GetTexture(basetexture);
1836                         m.pointer_texcoord[0] = texcoord2f;
1837                         if (r_textureunits.integer >= 2)
1838                         {
1839                                 // voodoo2
1840                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1841 #ifdef USETEXMATRIX
1842                                 m.pointer_texcoord3f[1] = vertex3f;
1843                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1844 #else
1845                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1846                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1847 #endif
1848                                 if (r_textureunits.integer >= 3)
1849                                 {
1850                                         // Geforce3/Radeon class but not using dot3
1851                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1852 #ifdef USETEXMATRIX
1853                                         m.pointer_texcoord3f[2] = vertex3f;
1854                                         m.texmatrix[2] = *matrix_modeltoattenuationz;
1855 #else
1856                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1857                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
1858 #endif
1859                                 }
1860                         }
1861                         R_Mesh_State(&m);
1862                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1863                         {
1864                                 color[0] = bound(0, color2[0], 1);
1865                                 color[1] = bound(0, color2[1], 1);
1866                                 color[2] = bound(0, color2[2], 1);
1867                                 if (r_textureunits.integer >= 3)
1868                                         R_Shadow_VertexShading(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1869                                 else if (r_textureunits.integer >= 2)
1870                                         R_Shadow_VertexShadingWithZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1871                                 else
1872                                         R_Shadow_VertexShadingWithXYZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1873                                 GL_LockArrays(0, numverts);
1874                                 R_Mesh_Draw(numverts, numtriangles, elements);
1875                                 GL_LockArrays(0, 0);
1876                                 c_rt_lightmeshes++;
1877                                 c_rt_lighttris += numtriangles;
1878                         }
1879                 }
1880         }
1881 }
1882
1883 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1884 {
1885         int j, k;
1886         float scale;
1887         R_RTLight_Uncompile(rtlight);
1888         memset(rtlight, 0, sizeof(*rtlight));
1889
1890         VectorCopy(light->origin, rtlight->shadoworigin);
1891         VectorCopy(light->color, rtlight->color);
1892         rtlight->radius = light->radius;
1893         //rtlight->cullradius = rtlight->radius;
1894         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
1895         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1896         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1897         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1898         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1899         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1900         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1901         rtlight->cubemapname[0] = 0;
1902         if (light->cubemapname[0])
1903                 strcpy(rtlight->cubemapname, light->cubemapname);
1904         else if (light->cubemapnum > 0)
1905                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1906         rtlight->shadow = light->shadow;
1907         rtlight->corona = light->corona;
1908         rtlight->style = light->style;
1909         rtlight->isstatic = isstatic;
1910         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1911         // ConcatScale won't work here because this needs to scale rotate and
1912         // translate, not just rotate
1913         scale = 1.0f / rtlight->radius;
1914         for (k = 0;k < 3;k++)
1915                 for (j = 0;j < 4;j++)
1916                         rtlight->matrix_worldtolight.m[k][j] *= scale;
1917         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1918         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1919
1920         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1921         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1922         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.125f, rtlight->lightmap_light);
1923         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1924 }
1925
1926 // compiles rtlight geometry
1927 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1928 void R_RTLight_Compile(rtlight_t *rtlight)
1929 {
1930         int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
1931         entity_render_t *ent = &cl_entities[0].render;
1932         model_t *model = ent->model;
1933
1934         // compile the light
1935         rtlight->compiled = true;
1936         rtlight->static_numclusters = 0;
1937         rtlight->static_numclusterpvsbytes = 0;
1938         rtlight->static_clusterlist = NULL;
1939         rtlight->static_clusterpvs = NULL;
1940         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1941         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1942         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1943         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1944         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1945         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1946
1947         if (model && model->GetLightInfo)
1948         {
1949                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
1950                 r_shadow_compilingrtlight = rtlight;
1951                 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
1952                 R_Shadow_EnlargeSurfaceBuffer(model->nummodelsurfaces); 
1953                 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);
1954                 if (numclusters)
1955                 {
1956                         rtlight->static_numclusters = numclusters;
1957                         rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
1958                         rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1959                         rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
1960                         memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1961                         memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
1962                 }
1963                 if (model->DrawShadowVolume && rtlight->shadow)
1964                 {
1965                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1966                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
1967                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1968                 }
1969                 if (model->DrawLight)
1970                 {
1971                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1972                         model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, numsurfaces, r_shadow_buffer_surfacelist);
1973                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1974                 }
1975                 // switch back to rendering when DrawShadowVolume or DrawLight is called
1976                 r_shadow_compilingrtlight = NULL;
1977         }
1978
1979
1980         // use smallest available cullradius - box radius or light radius
1981         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1982         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1983
1984         shadowmeshes = 0;
1985         shadowtris = 0;
1986         if (rtlight->static_meshchain_shadow)
1987         {
1988                 shadowmesh_t *mesh;
1989                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1990                 {
1991                         shadowmeshes++;
1992                         shadowtris += mesh->numtriangles;
1993                 }
1994         }
1995
1996         lightmeshes = 0;
1997         lighttris = 0;
1998         if (rtlight->static_meshchain_light)
1999         {
2000                 shadowmesh_t *mesh;
2001                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2002                 {
2003                         lightmeshes++;
2004                         lighttris += mesh->numtriangles;
2005                 }
2006         }
2007
2008         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);
2009 }
2010
2011 void R_RTLight_Uncompile(rtlight_t *rtlight)
2012 {
2013         if (rtlight->compiled)
2014         {
2015                 if (rtlight->static_meshchain_shadow)
2016                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2017                 rtlight->static_meshchain_shadow = NULL;
2018                 if (rtlight->static_meshchain_light)
2019                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2020                 rtlight->static_meshchain_light = NULL;
2021                 if (rtlight->static_clusterlist)
2022                         Mem_Free(rtlight->static_clusterlist);
2023                 rtlight->static_clusterlist = NULL;
2024                 if (rtlight->static_clusterpvs)
2025                         Mem_Free(rtlight->static_clusterpvs);
2026                 rtlight->static_clusterpvs = NULL;
2027                 rtlight->static_numclusters = 0;
2028                 rtlight->static_numclusterpvsbytes = 0;
2029                 rtlight->compiled = false;
2030         }
2031 }
2032
2033 void R_Shadow_UncompileWorldLights(void)
2034 {
2035         dlight_t *light;
2036         for (light = r_shadow_worldlightchain;light;light = light->next)
2037                 R_RTLight_Uncompile(&light->rtlight);
2038 }
2039
2040 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2041 {
2042         int i, shadow;
2043         entity_render_t *ent;
2044         float f;
2045         vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2046         rtexture_t *cubemaptexture;
2047         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2048         int numclusters, numsurfaces;
2049         int *clusterlist, *surfacelist;
2050         qbyte *clusterpvs;
2051         vec3_t cullmins, cullmaxs;
2052         shadowmesh_t *mesh;
2053         rmeshstate_t m;
2054
2055         // loading is done before visibility checks because loading should happen
2056         // all at once at the start of a level, not when it stalls gameplay.
2057         // (especially important to benchmarks)
2058         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2059                 R_RTLight_Compile(rtlight);
2060         if (rtlight->cubemapname[0])
2061                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2062         else
2063                 cubemaptexture = NULL;
2064
2065         cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2066         cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2067         cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2068         cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2069         cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2070         cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2071         if (d_lightstylevalue[rtlight->style] <= 0)
2072                 return;
2073         numclusters = 0;
2074         clusterlist = NULL;
2075         clusterpvs = NULL;
2076         numsurfaces = 0;
2077         surfacelist = NULL;
2078         if (rtlight->compiled && r_shadow_staticworldlights.integer)
2079         {
2080                 // compiled light, world available and can receive realtime lighting
2081                 // retrieve cluster information
2082                 numclusters = rtlight->static_numclusters;
2083                 clusterlist = rtlight->static_clusterlist;
2084                 clusterpvs = rtlight->static_clusterpvs;
2085                 VectorCopy(rtlight->cullmins, cullmins);
2086                 VectorCopy(rtlight->cullmaxs, cullmaxs);
2087         }
2088         else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
2089         {
2090                 // dynamic light, world available and can receive realtime lighting
2091                 // if the light box is offscreen, skip it right away
2092                 if (R_CullBox(cullmins, cullmaxs))
2093                         return;
2094                 // calculate lit surfaces and clusters
2095                 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
2096                 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->nummodelsurfaces); 
2097                 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);
2098                 clusterlist = r_shadow_buffer_clusterlist;
2099                 clusterpvs = r_shadow_buffer_clusterpvs;
2100                 surfacelist = r_shadow_buffer_surfacelist;
2101         }
2102         // if the reduced cluster bounds are offscreen, skip it
2103         if (R_CullBox(cullmins, cullmaxs))
2104                 return;
2105         // check if light is illuminating any visible clusters
2106         if (numclusters)
2107         {
2108                 for (i = 0;i < numclusters;i++)
2109                         if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
2110                                 break;
2111                 if (i == numclusters)
2112                         return;
2113         }
2114         // set up a scissor rectangle for this light
2115         if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
2116                 return;
2117
2118         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2119         VectorScale(rtlight->color, f, lightcolor);
2120         /*
2121         if (rtlight->selected)
2122         {
2123                 f = 2 + sin(realtime * M_PI * 4.0);
2124                 VectorScale(lightcolor, f, lightcolor);
2125         }
2126         */
2127
2128 #if 1
2129         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));
2130 #else
2131         shadow = false;
2132         if (rtlight->shadow)
2133         {
2134                 if (rtlight->isstatic)
2135                         shadow = r_shadow_realtime_world_shadows.integer;
2136                 else
2137                 {
2138                         if (r_shadow_realtime_world.integer)
2139                                 shadow = r_shadow_realtime_world_dlightshadows.integer;
2140                         else
2141                                 shadow = r_shadow_realtime_dlight_shadows.integer;
2142                 }
2143         }
2144 #endif
2145
2146         if (shadow && (gl_stencil || visiblevolumes))
2147         {
2148                 if (!visiblevolumes)
2149                         R_Shadow_Stage_ShadowVolumes();
2150                 ent = &cl_entities[0].render;
2151                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2152                 {
2153                         memset(&m, 0, sizeof(m));
2154                         R_Mesh_Matrix(&ent->matrix);
2155                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2156                         {
2157                                 m.pointer_vertex = mesh->vertex3f;
2158                                 R_Mesh_State(&m);
2159                                 GL_LockArrays(0, mesh->numverts);
2160                                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2161                                 {
2162                                         // decrement stencil if frontface is behind depthbuffer
2163                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2164                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2165                                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2166                                         c_rtcached_shadowmeshes++;
2167                                         c_rtcached_shadowtris += mesh->numtriangles;
2168                                         // increment stencil if backface is behind depthbuffer
2169                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2170                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2171                                 }
2172                                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2173                                 c_rtcached_shadowmeshes++;
2174                                 c_rtcached_shadowtris += mesh->numtriangles;
2175                                 GL_LockArrays(0, 0);
2176                         }
2177                 }
2178                 else
2179                 {
2180                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2181                         ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2182                 }
2183                 if (r_drawentities.integer)
2184                 {
2185                         for (i = 0;i < r_refdef.numentities;i++)
2186                         {
2187                                 ent = r_refdef.entities[i];
2188                                 // rough checks
2189                                 if (r_shadow_cull.integer)
2190                                 {
2191                                         if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2192                                                 continue;
2193                                         if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2194                                                 continue;
2195                                 }
2196                                 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2197                                         continue;
2198                                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2199                                 // light emitting entities should not cast their own shadow
2200                                 if (VectorLength2(relativelightorigin) < 0.1)
2201                                         continue;
2202                                 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist);
2203                         }
2204                 }
2205         }
2206
2207         if (!visiblevolumes)
2208         {
2209                 if (shadow && gl_stencil)
2210                         R_Shadow_Stage_LightWithShadows();
2211                 else
2212                         R_Shadow_Stage_LightWithoutShadows();
2213
2214                 ent = &cl_entities[0].render;
2215                 if (ent->model && ent->model->DrawLight)
2216                 {
2217                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2218                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2219                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2220                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2221                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2222                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2223                         {
2224                                 R_Mesh_Matrix(&ent->matrix);
2225                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2226                                         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);
2227                         }
2228                         else
2229                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, numsurfaces, surfacelist);
2230                 }
2231                 if (r_drawentities.integer)
2232                 {
2233                         for (i = 0;i < r_refdef.numentities;i++)
2234                         {
2235                                 ent = r_refdef.entities[i];
2236                                 if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2237                                 {
2238                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2239                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2240                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2241                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2242                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2243                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->nummodelsurfaces, ent->model->surfacelist);
2244                                 }
2245                         }
2246                 }
2247         }
2248 }
2249
2250 void R_ShadowVolumeLighting(int visiblevolumes)
2251 {
2252         int lnum;
2253         dlight_t *light;
2254         rmeshstate_t m;
2255
2256         if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2257                 R_Shadow_EditLights_Reload_f();
2258
2259         if (visiblevolumes)
2260         {
2261                 memset(&m, 0, sizeof(m));
2262                 R_Mesh_State(&m);
2263
2264                 GL_BlendFunc(GL_ONE, GL_ONE);
2265                 GL_DepthMask(false);
2266                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2267                 qglDisable(GL_CULL_FACE);
2268                 GL_Color(0.0, 0.0125, 0.1, 1);
2269         }
2270         else
2271                 R_Shadow_Stage_Begin();
2272         if (r_shadow_realtime_world.integer)
2273         {
2274                 if (r_shadow_debuglight.integer >= 0)
2275                 {
2276                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2277                                 if (lnum == r_shadow_debuglight.integer)
2278                                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2279                 }
2280                 else
2281                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2282                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2283         }
2284         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2285                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2286                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2287
2288         if (visiblevolumes)
2289         {
2290                 qglEnable(GL_CULL_FACE);
2291                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2292         }
2293         else
2294                 R_Shadow_Stage_End();
2295 }
2296
2297 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2298 typedef struct suffixinfo_s
2299 {
2300         char *suffix;
2301         qboolean flipx, flipy, flipdiagonal;
2302 }
2303 suffixinfo_t;
2304 static suffixinfo_t suffix[3][6] =
2305 {
2306         {
2307                 {"px",   false, false, false},
2308                 {"nx",   false, false, false},
2309                 {"py",   false, false, false},
2310                 {"ny",   false, false, false},
2311                 {"pz",   false, false, false},
2312                 {"nz",   false, false, false}
2313         },
2314         {
2315                 {"posx", false, false, false},
2316                 {"negx", false, false, false},
2317                 {"posy", false, false, false},
2318                 {"negy", false, false, false},
2319                 {"posz", false, false, false},
2320                 {"negz", false, false, false}
2321         },
2322         {
2323                 {"rt",    true, false,  true},
2324                 {"lf",   false,  true,  true},
2325                 {"ft",    true,  true, false},
2326                 {"bk",   false, false, false},
2327                 {"up",    true, false,  true},
2328                 {"dn",    true, false,  true}
2329         }
2330 };
2331
2332 static int componentorder[4] = {0, 1, 2, 3};
2333
2334 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2335 {
2336         int i, j, cubemapsize;
2337         qbyte *cubemappixels, *image_rgba;
2338         rtexture_t *cubemaptexture;
2339         char name[256];
2340         // must start 0 so the first loadimagepixels has no requested width/height
2341         cubemapsize = 0;
2342         cubemappixels = NULL;
2343         cubemaptexture = NULL;
2344         // keep trying different suffix groups (posx, px, rt) until one loads
2345         for (j = 0;j < 3 && !cubemappixels;j++)
2346         {
2347                 // load the 6 images in the suffix group
2348                 for (i = 0;i < 6;i++)
2349                 {
2350                         // generate an image name based on the base and and suffix
2351                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2352                         // load it
2353                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2354                         {
2355                                 // an image loaded, make sure width and height are equal
2356                                 if (image_width == image_height)
2357                                 {
2358                                         // if this is the first image to load successfully, allocate the cubemap memory
2359                                         if (!cubemappixels && image_width >= 1)
2360                                         {
2361                                                 cubemapsize = image_width;
2362                                                 // note this clears to black, so unavailable sides are black
2363                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2364                                         }
2365                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2366                                         if (cubemappixels)
2367                                                 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);
2368                                 }
2369                                 else
2370                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2371                                 // free the image
2372                                 Mem_Free(image_rgba);
2373                         }
2374                 }
2375         }
2376         // if a cubemap loaded, upload it
2377         if (cubemappixels)
2378         {
2379                 if (!r_shadow_filters_texturepool)
2380                         r_shadow_filters_texturepool = R_AllocTexturePool();
2381                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2382                 Mem_Free(cubemappixels);
2383         }
2384         else
2385         {
2386                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2387                 for (j = 0;j < 3;j++)
2388                         for (i = 0;i < 6;i++)
2389                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2390                 Con_Print(" and was unable to find any of them.\n");
2391         }
2392         return cubemaptexture;
2393 }
2394
2395 rtexture_t *R_Shadow_Cubemap(const char *basename)
2396 {
2397         int i;
2398         for (i = 0;i < numcubemaps;i++)
2399                 if (!strcasecmp(cubemaps[i].basename, basename))
2400                         return cubemaps[i].texture;
2401         if (i >= MAX_CUBEMAPS)
2402                 return NULL;
2403         numcubemaps++;
2404         strcpy(cubemaps[i].basename, basename);
2405         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2406         return cubemaps[i].texture;
2407 }
2408
2409 void R_Shadow_FreeCubemaps(void)
2410 {
2411         numcubemaps = 0;
2412         R_FreeTexturePool(&r_shadow_filters_texturepool);
2413 }
2414
2415 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)
2416 {
2417         dlight_t *light;
2418
2419         if (radius < 15 || DotProduct(color, color) < 0.03)
2420         {
2421                 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2422                 return;
2423         }
2424
2425         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2426         VectorCopy(origin, light->origin);
2427         VectorCopy(angles, light->angles);
2428         VectorCopy(color, light->color);
2429         light->radius = radius;
2430         light->style = style;
2431         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2432         {
2433                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2434                 light->style = 0;
2435         }
2436         light->shadow = shadowenable;
2437         light->corona = corona;
2438         if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2439                 strcpy(light->cubemapname, cubemapname);
2440         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2441         light->next = r_shadow_worldlightchain;
2442         r_shadow_worldlightchain = light;
2443
2444         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2445 }
2446
2447 void R_Shadow_FreeWorldLight(dlight_t *light)
2448 {
2449         dlight_t **lightpointer;
2450         R_RTLight_Uncompile(&light->rtlight);
2451         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2452         if (*lightpointer != light)
2453                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2454         *lightpointer = light->next;
2455         Mem_Free(light);
2456 }
2457
2458 void R_Shadow_ClearWorldLights(void)
2459 {
2460         while (r_shadow_worldlightchain)
2461                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2462         r_shadow_selectedlight = NULL;
2463         R_Shadow_FreeCubemaps();
2464 }
2465
2466 void R_Shadow_SelectLight(dlight_t *light)
2467 {
2468         if (r_shadow_selectedlight)
2469                 r_shadow_selectedlight->selected = false;
2470         r_shadow_selectedlight = light;
2471         if (r_shadow_selectedlight)
2472                 r_shadow_selectedlight->selected = true;
2473 }
2474
2475 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2476 {
2477         float scale = r_editlights_cursorgrid.value * 0.5f;
2478         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);
2479 }
2480
2481 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2482 {
2483         float intensity;
2484         const dlight_t *light;
2485         light = calldata1;
2486         intensity = 0.5;
2487         if (light->selected)
2488                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2489         if (!light->shadow)
2490                 intensity *= 0.5f;
2491         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);
2492 }
2493
2494 void R_Shadow_DrawLightSprites(void)
2495 {
2496         int i;
2497         cachepic_t *pic;
2498         dlight_t *light;
2499
2500         for (i = 0;i < 5;i++)
2501         {
2502                 lighttextures[i] = NULL;
2503                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2504                         lighttextures[i] = pic->tex;
2505         }
2506
2507         for (light = r_shadow_worldlightchain;light;light = light->next)
2508                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2509         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2510 }
2511
2512 void R_Shadow_SelectLightInView(void)
2513 {
2514         float bestrating, rating, temp[3];
2515         dlight_t *best, *light;
2516         best = NULL;
2517         bestrating = 0;
2518         for (light = r_shadow_worldlightchain;light;light = light->next)
2519         {
2520                 VectorSubtract(light->origin, r_vieworigin, temp);
2521                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2522                 if (rating >= 0.95)
2523                 {
2524                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2525                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2526                         {
2527                                 bestrating = rating;
2528                                 best = light;
2529                         }
2530                 }
2531         }
2532         R_Shadow_SelectLight(best);
2533 }
2534
2535 void R_Shadow_LoadWorldLights(void)
2536 {
2537         int n, a, style, shadow;
2538         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2539         float origin[3], radius, color[3], angles[3], corona;
2540         if (cl.worldmodel == NULL)
2541         {
2542                 Con_Print("No map loaded.\n");
2543                 return;
2544         }
2545         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2546         strlcat (name, ".rtlights", sizeof (name));
2547         lightsstring = FS_LoadFile(name, tempmempool, false);
2548         if (lightsstring)
2549         {
2550                 s = lightsstring;
2551                 n = 0;
2552                 while (*s)
2553                 {
2554                         t = s;
2555                         /*
2556                         shadow = true;
2557                         for (;COM_Parse(t, true) && strcmp(
2558                         if (COM_Parse(t, true))
2559                         {
2560                                 if (com_token[0] == '!')
2561                                 {
2562                                         shadow = false;
2563                                         origin[0] = atof(com_token+1);
2564                                 }
2565                                 else
2566                                         origin[0] = atof(com_token);
2567                                 if (Com_Parse(t
2568                         }
2569                         */
2570                         t = s;
2571                         while (*s && *s != '\n')
2572                                 s++;
2573                         if (!*s)
2574                                 break;
2575                         *s = 0;
2576                         shadow = true;
2577                         // check for modifier flags
2578                         if (*t == '!')
2579                         {
2580                                 shadow = false;
2581                                 t++;
2582                         }
2583                         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]);
2584                         if (a < 13)
2585                                 VectorClear(angles);
2586                         if (a < 10)
2587                                 corona = 0;
2588                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2589                                 cubemapname[0] = 0;
2590                         *s = '\n';
2591                         if (a < 8)
2592                         {
2593                                 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);
2594                                 break;
2595                         }
2596                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2597                         radius *= r_editlights_rtlightssizescale.value;
2598                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2599                         s++;
2600                         n++;
2601                 }
2602                 if (*s)
2603                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2604                 Mem_Free(lightsstring);
2605         }
2606 }
2607
2608 void R_Shadow_SaveWorldLights(void)
2609 {
2610         dlight_t *light;
2611         int bufchars, bufmaxchars;
2612         char *buf, *oldbuf;
2613         char name[MAX_QPATH];
2614         char line[1024];
2615         if (!r_shadow_worldlightchain)
2616                 return;
2617         if (cl.worldmodel == NULL)
2618         {
2619                 Con_Print("No map loaded.\n");
2620                 return;
2621         }
2622         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2623         strlcat (name, ".rtlights", sizeof (name));
2624         bufchars = bufmaxchars = 0;
2625         buf = NULL;
2626         for (light = r_shadow_worldlightchain;light;light = light->next)
2627         {
2628                 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]);
2629                 if (bufchars + (int) strlen(line) > bufmaxchars)
2630                 {
2631                         bufmaxchars = bufchars + strlen(line) + 2048;
2632                         oldbuf = buf;
2633                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2634                         if (oldbuf)
2635                         {
2636                                 if (bufchars)
2637                                         memcpy(buf, oldbuf, bufchars);
2638                                 Mem_Free(oldbuf);
2639                         }
2640                 }
2641                 if (strlen(line))
2642                 {
2643                         memcpy(buf + bufchars, line, strlen(line));
2644                         bufchars += strlen(line);
2645                 }
2646         }
2647         if (bufchars)
2648                 FS_WriteFile(name, buf, bufchars);
2649         if (buf)
2650                 Mem_Free(buf);
2651 }
2652
2653 void R_Shadow_LoadLightsFile(void)
2654 {
2655         int n, a, style;
2656         char name[MAX_QPATH], *lightsstring, *s, *t;
2657         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2658         if (cl.worldmodel == NULL)
2659         {
2660                 Con_Print("No map loaded.\n");
2661                 return;
2662         }
2663         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2664         strlcat (name, ".lights", sizeof (name));
2665         lightsstring = FS_LoadFile(name, tempmempool, false);
2666         if (lightsstring)
2667         {
2668                 s = lightsstring;
2669                 n = 0;
2670                 while (*s)
2671                 {
2672                         t = s;
2673                         while (*s && *s != '\n')
2674                                 s++;
2675                         if (!*s)
2676                                 break;
2677                         *s = 0;
2678                         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);
2679                         *s = '\n';
2680                         if (a < 14)
2681                         {
2682                                 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);
2683                                 break;
2684                         }
2685                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2686                         radius = bound(15, radius, 4096);
2687                         VectorScale(color, (2.0f / (8388608.0f)), color);
2688                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2689                         s++;
2690                         n++;
2691                 }
2692                 if (*s)
2693                         Con_Printf("invalid lights file \"%s\"\n", name);
2694                 Mem_Free(lightsstring);
2695         }
2696 }
2697
2698 // tyrlite/hmap2 light types in the delay field
2699 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
2700
2701 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2702 {
2703         int entnum, style, islight, skin, pflags, effects, type, n;
2704         char key[256], value[1024];
2705         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
2706         const char *data;
2707
2708         if (cl.worldmodel == NULL)
2709         {
2710                 Con_Print("No map loaded.\n");
2711                 return;
2712         }
2713         data = cl.worldmodel->brush.entities;
2714         if (!data)
2715                 return;
2716         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2717         {
2718                 type = LIGHTTYPE_MINUSX;
2719                 origin[0] = origin[1] = origin[2] = 0;
2720                 originhack[0] = originhack[1] = originhack[2] = 0;
2721                 angles[0] = angles[1] = angles[2] = 0;
2722                 color[0] = color[1] = color[2] = 1;
2723                 light[0] = light[1] = light[2] = 1;light[3] = 300;
2724                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2725                 fadescale = 1;
2726                 lightscale = 1;
2727                 style = 0;
2728                 skin = 0;
2729                 pflags = 0;
2730                 effects = 0;
2731                 islight = false;
2732                 while (1)
2733                 {
2734                         if (!COM_ParseToken(&data, false))
2735                                 break; // error
2736                         if (com_token[0] == '}')
2737                                 break; // end of entity
2738                         if (com_token[0] == '_')
2739                                 strcpy(key, com_token + 1);
2740                         else
2741                                 strcpy(key, com_token);
2742                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2743                                 key[strlen(key)-1] = 0;
2744                         if (!COM_ParseToken(&data, false))
2745                                 break; // error
2746                         strcpy(value, com_token);
2747
2748                         // now that we have the key pair worked out...
2749                         if (!strcmp("light", key))
2750                         {
2751                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
2752                                 if (n == 1)
2753                                 {
2754                                         // quake
2755                                         light[0] = vec[0] * (1.0f / 256.0f);
2756                                         light[1] = vec[0] * (1.0f / 256.0f);
2757                                         light[2] = vec[0] * (1.0f / 256.0f);
2758                                         light[3] = vec[0];
2759                                 }
2760                                 else if (n == 4)
2761                                 {
2762                                         // halflife
2763                                         light[0] = vec[0] * (1.0f / 255.0f);
2764                                         light[1] = vec[1] * (1.0f / 255.0f);
2765                                         light[2] = vec[2] * (1.0f / 255.0f);
2766                                         light[3] = vec[3];
2767                                 }
2768                         }
2769                         else if (!strcmp("delay", key))
2770                                 type = atoi(value);
2771                         else if (!strcmp("origin", key))
2772                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2773                         else if (!strcmp("angle", key))
2774                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2775                         else if (!strcmp("angles", key))
2776                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2777                         else if (!strcmp("color", key))
2778                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2779                         else if (!strcmp("wait", key))
2780                                 fadescale = atof(value);
2781                         else if (!strcmp("classname", key))
2782                         {
2783                                 if (!strncmp(value, "light", 5))
2784                                 {
2785                                         islight = true;
2786                                         if (!strcmp(value, "light_fluoro"))
2787                                         {
2788                                                 originhack[0] = 0;
2789                                                 originhack[1] = 0;
2790                                                 originhack[2] = 0;
2791                                                 overridecolor[0] = 1;
2792                                                 overridecolor[1] = 1;
2793                                                 overridecolor[2] = 1;
2794                                         }
2795                                         if (!strcmp(value, "light_fluorospark"))
2796                                         {
2797                                                 originhack[0] = 0;
2798                                                 originhack[1] = 0;
2799                                                 originhack[2] = 0;
2800                                                 overridecolor[0] = 1;
2801                                                 overridecolor[1] = 1;
2802                                                 overridecolor[2] = 1;
2803                                         }
2804                                         if (!strcmp(value, "light_globe"))
2805                                         {
2806                                                 originhack[0] = 0;
2807                                                 originhack[1] = 0;
2808                                                 originhack[2] = 0;
2809                                                 overridecolor[0] = 1;
2810                                                 overridecolor[1] = 0.8;
2811                                                 overridecolor[2] = 0.4;
2812                                         }
2813                                         if (!strcmp(value, "light_flame_large_yellow"))
2814                                         {
2815                                                 originhack[0] = 0;
2816                                                 originhack[1] = 0;
2817                                                 originhack[2] = 48;
2818                                                 overridecolor[0] = 1;
2819                                                 overridecolor[1] = 0.5;
2820                                                 overridecolor[2] = 0.1;
2821                                         }
2822                                         if (!strcmp(value, "light_flame_small_yellow"))
2823                                         {
2824                                                 originhack[0] = 0;
2825                                                 originhack[1] = 0;
2826                                                 originhack[2] = 40;
2827                                                 overridecolor[0] = 1;
2828                                                 overridecolor[1] = 0.5;
2829                                                 overridecolor[2] = 0.1;
2830                                         }
2831                                         if (!strcmp(value, "light_torch_small_white"))
2832                                         {
2833                                                 originhack[0] = 0;
2834                                                 originhack[1] = 0;
2835                                                 originhack[2] = 40;
2836                                                 overridecolor[0] = 1;
2837                                                 overridecolor[1] = 0.5;
2838                                                 overridecolor[2] = 0.1;
2839                                         }
2840                                         if (!strcmp(value, "light_torch_small_walltorch"))
2841                                         {
2842                                                 originhack[0] = 0;
2843                                                 originhack[1] = 0;
2844                                                 originhack[2] = 40;
2845                                                 overridecolor[0] = 1;
2846                                                 overridecolor[1] = 0.5;
2847                                                 overridecolor[2] = 0.1;
2848                                         }
2849                                 }
2850                         }
2851                         else if (!strcmp("style", key))
2852                                 style = atoi(value);
2853                         else if (cl.worldmodel->type == mod_brushq3)
2854                         {
2855                                 if (!strcmp("scale", key))
2856                                         lightscale = atof(value);
2857                                 if (!strcmp("fade", key))
2858                                         fadescale = atof(value);
2859                         }
2860                         else if (!strcmp("skin", key))
2861                                 skin = (int)atof(value);
2862                         else if (!strcmp("pflags", key))
2863                                 pflags = (int)atof(value);
2864                         else if (!strcmp("effects", key))
2865                                 effects = (int)atof(value);
2866                 }
2867                 if (!islight)
2868                         continue;
2869                 if (lightscale <= 0)
2870                         lightscale = 1;
2871                 if (fadescale <= 0)
2872                         fadescale = 1;
2873                 if (color[0] == color[1] && color[0] == color[2])
2874                 {
2875                         color[0] *= overridecolor[0];
2876                         color[1] *= overridecolor[1];
2877                         color[2] *= overridecolor[2];
2878                 }
2879                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
2880                 color[0] = color[0] * light[0];
2881                 color[1] = color[1] * light[1];
2882                 color[2] = color[2] * light[2];
2883                 switch (type)
2884                 {
2885                 case LIGHTTYPE_MINUSX:
2886                         break;
2887                 case LIGHTTYPE_RECIPX:
2888                         radius *= 2;
2889                         VectorScale(color, (1.0f / 16.0f), color);
2890                         break;
2891                 case LIGHTTYPE_RECIPXX:
2892                         radius *= 2;
2893                         VectorScale(color, (1.0f / 16.0f), color);
2894                         break;
2895                 default:
2896                 case LIGHTTYPE_NONE:
2897                         break;
2898                 case LIGHTTYPE_SUN:
2899                         break;
2900                 case LIGHTTYPE_MINUSXX:
2901                         break;
2902                 }
2903                 VectorAdd(origin, originhack, origin);
2904                 if (radius >= 1)
2905                         R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2906         }
2907 }
2908
2909
2910 void R_Shadow_SetCursorLocationForView(void)
2911 {
2912         vec_t dist, push, frac;
2913         vec3_t dest, endpos, normal;
2914         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2915         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2916         if (frac < 1)
2917         {
2918                 dist = frac * r_editlights_cursordistance.value;
2919                 push = r_editlights_cursorpushback.value;
2920                 if (push > dist)
2921                         push = dist;
2922                 push = -push;
2923                 VectorMA(endpos, push, r_viewforward, endpos);
2924                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2925         }
2926         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2927         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2928         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2929 }
2930
2931 void R_Shadow_UpdateWorldLightSelection(void)
2932 {
2933         if (r_editlights.integer)
2934         {
2935                 R_Shadow_SetCursorLocationForView();
2936                 R_Shadow_SelectLightInView();
2937                 R_Shadow_DrawLightSprites();
2938         }
2939         else
2940                 R_Shadow_SelectLight(NULL);
2941 }
2942
2943 void R_Shadow_EditLights_Clear_f(void)
2944 {
2945         R_Shadow_ClearWorldLights();
2946 }
2947
2948 void R_Shadow_EditLights_Reload_f(void)
2949 {
2950         if (!cl.worldmodel)
2951                 return;
2952         strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
2953         R_Shadow_ClearWorldLights();
2954         R_Shadow_LoadWorldLights();
2955         if (r_shadow_worldlightchain == NULL)
2956         {
2957                 R_Shadow_LoadLightsFile();
2958                 if (r_shadow_worldlightchain == NULL)
2959                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2960         }
2961 }
2962
2963 void R_Shadow_EditLights_Save_f(void)
2964 {
2965         if (!cl.worldmodel)
2966                 return;
2967         R_Shadow_SaveWorldLights();
2968 }
2969
2970 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2971 {
2972         R_Shadow_ClearWorldLights();
2973         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2974 }
2975
2976 void R_Shadow_EditLights_ImportLightsFile_f(void)
2977 {
2978         R_Shadow_ClearWorldLights();
2979         R_Shadow_LoadLightsFile();
2980 }
2981
2982 void R_Shadow_EditLights_Spawn_f(void)
2983 {
2984         vec3_t color;
2985         if (!r_editlights.integer)
2986         {
2987                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2988                 return;
2989         }
2990         if (Cmd_Argc() != 1)
2991         {
2992                 Con_Print("r_editlights_spawn does not take parameters\n");
2993                 return;
2994         }
2995         color[0] = color[1] = color[2] = 1;
2996         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2997 }
2998
2999 void R_Shadow_EditLights_Edit_f(void)
3000 {
3001         vec3_t origin, angles, color;
3002         vec_t radius, corona;
3003         int style, shadows;
3004         char cubemapname[1024];
3005         if (!r_editlights.integer)
3006         {
3007                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3008                 return;
3009         }
3010         if (!r_shadow_selectedlight)
3011         {
3012                 Con_Print("No selected light.\n");
3013                 return;
3014         }
3015         VectorCopy(r_shadow_selectedlight->origin, origin);
3016         VectorCopy(r_shadow_selectedlight->angles, angles);
3017         VectorCopy(r_shadow_selectedlight->color, color);
3018         radius = r_shadow_selectedlight->radius;
3019         style = r_shadow_selectedlight->style;
3020         if (r_shadow_selectedlight->cubemapname)
3021                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
3022         else
3023                 cubemapname[0] = 0;
3024         shadows = r_shadow_selectedlight->shadow;
3025         corona = r_shadow_selectedlight->corona;
3026         if (!strcmp(Cmd_Argv(1), "origin"))
3027         {
3028                 if (Cmd_Argc() != 5)
3029                 {
3030                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3031                         return;
3032                 }
3033                 origin[0] = atof(Cmd_Argv(2));
3034                 origin[1] = atof(Cmd_Argv(3));
3035                 origin[2] = atof(Cmd_Argv(4));
3036         }
3037         else if (!strcmp(Cmd_Argv(1), "originx"))
3038         {
3039                 if (Cmd_Argc() != 3)
3040                 {
3041                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3042                         return;
3043                 }
3044                 origin[0] = atof(Cmd_Argv(2));
3045         }
3046         else if (!strcmp(Cmd_Argv(1), "originy"))
3047         {
3048                 if (Cmd_Argc() != 3)
3049                 {
3050                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3051                         return;
3052                 }
3053                 origin[1] = atof(Cmd_Argv(2));
3054         }
3055         else if (!strcmp(Cmd_Argv(1), "originz"))
3056         {
3057                 if (Cmd_Argc() != 3)
3058                 {
3059                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3060                         return;
3061                 }
3062                 origin[2] = atof(Cmd_Argv(2));
3063         }
3064         else if (!strcmp(Cmd_Argv(1), "move"))
3065         {
3066                 if (Cmd_Argc() != 5)
3067                 {
3068                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3069                         return;
3070                 }
3071                 origin[0] += atof(Cmd_Argv(2));
3072                 origin[1] += atof(Cmd_Argv(3));
3073                 origin[2] += atof(Cmd_Argv(4));
3074         }
3075         else if (!strcmp(Cmd_Argv(1), "movex"))
3076         {
3077                 if (Cmd_Argc() != 3)
3078                 {
3079                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3080                         return;
3081                 }
3082                 origin[0] += atof(Cmd_Argv(2));
3083         }
3084         else if (!strcmp(Cmd_Argv(1), "movey"))
3085         {
3086                 if (Cmd_Argc() != 3)
3087                 {
3088                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3089                         return;
3090                 }
3091                 origin[1] += atof(Cmd_Argv(2));
3092         }
3093         else if (!strcmp(Cmd_Argv(1), "movez"))
3094         {
3095                 if (Cmd_Argc() != 3)
3096                 {
3097                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3098                         return;
3099                 }
3100                 origin[2] += atof(Cmd_Argv(2));
3101         }
3102         else if (!strcmp(Cmd_Argv(1), "angles"))
3103         {
3104                 if (Cmd_Argc() != 5)
3105                 {
3106                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3107                         return;
3108                 }
3109                 angles[0] = atof(Cmd_Argv(2));
3110                 angles[1] = atof(Cmd_Argv(3));
3111                 angles[2] = atof(Cmd_Argv(4));
3112         }
3113         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3114         {
3115                 if (Cmd_Argc() != 3)
3116                 {
3117                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3118                         return;
3119                 }
3120                 angles[0] = atof(Cmd_Argv(2));
3121         }
3122         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3123         {
3124                 if (Cmd_Argc() != 3)
3125                 {
3126                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3127                         return;
3128                 }
3129                 angles[1] = atof(Cmd_Argv(2));
3130         }
3131         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3132         {
3133                 if (Cmd_Argc() != 3)
3134                 {
3135                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3136                         return;
3137                 }
3138                 angles[2] = atof(Cmd_Argv(2));
3139         }
3140         else if (!strcmp(Cmd_Argv(1), "color"))
3141         {
3142                 if (Cmd_Argc() != 5)
3143                 {
3144                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3145                         return;
3146                 }
3147                 color[0] = atof(Cmd_Argv(2));
3148                 color[1] = atof(Cmd_Argv(3));
3149                 color[2] = atof(Cmd_Argv(4));
3150         }
3151         else if (!strcmp(Cmd_Argv(1), "radius"))
3152         {
3153                 if (Cmd_Argc() != 3)
3154                 {
3155                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3156                         return;
3157                 }
3158                 radius = atof(Cmd_Argv(2));
3159         }
3160         else if (!strcmp(Cmd_Argv(1), "style"))
3161         {
3162                 if (Cmd_Argc() != 3)
3163                 {
3164                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3165                         return;
3166                 }
3167                 style = atoi(Cmd_Argv(2));
3168         }
3169         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3170         {
3171                 if (Cmd_Argc() > 3)
3172                 {
3173                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3174                         return;
3175                 }
3176                 if (Cmd_Argc() == 3)
3177                         strcpy(cubemapname, Cmd_Argv(2));
3178                 else
3179                         cubemapname[0] = 0;
3180         }
3181         else if (!strcmp(Cmd_Argv(1), "shadows"))
3182         {
3183                 if (Cmd_Argc() != 3)
3184                 {
3185                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3186                         return;
3187                 }
3188                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3189         }
3190         else if (!strcmp(Cmd_Argv(1), "corona"))
3191         {
3192                 if (Cmd_Argc() != 3)
3193                 {
3194                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3195                         return;
3196                 }
3197                 corona = atof(Cmd_Argv(2));
3198         }
3199         else
3200         {
3201                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3202                 Con_Print("Selected light's properties:\n");
3203                 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3204                 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3205                 Con_Printf("Color  : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3206                 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3207                 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3208                 Con_Printf("Style  : %i\n", r_shadow_selectedlight->style);
3209                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3210                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3211                 return;
3212         }
3213         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3214         r_shadow_selectedlight = NULL;
3215         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3216 }
3217
3218 void R_Shadow_EditLights_EditAll_f(void)
3219 {
3220         dlight_t *light;
3221
3222         for (light = r_shadow_worldlightchain;light;light = light->next)
3223         {
3224                 R_Shadow_SelectLight(light);
3225                 R_Shadow_EditLights_Edit_f();
3226         }
3227 }
3228
3229 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3230 {
3231         float x, y;
3232         char temp[256];
3233         if (r_shadow_selectedlight == NULL)
3234                 return;
3235         x = 0;
3236         y = con_vislines;
3237         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3238         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;
3239         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;
3240         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;
3241         sprintf(temp, "Radius  %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3242         sprintf(temp, "Corona  %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3243         sprintf(temp, "Style   %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3244         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;
3245         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3246 }
3247
3248 void R_Shadow_EditLights_ToggleShadow_f(void)
3249 {
3250         if (!r_editlights.integer)
3251         {
3252                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3253                 return;
3254         }
3255         if (!r_shadow_selectedlight)
3256         {
3257                 Con_Print("No selected light.\n");
3258                 return;
3259         }
3260         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);
3261         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3262         r_shadow_selectedlight = NULL;
3263 }
3264
3265 void R_Shadow_EditLights_ToggleCorona_f(void)
3266 {
3267         if (!r_editlights.integer)
3268         {
3269                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3270                 return;
3271         }
3272         if (!r_shadow_selectedlight)
3273         {
3274                 Con_Print("No selected light.\n");
3275                 return;
3276         }
3277         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);
3278         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3279         r_shadow_selectedlight = NULL;
3280 }
3281
3282 void R_Shadow_EditLights_Remove_f(void)
3283 {
3284         if (!r_editlights.integer)
3285         {
3286                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3287                 return;
3288         }
3289         if (!r_shadow_selectedlight)
3290         {
3291                 Con_Print("No selected light.\n");
3292                 return;
3293         }
3294         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3295         r_shadow_selectedlight = NULL;
3296 }
3297
3298 void R_Shadow_EditLights_Help_f(void)
3299 {
3300         Con_Print(
3301 "Documentation on r_editlights system:\n"
3302 "Settings:\n"
3303 "r_editlights : enable/disable editing mode\n"
3304 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3305 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3306 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3307 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3308 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3309 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3310 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3311 "Commands:\n"
3312 "r_editlights_help : this help\n"
3313 "r_editlights_clear : remove all lights\n"
3314 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3315 "r_editlights_save : save to .rtlights file\n"
3316 "r_editlights_spawn : create a light with default settings\n"
3317 "r_editlights_edit command : edit selected light - more documentation below\n"
3318 "r_editlights_remove : remove selected light\n"
3319 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3320 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3321 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3322 "Edit commands:\n"
3323 "origin x y z : set light location\n"
3324 "originx x: set x component of light location\n"
3325 "originy y: set y component of light location\n"
3326 "originz z: set z component of light location\n"
3327 "move x y z : adjust light location\n"
3328 "movex x: adjust x component of light location\n"
3329 "movey y: adjust y component of light location\n"
3330 "movez z: adjust z component of light location\n"
3331 "angles x y z : set light angles\n"
3332 "anglesx x: set x component of light angles\n"
3333 "anglesy y: set y component of light angles\n"
3334 "anglesz z: set z component of light angles\n"
3335 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3336 "radius radius : set radius (size) of light\n"
3337 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3338 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3339 "shadows 1/0 : turn on/off shadows\n"
3340 "corona n : set corona intensity\n"
3341 "<nothing> : print light properties to console\n"
3342         );
3343 }
3344
3345 void R_Shadow_EditLights_Init(void)
3346 {
3347         Cvar_RegisterVariable(&r_editlights);
3348         Cvar_RegisterVariable(&r_editlights_cursordistance);
3349         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3350         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3351         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3352         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3353         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3354         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3355         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3356         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3357         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3358         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3359         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3360         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3361         Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
3362         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3363         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3364         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3365         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3366         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3367 }
3368