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