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