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