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