]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
now checks that a lightmap surfacechain is not NULL before dereferencing it
[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 static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
1229 {
1230         float *color4f = varray_color4f;
1231         float dist, intensity, v[3];
1232         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1233         {
1234                 Matrix4x4_Transform(m, vertex3f, v);
1235                 if ((dist = DotProduct(v, v)) < 1)
1236                 {
1237                         dist = sqrt(dist);
1238                         intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1239                         VectorScale(lightcolor, intensity, color4f);
1240                         color4f[3] = 1;
1241                 }
1242                 else
1243                 {
1244                         VectorClear(color4f);
1245                         color4f[3] = 1;
1246                 }
1247         }
1248 }
1249
1250 static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
1251 {
1252         float *color4f = varray_color4f;
1253         float dist, intensity, v[3];
1254         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1255         {
1256                 Matrix4x4_Transform(m, vertex3f, v);
1257                 if ((dist = fabs(v[2])) < 1)
1258                 {
1259                         intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1260                         VectorScale(lightcolor, intensity, color4f);
1261                         color4f[3] = 1;
1262                 }
1263                 else
1264                 {
1265                         VectorClear(color4f);
1266                         color4f[3] = 1;
1267                 }
1268         }
1269 }
1270
1271 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1272 #define USETEXMATRIX
1273
1274 #ifndef USETEXMATRIX
1275 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1276 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1277 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1278 {
1279         do
1280         {
1281                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1282                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1283                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1284                 vertex3f += 3;
1285                 tc3f += 3;
1286         }
1287         while (--numverts);
1288 }
1289
1290 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1291 {
1292         do
1293         {
1294                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1295                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1296                 vertex3f += 3;
1297                 tc2f += 2;
1298         }
1299         while (--numverts);
1300 }
1301 #endif
1302
1303 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)
1304 {
1305         int i;
1306         float lightdir[3];
1307         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1308         {
1309                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1310                 // the cubemap normalizes this for us
1311                 out3f[0] = DotProduct(svector3f, lightdir);
1312                 out3f[1] = DotProduct(tvector3f, lightdir);
1313                 out3f[2] = DotProduct(normal3f, lightdir);
1314         }
1315 }
1316
1317 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)
1318 {
1319         int i;
1320         float lightdir[3], eyedir[3], halfdir[3];
1321         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1322         {
1323                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1324                 VectorNormalizeFast(lightdir);
1325                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1326                 VectorNormalizeFast(eyedir);
1327                 VectorAdd(lightdir, eyedir, halfdir);
1328                 // the cubemap normalizes this for us
1329                 out3f[0] = DotProduct(svector3f, halfdir);
1330                 out3f[1] = DotProduct(tvector3f, halfdir);
1331                 out3f[2] = DotProduct(normal3f, halfdir);
1332         }
1333 }
1334
1335 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, vec_t ambientscale, vec_t diffusescale, vec_t specularscale)
1336 {
1337         int renders;
1338         float color[3], color2[3], colorscale;
1339         rmeshstate_t m;
1340         if (!bumptexture)
1341                 bumptexture = r_shadow_blankbumptexture;
1342         if (!glosstexture)
1343                 glosstexture = r_shadow_blankglosstexture;
1344         // FIXME: support EF_NODEPTHTEST
1345         GL_DepthMask(false);
1346         GL_DepthTest(true);
1347         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1348         {
1349                 if (ambientscale)
1350                 {
1351                         GL_Color(1,1,1,1);
1352                         colorscale = r_shadow_lightintensityscale.value * ambientscale;
1353                         // colorscale accounts for how much we multiply the brightness
1354                         // during combine.
1355                         //
1356                         // mult is how many times the final pass of the lighting will be
1357                         // performed to get more brightness than otherwise possible.
1358                         //
1359                         // Limit mult to 64 for sanity sake.
1360                         if (r_shadow_texture3d.integer && lightcubemap && r_textureunits.integer >= 4)
1361                         {
1362                                 // 3 3D combine path (Geforce3, Radeon 8500)
1363                                 memset(&m, 0, sizeof(m));
1364                                 m.pointer_vertex = vertex3f;
1365                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1366 #ifdef USETEXMATRIX
1367                                 m.pointer_texcoord3f[0] = vertex3f;
1368                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1369 #else
1370                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1371                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1372 #endif
1373                                 m.tex[1] = R_GetTexture(basetexture);
1374                                 m.pointer_texcoord[1] = texcoord2f;
1375                                 m.texcubemap[2] = R_GetTexture(lightcubemap);
1376 #ifdef USETEXMATRIX
1377                                 m.pointer_texcoord3f[2] = vertex3f;
1378                                 m.texmatrix[2] = *matrix_modeltolight;
1379 #else
1380                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1381                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltolight);
1382 #endif
1383                         }
1384                         else if (r_shadow_texture3d.integer && !lightcubemap && r_textureunits.integer >= 2)
1385                         {
1386                                 // 2 3D combine path (Geforce3, original Radeon)
1387                                 memset(&m, 0, sizeof(m));
1388                                 m.pointer_vertex = vertex3f;
1389                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1390 #ifdef USETEXMATRIX
1391                                 m.pointer_texcoord3f[0] = vertex3f;
1392                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1393 #else
1394                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1395                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1396 #endif
1397                                 m.tex[1] = R_GetTexture(basetexture);
1398                                 m.pointer_texcoord[1] = texcoord2f;
1399                         }
1400                         else if (r_textureunits.integer >= 4 && lightcubemap)
1401                         {
1402                                 // 4 2D combine path (Geforce3, Radeon 8500)
1403                                 memset(&m, 0, sizeof(m));
1404                                 m.pointer_vertex = vertex3f;
1405                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1406 #ifdef USETEXMATRIX
1407                                 m.pointer_texcoord3f[0] = vertex3f;
1408                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1409 #else
1410                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1411                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1412 #endif
1413                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1414 #ifdef USETEXMATRIX
1415                                 m.pointer_texcoord3f[1] = vertex3f;
1416                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1417 #else
1418                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1419                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1420 #endif
1421                                 m.tex[2] = R_GetTexture(basetexture);
1422                                 m.pointer_texcoord[2] = texcoord2f;
1423                                 if (lightcubemap)
1424                                 {
1425                                         m.texcubemap[3] = R_GetTexture(lightcubemap);
1426 #ifdef USETEXMATRIX
1427                                         m.pointer_texcoord3f[3] = vertex3f;
1428                                         m.texmatrix[3] = *matrix_modeltolight;
1429 #else
1430                                         m.pointer_texcoord3f[3] = varray_texcoord3f[3];
1431                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3], numverts, vertex3f, matrix_modeltolight);
1432 #endif
1433                                 }
1434                         }
1435                         else if (r_textureunits.integer >= 3 && !lightcubemap)
1436                         {
1437                                 // 3 2D combine path (Geforce3, original Radeon)
1438                                 memset(&m, 0, sizeof(m));
1439                                 m.pointer_vertex = vertex3f;
1440                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1441 #ifdef USETEXMATRIX
1442                                 m.pointer_texcoord3f[0] = vertex3f;
1443                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1444 #else
1445                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1446                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1447 #endif
1448                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1449 #ifdef USETEXMATRIX
1450                                 m.pointer_texcoord3f[1] = vertex3f;
1451                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1452 #else
1453                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1454                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1455 #endif
1456                                 m.tex[2] = R_GetTexture(basetexture);
1457                                 m.pointer_texcoord[2] = texcoord2f;
1458                         }
1459                         else
1460                         {
1461                                 // 2/2/2 2D combine path (any dot3 card)
1462                                 memset(&m, 0, sizeof(m));
1463                                 m.pointer_vertex = vertex3f;
1464                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1465 #ifdef USETEXMATRIX
1466                                 m.pointer_texcoord3f[0] = vertex3f;
1467                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1468 #else
1469                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1470                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1471 #endif
1472                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1473 #ifdef USETEXMATRIX
1474                                 m.pointer_texcoord3f[1] = vertex3f;
1475                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1476 #else
1477                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1478                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1479 #endif
1480                                 R_Mesh_State(&m);
1481                                 GL_ColorMask(0,0,0,1);
1482                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1483                                 GL_LockArrays(0, numverts);
1484                                 R_Mesh_Draw(numverts, numtriangles, elements);
1485                                 GL_LockArrays(0, 0);
1486                                 c_rt_lightmeshes++;
1487                                 c_rt_lighttris += numtriangles;
1488         
1489                                 memset(&m, 0, sizeof(m));
1490                                 m.pointer_vertex = vertex3f;
1491                                 m.tex[0] = R_GetTexture(basetexture);
1492                                 m.pointer_texcoord[0] = texcoord2f;
1493                                 if (lightcubemap)
1494                                 {
1495                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1496 #ifdef USETEXMATRIX
1497                                         m.pointer_texcoord3f[1] = vertex3f;
1498                                         m.texmatrix[1] = *matrix_modeltolight;
1499 #else
1500                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1501                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1502 #endif
1503                                 }
1504                         }
1505                         // this final code is shared
1506                         R_Mesh_State(&m);
1507                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1508                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1509                         VectorScale(lightcolor, colorscale, color2);
1510                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1511                         {
1512                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1513                                 GL_LockArrays(0, numverts);
1514                                 R_Mesh_Draw(numverts, numtriangles, elements);
1515                                 GL_LockArrays(0, 0);
1516                                 c_rt_lightmeshes++;
1517                                 c_rt_lighttris += numtriangles;
1518                         }
1519                 }
1520                 if (diffusescale)
1521                 {
1522                         GL_Color(1,1,1,1);
1523                         colorscale = r_shadow_lightintensityscale.value * diffusescale;
1524                         // colorscale accounts for how much we multiply the brightness
1525                         // during combine.
1526                         //
1527                         // mult is how many times the final pass of the lighting will be
1528                         // performed to get more brightness than otherwise possible.
1529                         //
1530                         // Limit mult to 64 for sanity sake.
1531                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1532                         {
1533                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
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                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1544 #ifdef USETEXMATRIX
1545                                 m.pointer_texcoord3f[2] = vertex3f;
1546                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1547 #else
1548                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1549                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1550 #endif
1551                                 R_Mesh_State(&m);
1552                                 GL_ColorMask(0,0,0,1);
1553                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1554                                 GL_LockArrays(0, numverts);
1555                                 R_Mesh_Draw(numverts, numtriangles, elements);
1556                                 GL_LockArrays(0, 0);
1557                                 c_rt_lightmeshes++;
1558                                 c_rt_lighttris += numtriangles;
1559         
1560                                 memset(&m, 0, sizeof(m));
1561                                 m.pointer_vertex = vertex3f;
1562                                 m.tex[0] = R_GetTexture(basetexture);
1563                                 m.pointer_texcoord[0] = texcoord2f;
1564                                 if (lightcubemap)
1565                                 {
1566                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1567 #ifdef USETEXMATRIX
1568                                         m.pointer_texcoord3f[1] = vertex3f;
1569                                         m.texmatrix[1] = *matrix_modeltolight;
1570 #else
1571                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1572                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1573 #endif
1574                                 }
1575                         }
1576                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1577                         {
1578                                 // 1/2/2 3D combine path (original Radeon)
1579                                 memset(&m, 0, sizeof(m));
1580                                 m.pointer_vertex = vertex3f;
1581                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1582 #ifdef USETEXMATRIX
1583                                 m.pointer_texcoord3f[0] = vertex3f;
1584                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1585 #else
1586                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1587                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1588 #endif
1589                                 R_Mesh_State(&m);
1590                                 GL_ColorMask(0,0,0,1);
1591                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1592                                 GL_LockArrays(0, numverts);
1593                                 R_Mesh_Draw(numverts, numtriangles, elements);
1594                                 GL_LockArrays(0, 0);
1595                                 c_rt_lightmeshes++;
1596                                 c_rt_lighttris += numtriangles;
1597         
1598                                 memset(&m, 0, sizeof(m));
1599                                 m.pointer_vertex = vertex3f;
1600                                 m.tex[0] = R_GetTexture(bumptexture);
1601                                 m.texcombinergb[0] = GL_REPLACE;
1602                                 m.pointer_texcoord[0] = texcoord2f;
1603                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1604                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1605                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1606                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1607                                 R_Mesh_State(&m);
1608                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1609                                 GL_LockArrays(0, numverts);
1610                                 R_Mesh_Draw(numverts, numtriangles, elements);
1611                                 GL_LockArrays(0, 0);
1612                                 c_rt_lightmeshes++;
1613                                 c_rt_lighttris += numtriangles;
1614         
1615                                 memset(&m, 0, sizeof(m));
1616                                 m.pointer_vertex = vertex3f;
1617                                 m.tex[0] = R_GetTexture(basetexture);
1618                                 m.pointer_texcoord[0] = texcoord2f;
1619                                 if (lightcubemap)
1620                                 {
1621                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1622 #ifdef USETEXMATRIX
1623                                         m.pointer_texcoord3f[1] = vertex3f;
1624                                         m.texmatrix[1] = *matrix_modeltolight;
1625 #else
1626                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1627                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1628 #endif
1629                                 }
1630                         }
1631                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1632                         {
1633                                 // 2/2 3D combine path (original Radeon)
1634                                 memset(&m, 0, sizeof(m));
1635                                 m.pointer_vertex = vertex3f;
1636                                 m.tex[0] = R_GetTexture(bumptexture);
1637                                 m.texcombinergb[0] = GL_REPLACE;
1638                                 m.pointer_texcoord[0] = texcoord2f;
1639                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1640                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1641                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1642                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1643                                 R_Mesh_State(&m);
1644                                 GL_ColorMask(0,0,0,1);
1645                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1646                                 GL_LockArrays(0, numverts);
1647                                 R_Mesh_Draw(numverts, numtriangles, elements);
1648                                 GL_LockArrays(0, 0);
1649                                 c_rt_lightmeshes++;
1650                                 c_rt_lighttris += numtriangles;
1651         
1652                                 memset(&m, 0, sizeof(m));
1653                                 m.pointer_vertex = vertex3f;
1654                                 m.tex[0] = R_GetTexture(basetexture);
1655                                 m.pointer_texcoord[0] = texcoord2f;
1656                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1657 #ifdef USETEXMATRIX
1658                                 m.pointer_texcoord3f[1] = vertex3f;
1659                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1660 #else
1661                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1662                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1663 #endif
1664                         }
1665                         else if (r_textureunits.integer >= 4)
1666                         {
1667                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1668                                 memset(&m, 0, sizeof(m));
1669                                 m.pointer_vertex = vertex3f;
1670                                 m.tex[0] = R_GetTexture(bumptexture);
1671                                 m.texcombinergb[0] = GL_REPLACE;
1672                                 m.pointer_texcoord[0] = texcoord2f;
1673                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1674                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1675                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1676                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1677                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1678 #ifdef USETEXMATRIX
1679                                 m.pointer_texcoord3f[2] = vertex3f;
1680                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1681 #else
1682                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
1683                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1684 #endif
1685                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1686 #ifdef USETEXMATRIX
1687                                 m.pointer_texcoord3f[3] = vertex3f;
1688                                 m.texmatrix[3] = *matrix_modeltoattenuationz;
1689 #else
1690                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
1691                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1692 #endif
1693                                 R_Mesh_State(&m);
1694                                 GL_ColorMask(0,0,0,1);
1695                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1696                                 GL_LockArrays(0, numverts);
1697                                 R_Mesh_Draw(numverts, numtriangles, elements);
1698                                 GL_LockArrays(0, 0);
1699                                 c_rt_lightmeshes++;
1700                                 c_rt_lighttris += numtriangles;
1701         
1702                                 memset(&m, 0, sizeof(m));
1703                                 m.pointer_vertex = vertex3f;
1704                                 m.tex[0] = R_GetTexture(basetexture);
1705                                 m.pointer_texcoord[0] = texcoord2f;
1706                                 if (lightcubemap)
1707                                 {
1708                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1709 #ifdef USETEXMATRIX
1710                                         m.pointer_texcoord3f[1] = vertex3f;
1711                                         m.texmatrix[1] = *matrix_modeltolight;
1712 #else
1713                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1714                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1715 #endif
1716                                 }
1717                         }
1718                         else
1719                         {
1720                                 // 2/2/2 2D combine path (any dot3 card)
1721                                 memset(&m, 0, sizeof(m));
1722                                 m.pointer_vertex = vertex3f;
1723                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1724 #ifdef USETEXMATRIX
1725                                 m.pointer_texcoord3f[0] = vertex3f;
1726                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1727 #else
1728                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1729                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1730 #endif
1731                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1732 #ifdef USETEXMATRIX
1733                                 m.pointer_texcoord3f[1] = vertex3f;
1734                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1735 #else
1736                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1737                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1738 #endif
1739                                 R_Mesh_State(&m);
1740                                 GL_ColorMask(0,0,0,1);
1741                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1742                                 GL_LockArrays(0, numverts);
1743                                 R_Mesh_Draw(numverts, numtriangles, elements);
1744                                 GL_LockArrays(0, 0);
1745                                 c_rt_lightmeshes++;
1746                                 c_rt_lighttris += numtriangles;
1747         
1748                                 memset(&m, 0, sizeof(m));
1749                                 m.pointer_vertex = vertex3f;
1750                                 m.tex[0] = R_GetTexture(bumptexture);
1751                                 m.texcombinergb[0] = GL_REPLACE;
1752                                 m.pointer_texcoord[0] = texcoord2f;
1753                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1754                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1755                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1756                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1757                                 R_Mesh_State(&m);
1758                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1759                                 GL_LockArrays(0, numverts);
1760                                 R_Mesh_Draw(numverts, numtriangles, elements);
1761                                 GL_LockArrays(0, 0);
1762                                 c_rt_lightmeshes++;
1763                                 c_rt_lighttris += numtriangles;
1764         
1765                                 memset(&m, 0, sizeof(m));
1766                                 m.pointer_vertex = vertex3f;
1767                                 m.tex[0] = R_GetTexture(basetexture);
1768                                 m.pointer_texcoord[0] = texcoord2f;
1769                                 if (lightcubemap)
1770                                 {
1771                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1772 #ifdef USETEXMATRIX
1773                                         m.pointer_texcoord3f[1] = vertex3f;
1774                                         m.texmatrix[1] = *matrix_modeltolight;
1775 #else
1776                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1777                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1778 #endif
1779                                 }
1780                         }
1781                         // this final code is shared
1782                         R_Mesh_State(&m);
1783                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1784                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1785                         VectorScale(lightcolor, colorscale, color2);
1786                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1787                         {
1788                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1789                                 GL_LockArrays(0, numverts);
1790                                 R_Mesh_Draw(numverts, numtriangles, elements);
1791                                 GL_LockArrays(0, 0);
1792                                 c_rt_lightmeshes++;
1793                                 c_rt_lighttris += numtriangles;
1794                         }
1795                 }
1796                 if (specularscale && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1797                 {
1798                         // FIXME: detect blendsquare!
1799                         //if (gl_support_blendsquare)
1800                         {
1801                                 colorscale = r_shadow_lightintensityscale.value * r_shadow_glossintensity.value * specularscale;
1802                                 if (glosstexture == r_shadow_blankglosstexture)
1803                                         colorscale *= r_shadow_gloss2intensity.value;
1804                                 GL_Color(1,1,1,1);
1805                                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1806                                 {
1807                                         // 2/0/0/1/2 3D combine blendsquare path
1808                                         memset(&m, 0, sizeof(m));
1809                                         m.pointer_vertex = vertex3f;
1810                                         m.tex[0] = R_GetTexture(bumptexture);
1811                                         m.pointer_texcoord[0] = texcoord2f;
1812                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1813                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1814                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1815                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1816                                         R_Mesh_State(&m);
1817                                         GL_ColorMask(0,0,0,1);
1818                                         // this squares the result
1819                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1820                                         GL_LockArrays(0, numverts);
1821                                         R_Mesh_Draw(numverts, numtriangles, elements);
1822                                         GL_LockArrays(0, 0);
1823                                         c_rt_lightmeshes++;
1824                                         c_rt_lighttris += numtriangles;
1825                 
1826                                         memset(&m, 0, sizeof(m));
1827                                         m.pointer_vertex = vertex3f;
1828                                         R_Mesh_State(&m);
1829                                         GL_LockArrays(0, numverts);
1830                                         // square alpha in framebuffer a few times to make it shiny
1831                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1832                                         // these comments are a test run through this math for intensity 0.5
1833                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1834                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1835                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1836                                         R_Mesh_Draw(numverts, numtriangles, elements);
1837                                         c_rt_lightmeshes++;
1838                                         c_rt_lighttris += numtriangles;
1839                                         R_Mesh_Draw(numverts, numtriangles, elements);
1840                                         c_rt_lightmeshes++;
1841                                         c_rt_lighttris += numtriangles;
1842                                         GL_LockArrays(0, 0);
1843                 
1844                                         memset(&m, 0, sizeof(m));
1845                                         m.pointer_vertex = vertex3f;
1846                                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1847 #ifdef USETEXMATRIX
1848                                         m.pointer_texcoord3f[0] = vertex3f;
1849                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1850 #else
1851                                         m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1852                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1853 #endif
1854                                         R_Mesh_State(&m);
1855                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1856                                         GL_LockArrays(0, numverts);
1857                                         R_Mesh_Draw(numverts, numtriangles, elements);
1858                                         GL_LockArrays(0, 0);
1859                                         c_rt_lightmeshes++;
1860                                         c_rt_lighttris += numtriangles;
1861                 
1862                                         memset(&m, 0, sizeof(m));
1863                                         m.pointer_vertex = vertex3f;
1864                                         m.tex[0] = R_GetTexture(glosstexture);
1865                                         m.pointer_texcoord[0] = texcoord2f;
1866                                         if (lightcubemap)
1867                                         {
1868                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
1869 #ifdef USETEXMATRIX
1870                                                 m.pointer_texcoord3f[1] = vertex3f;
1871                                                 m.texmatrix[1] = *matrix_modeltolight;
1872 #else
1873                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1874                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1875 #endif
1876                                         }
1877                                 }
1878                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1879                                 {
1880                                         // 2/0/0/2 3D combine blendsquare path
1881                                         memset(&m, 0, sizeof(m));
1882                                         m.pointer_vertex = vertex3f;
1883                                         m.tex[0] = R_GetTexture(bumptexture);
1884                                         m.pointer_texcoord[0] = texcoord2f;
1885                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1886                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1887                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1888                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1889                                         R_Mesh_State(&m);
1890                                         GL_ColorMask(0,0,0,1);
1891                                         // this squares the result
1892                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1893                                         GL_LockArrays(0, numverts);
1894                                         R_Mesh_Draw(numverts, numtriangles, elements);
1895                                         GL_LockArrays(0, 0);
1896                                         c_rt_lightmeshes++;
1897                                         c_rt_lighttris += numtriangles;
1898                 
1899                                         memset(&m, 0, sizeof(m));
1900                                         m.pointer_vertex = vertex3f;
1901                                         R_Mesh_State(&m);
1902                                         GL_LockArrays(0, numverts);
1903                                         // square alpha in framebuffer a few times to make it shiny
1904                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1905                                         // these comments are a test run through this math for intensity 0.5
1906                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1907                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1908                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1909                                         R_Mesh_Draw(numverts, numtriangles, elements);
1910                                         c_rt_lightmeshes++;
1911                                         c_rt_lighttris += numtriangles;
1912                                         R_Mesh_Draw(numverts, numtriangles, elements);
1913                                         c_rt_lightmeshes++;
1914                                         c_rt_lighttris += numtriangles;
1915                                         GL_LockArrays(0, 0);
1916                 
1917                                         memset(&m, 0, sizeof(m));
1918                                         m.pointer_vertex = vertex3f;
1919                                         m.tex[0] = R_GetTexture(glosstexture);
1920                                         m.pointer_texcoord[0] = texcoord2f;
1921                                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1922 #ifdef USETEXMATRIX
1923                                         m.pointer_texcoord3f[1] = vertex3f;
1924                                         m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1925 #else
1926                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1927                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1928 #endif
1929                                 }
1930                                 else
1931                                 {
1932                                         // 2/0/0/2/2 2D combine blendsquare path
1933                                         memset(&m, 0, sizeof(m));
1934                                         m.pointer_vertex = vertex3f;
1935                                         m.tex[0] = R_GetTexture(bumptexture);
1936                                         m.pointer_texcoord[0] = texcoord2f;
1937                                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1938                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1939                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1940                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1941                                         R_Mesh_State(&m);
1942                                         GL_ColorMask(0,0,0,1);
1943                                         // this squares the result
1944                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1945                                         GL_LockArrays(0, numverts);
1946                                         R_Mesh_Draw(numverts, numtriangles, elements);
1947                                         GL_LockArrays(0, 0);
1948                                         c_rt_lightmeshes++;
1949                                         c_rt_lighttris += numtriangles;
1950                 
1951                                         memset(&m, 0, sizeof(m));
1952                                         m.pointer_vertex = vertex3f;
1953                                         R_Mesh_State(&m);
1954                                         GL_LockArrays(0, numverts);
1955                                         // square alpha in framebuffer a few times to make it shiny
1956                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1957                                         // these comments are a test run through this math for intensity 0.5
1958                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1959                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1960                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1961                                         R_Mesh_Draw(numverts, numtriangles, elements);
1962                                         c_rt_lightmeshes++;
1963                                         c_rt_lighttris += numtriangles;
1964                                         R_Mesh_Draw(numverts, numtriangles, elements);
1965                                         c_rt_lightmeshes++;
1966                                         c_rt_lighttris += numtriangles;
1967                                         GL_LockArrays(0, 0);
1968                 
1969                                         memset(&m, 0, sizeof(m));
1970                                         m.pointer_vertex = vertex3f;
1971                                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1972 #ifdef USETEXMATRIX
1973                                         m.pointer_texcoord3f[0] = vertex3f;
1974                                         m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1975 #else
1976                                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1977                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1978 #endif
1979                                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1980 #ifdef USETEXMATRIX
1981                                         m.pointer_texcoord3f[1] = vertex3f;
1982                                         m.texmatrix[1] = *matrix_modeltoattenuationz;
1983 #else
1984                                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1985                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1986 #endif
1987                                         R_Mesh_State(&m);
1988                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1989                                         GL_LockArrays(0, numverts);
1990                                         R_Mesh_Draw(numverts, numtriangles, elements);
1991                                         GL_LockArrays(0, 0);
1992                                         c_rt_lightmeshes++;
1993                                         c_rt_lighttris += numtriangles;
1994                 
1995                                         memset(&m, 0, sizeof(m));
1996                                         m.pointer_vertex = vertex3f;
1997                                         m.tex[0] = R_GetTexture(glosstexture);
1998                                         m.pointer_texcoord[0] = texcoord2f;
1999                                         if (lightcubemap)
2000                                         {
2001                                                 m.texcubemap[1] = R_GetTexture(lightcubemap);
2002 #ifdef USETEXMATRIX
2003                                                 m.pointer_texcoord3f[1] = vertex3f;
2004                                                 m.texmatrix[1] = *matrix_modeltolight;
2005 #else
2006                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2007                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
2008 #endif
2009                                         }
2010                                 }
2011                         }
2012                         R_Mesh_State(&m);
2013                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
2014                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2015                         VectorScale(lightcolor, colorscale, color2);
2016                         GL_LockArrays(0, numverts);
2017                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2018                         {
2019                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
2020                                 R_Mesh_Draw(numverts, numtriangles, elements);
2021                                 c_rt_lightmeshes++;
2022                                 c_rt_lighttris += numtriangles;
2023                         }
2024                         GL_LockArrays(0, 0);
2025                 }
2026         }
2027         else
2028         {
2029                 if (ambientscale)
2030                 {
2031                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2032                         VectorScale(lightcolor, r_shadow_lightintensityscale.value * ambientscale, color2);
2033                         memset(&m, 0, sizeof(m));
2034                         m.pointer_vertex = vertex3f;
2035                         m.tex[0] = R_GetTexture(basetexture);
2036                         m.pointer_texcoord[0] = texcoord2f;
2037                         if (r_textureunits.integer >= 2)
2038                         {
2039                                 // voodoo2
2040                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2041 #ifdef USETEXMATRIX
2042                                 m.pointer_texcoord3f[1] = vertex3f;
2043                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
2044 #else
2045                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
2046                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
2047 #endif
2048                                 if (r_textureunits.integer >= 3)
2049                                 {
2050                                         // Geforce3/Radeon class but not using dot3
2051                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2052 #ifdef USETEXMATRIX
2053                                         m.pointer_texcoord3f[2] = vertex3f;
2054                                         m.texmatrix[2] = *matrix_modeltoattenuationz;
2055 #else
2056                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
2057                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
2058 #endif
2059                                 }
2060                         }
2061                         R_Mesh_State(&m);
2062                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2063                         {
2064                                 color[0] = bound(0, color2[0], 1);
2065                                 color[1] = bound(0, color2[1], 1);
2066                                 color[2] = bound(0, color2[2], 1);
2067                                 if (r_textureunits.integer >= 3)
2068                                 {
2069                                         GL_Color(color[0], color[1], color[2], 1);
2070                                         m.pointer_color = NULL;
2071                                 }
2072                                 else if (r_textureunits.integer >= 2)
2073                                 {
2074                                         R_Shadow_VertexNoShadingWithZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
2075                                         m.pointer_color = varray_color4f;
2076                                 }
2077                                 else
2078                                 {
2079                                         R_Shadow_VertexNoShadingWithXYZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
2080                                         m.pointer_color = varray_color4f;
2081                                 }
2082                                 GL_LockArrays(0, numverts);
2083                                 R_Mesh_Draw(numverts, numtriangles, elements);
2084                                 GL_LockArrays(0, 0);
2085                                 c_rt_lightmeshes++;
2086                                 c_rt_lighttris += numtriangles;
2087                         }
2088                 }
2089                 if (diffusescale)
2090                 {
2091                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2092                         VectorScale(lightcolor, r_shadow_lightintensityscale.value * diffusescale, color2);
2093                         memset(&m, 0, sizeof(m));
2094                         m.pointer_vertex = vertex3f;
2095                         m.pointer_color = varray_color4f;
2096                         m.tex[0] = R_GetTexture(basetexture);
2097                         m.pointer_texcoord[0] = texcoord2f;
2098                         if (r_textureunits.integer >= 2)
2099                         {
2100                                 // voodoo2
2101                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2102 #ifdef USETEXMATRIX
2103                                 m.pointer_texcoord3f[1] = vertex3f;
2104                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
2105 #else
2106                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
2107                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
2108 #endif
2109                                 if (r_textureunits.integer >= 3)
2110                                 {
2111                                         // Geforce3/Radeon class but not using dot3
2112                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2113 #ifdef USETEXMATRIX
2114                                         m.pointer_texcoord3f[2] = vertex3f;
2115                                         m.texmatrix[2] = *matrix_modeltoattenuationz;
2116 #else
2117                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
2118                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
2119 #endif
2120                                 }
2121                         }
2122                         R_Mesh_State(&m);
2123                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2124                         {
2125                                 color[0] = bound(0, color2[0], 1);
2126                                 color[1] = bound(0, color2[1], 1);
2127                                 color[2] = bound(0, color2[2], 1);
2128                                 if (r_textureunits.integer >= 3)
2129                                         R_Shadow_VertexShading(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2130                                 else if (r_textureunits.integer >= 2)
2131                                         R_Shadow_VertexShadingWithZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2132                                 else
2133                                         R_Shadow_VertexShadingWithXYZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2134                                 GL_LockArrays(0, numverts);
2135                                 R_Mesh_Draw(numverts, numtriangles, elements);
2136                                 GL_LockArrays(0, 0);
2137                                 c_rt_lightmeshes++;
2138                                 c_rt_lighttris += numtriangles;
2139                         }
2140                 }
2141         }
2142 }
2143
2144 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
2145 {
2146         int j, k;
2147         float scale;
2148         R_RTLight_Uncompile(rtlight);
2149         memset(rtlight, 0, sizeof(*rtlight));
2150
2151         VectorCopy(light->origin, rtlight->shadoworigin);
2152         VectorCopy(light->color, rtlight->color);
2153         rtlight->radius = light->radius;
2154         //rtlight->cullradius = rtlight->radius;
2155         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2156         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2157         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2158         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2159         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2160         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2161         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2162         rtlight->cubemapname[0] = 0;
2163         if (light->cubemapname[0])
2164                 strcpy(rtlight->cubemapname, light->cubemapname);
2165         else if (light->cubemapnum > 0)
2166                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2167         rtlight->shadow = light->shadow;
2168         rtlight->corona = light->corona;
2169         rtlight->style = light->style;
2170         rtlight->isstatic = isstatic;
2171         rtlight->coronasizescale = light->coronasizescale;
2172         rtlight->ambientscale = light->ambientscale;
2173         rtlight->diffusescale = light->diffusescale;
2174         rtlight->specularscale = light->specularscale;
2175         rtlight->flags = light->flags;
2176         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2177         // ConcatScale won't work here because this needs to scale rotate and
2178         // translate, not just rotate
2179         scale = 1.0f / rtlight->radius;
2180         for (k = 0;k < 3;k++)
2181                 for (j = 0;j < 4;j++)
2182                         rtlight->matrix_worldtolight.m[k][j] *= scale;
2183         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
2184         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
2185
2186         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
2187         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
2188         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.125f, rtlight->lightmap_light);
2189         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
2190 }
2191
2192 // compiles rtlight geometry
2193 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2194 void R_RTLight_Compile(rtlight_t *rtlight)
2195 {
2196         int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
2197         entity_render_t *ent = &cl_entities[0].render;
2198         model_t *model = ent->model;
2199
2200         // compile the light
2201         rtlight->compiled = true;
2202         rtlight->static_numclusters = 0;
2203         rtlight->static_numclusterpvsbytes = 0;
2204         rtlight->static_clusterlist = NULL;
2205         rtlight->static_clusterpvs = NULL;
2206         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2207         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2208         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2209         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2210         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2211         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2212
2213         if (model && model->GetLightInfo)
2214         {
2215                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
2216                 r_shadow_compilingrtlight = rtlight;
2217                 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
2218                 R_Shadow_EnlargeSurfaceBuffer(model->nummodelsurfaces); 
2219                 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);
2220                 if (numclusters)
2221                 {
2222                         rtlight->static_numclusters = numclusters;
2223                         rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
2224                         rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
2225                         rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
2226                         memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
2227                         memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
2228                 }
2229                 if (model->DrawShadowVolume && rtlight->shadow)
2230                 {
2231                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2232                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2233                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
2234                 }
2235                 if (model->DrawLight)
2236                 {
2237                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2238                         model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, 0, 0, 0, numsurfaces, r_shadow_buffer_surfacelist);
2239                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
2240                 }
2241                 // switch back to rendering when DrawShadowVolume or DrawLight is called
2242                 r_shadow_compilingrtlight = NULL;
2243         }
2244
2245
2246         // use smallest available cullradius - box radius or light radius
2247         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2248         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2249
2250         shadowmeshes = 0;
2251         shadowtris = 0;
2252         if (rtlight->static_meshchain_shadow)
2253         {
2254                 shadowmesh_t *mesh;
2255                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2256                 {
2257                         shadowmeshes++;
2258                         shadowtris += mesh->numtriangles;
2259                 }
2260         }
2261
2262         lightmeshes = 0;
2263         lighttris = 0;
2264         if (rtlight->static_meshchain_light)
2265         {
2266                 shadowmesh_t *mesh;
2267                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2268                 {
2269                         lightmeshes++;
2270                         lighttris += mesh->numtriangles;
2271                 }
2272         }
2273
2274         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);
2275 }
2276
2277 void R_RTLight_Uncompile(rtlight_t *rtlight)
2278 {
2279         if (rtlight->compiled)
2280         {
2281                 if (rtlight->static_meshchain_shadow)
2282                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2283                 rtlight->static_meshchain_shadow = NULL;
2284                 if (rtlight->static_meshchain_light)
2285                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2286                 rtlight->static_meshchain_light = NULL;
2287                 if (rtlight->static_clusterlist)
2288                         Mem_Free(rtlight->static_clusterlist);
2289                 rtlight->static_clusterlist = NULL;
2290                 if (rtlight->static_clusterpvs)
2291                         Mem_Free(rtlight->static_clusterpvs);
2292                 rtlight->static_clusterpvs = NULL;
2293                 rtlight->static_numclusters = 0;
2294                 rtlight->static_numclusterpvsbytes = 0;
2295                 rtlight->compiled = false;
2296         }
2297 }
2298
2299 void R_Shadow_UncompileWorldLights(void)
2300 {
2301         dlight_t *light;
2302         for (light = r_shadow_worldlightchain;light;light = light->next)
2303                 R_RTLight_Uncompile(&light->rtlight);
2304 }
2305
2306 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2307 {
2308         int i, shadow;
2309         entity_render_t *ent;
2310         float f;
2311         vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2;
2312         rtexture_t *cubemaptexture;
2313         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2314         int numclusters, numsurfaces;
2315         int *clusterlist, *surfacelist;
2316         qbyte *clusterpvs;
2317         vec3_t cullmins, cullmaxs;
2318         shadowmesh_t *mesh;
2319         rmeshstate_t m;
2320
2321         // loading is done before visibility checks because loading should happen
2322         // all at once at the start of a level, not when it stalls gameplay.
2323         // (especially important to benchmarks)
2324         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2325                 R_RTLight_Compile(rtlight);
2326         if (rtlight->cubemapname[0])
2327                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2328         else
2329                 cubemaptexture = NULL;
2330
2331         cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2332         cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2333         cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2334         cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2335         cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2336         cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2337         if (d_lightstylevalue[rtlight->style] <= 0)
2338                 return;
2339         numclusters = 0;
2340         clusterlist = NULL;
2341         clusterpvs = NULL;
2342         numsurfaces = 0;
2343         surfacelist = NULL;
2344         if (rtlight->compiled && r_shadow_staticworldlights.integer)
2345         {
2346                 // compiled light, world available and can receive realtime lighting
2347                 // retrieve cluster information
2348                 numclusters = rtlight->static_numclusters;
2349                 clusterlist = rtlight->static_clusterlist;
2350                 clusterpvs = rtlight->static_clusterpvs;
2351                 VectorCopy(rtlight->cullmins, cullmins);
2352                 VectorCopy(rtlight->cullmaxs, cullmaxs);
2353         }
2354         else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
2355         {
2356                 // dynamic light, world available and can receive realtime lighting
2357                 // if the light box is offscreen, skip it right away
2358                 if (R_CullBox(cullmins, cullmaxs))
2359                         return;
2360                 // calculate lit surfaces and clusters
2361                 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
2362                 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->nummodelsurfaces); 
2363                 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);
2364                 clusterlist = r_shadow_buffer_clusterlist;
2365                 clusterpvs = r_shadow_buffer_clusterpvs;
2366                 surfacelist = r_shadow_buffer_surfacelist;
2367         }
2368         // if the reduced cluster bounds are offscreen, skip it
2369         if (R_CullBox(cullmins, cullmaxs))
2370                 return;
2371         // check if light is illuminating any visible clusters
2372         if (numclusters)
2373         {
2374                 for (i = 0;i < numclusters;i++)
2375                         if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
2376                                 break;
2377                 if (i == numclusters)
2378                         return;
2379         }
2380         // set up a scissor rectangle for this light
2381         if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
2382                 return;
2383
2384         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2385         VectorScale(rtlight->color, f, lightcolor);
2386         /*
2387         if (rtlight->selected)
2388         {
2389                 f = 2 + sin(realtime * M_PI * 4.0);
2390                 VectorScale(lightcolor, f, lightcolor);
2391         }
2392         */
2393
2394         shadow = rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows);
2395
2396         if (shadow && (gl_stencil || visiblevolumes))
2397         {
2398                 if (!visiblevolumes)
2399                         R_Shadow_Stage_ShadowVolumes();
2400                 ent = &cl_entities[0].render;
2401                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2402                 {
2403                         memset(&m, 0, sizeof(m));
2404                         R_Mesh_Matrix(&ent->matrix);
2405                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2406                         {
2407                                 m.pointer_vertex = mesh->vertex3f;
2408                                 R_Mesh_State(&m);
2409                                 GL_LockArrays(0, mesh->numverts);
2410                                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2411                                 {
2412                                         // decrement stencil if frontface is behind depthbuffer
2413                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2414                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2415                                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2416                                         c_rtcached_shadowmeshes++;
2417                                         c_rtcached_shadowtris += mesh->numtriangles;
2418                                         // increment stencil if backface is behind depthbuffer
2419                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2420                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2421                                 }
2422                                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2423                                 c_rtcached_shadowmeshes++;
2424                                 c_rtcached_shadowtris += mesh->numtriangles;
2425                                 GL_LockArrays(0, 0);
2426                         }
2427                 }
2428                 else if (numsurfaces)
2429                 {
2430                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2431                         ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2432                 }
2433                 if (r_drawentities.integer)
2434                 {
2435                         for (i = 0;i < r_refdef.numentities;i++)
2436                         {
2437                                 ent = r_refdef.entities[i];
2438                                 // rough checks
2439                                 if (r_shadow_cull.integer)
2440                                 {
2441                                         if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2442                                                 continue;
2443                                         if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2444                                                 continue;
2445                                 }
2446                                 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2447                                         continue;
2448                                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2449                                 // light emitting entities should not cast their own shadow
2450                                 if (VectorLength2(relativelightorigin) < 0.1)
2451                                         continue;
2452                                 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist);
2453                         }
2454                 }
2455         }
2456
2457         if (!visiblevolumes)
2458         {
2459                 R_Shadow_Stage_Light(shadow && gl_stencil);
2460
2461                 ent = &cl_entities[0].render;
2462                 if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2463                 {
2464                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2465                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2466                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2467                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2468                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2469                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2470                         {
2471                                 R_Mesh_Matrix(&ent->matrix);
2472                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2473                                         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, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale);
2474                         }
2475                         else
2476                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, numsurfaces, surfacelist);
2477                 }
2478                 if (r_drawentities.integer)
2479                 {
2480                         for (i = 0;i < r_refdef.numentities;i++)
2481                         {
2482                                 ent = r_refdef.entities[i];
2483                                 // can't draw transparent entity lighting here because
2484                                 // transparent meshes are deferred for later
2485                                 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)
2486                                 {
2487                                         VectorScale(lightcolor, ent->alpha, lightcolor2);
2488                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2489                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2490                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2491                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2492                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2493                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, ent->model->nummodelsurfaces, ent->model->surfacelist);
2494                                 }
2495                         }
2496                 }
2497         }
2498 }
2499
2500 void R_ShadowVolumeLighting(int visiblevolumes)
2501 {
2502         int lnum, flag;
2503         dlight_t *light;
2504         rmeshstate_t m;
2505
2506         if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2507                 R_Shadow_EditLights_Reload_f();
2508
2509         if (visiblevolumes)
2510         {
2511                 memset(&m, 0, sizeof(m));
2512                 R_Mesh_State(&m);
2513
2514                 GL_BlendFunc(GL_ONE, GL_ONE);
2515                 GL_DepthMask(false);
2516                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2517                 qglDisable(GL_CULL_FACE);
2518                 GL_Color(0.0, 0.0125, 0.1, 1);
2519         }
2520         else
2521                 R_Shadow_Stage_Begin();
2522         flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2523         if (r_shadow_debuglight.integer >= 0)
2524         {
2525                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2526                         if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2527                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2528         }
2529         else
2530                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2531                         if (light->flags & flag)
2532                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2533         if (r_rtdlight)
2534                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2535                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2536
2537         if (visiblevolumes)
2538         {
2539                 qglEnable(GL_CULL_FACE);
2540                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2541         }
2542         else
2543                 R_Shadow_Stage_End();
2544 }
2545
2546 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2547 typedef struct suffixinfo_s
2548 {
2549         char *suffix;
2550         qboolean flipx, flipy, flipdiagonal;
2551 }
2552 suffixinfo_t;
2553 static suffixinfo_t suffix[3][6] =
2554 {
2555         {
2556                 {"px",   false, false, false},
2557                 {"nx",   false, false, false},
2558                 {"py",   false, false, false},
2559                 {"ny",   false, false, false},
2560                 {"pz",   false, false, false},
2561                 {"nz",   false, false, false}
2562         },
2563         {
2564                 {"posx", false, false, false},
2565                 {"negx", false, false, false},
2566                 {"posy", false, false, false},
2567                 {"negy", false, false, false},
2568                 {"posz", false, false, false},
2569                 {"negz", false, false, false}
2570         },
2571         {
2572                 {"rt",    true, false,  true},
2573                 {"lf",   false,  true,  true},
2574                 {"ft",    true,  true, false},
2575                 {"bk",   false, false, false},
2576                 {"up",    true, false,  true},
2577                 {"dn",    true, false,  true}
2578         }
2579 };
2580
2581 static int componentorder[4] = {0, 1, 2, 3};
2582
2583 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2584 {
2585         int i, j, cubemapsize;
2586         qbyte *cubemappixels, *image_rgba;
2587         rtexture_t *cubemaptexture;
2588         char name[256];
2589         // must start 0 so the first loadimagepixels has no requested width/height
2590         cubemapsize = 0;
2591         cubemappixels = NULL;
2592         cubemaptexture = NULL;
2593         // keep trying different suffix groups (posx, px, rt) until one loads
2594         for (j = 0;j < 3 && !cubemappixels;j++)
2595         {
2596                 // load the 6 images in the suffix group
2597                 for (i = 0;i < 6;i++)
2598                 {
2599                         // generate an image name based on the base and and suffix
2600                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2601                         // load it
2602                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2603                         {
2604                                 // an image loaded, make sure width and height are equal
2605                                 if (image_width == image_height)
2606                                 {
2607                                         // if this is the first image to load successfully, allocate the cubemap memory
2608                                         if (!cubemappixels && image_width >= 1)
2609                                         {
2610                                                 cubemapsize = image_width;
2611                                                 // note this clears to black, so unavailable sides are black
2612                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2613                                         }
2614                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2615                                         if (cubemappixels)
2616                                                 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);
2617                                 }
2618                                 else
2619                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2620                                 // free the image
2621                                 Mem_Free(image_rgba);
2622                         }
2623                 }
2624         }
2625         // if a cubemap loaded, upload it
2626         if (cubemappixels)
2627         {
2628                 if (!r_shadow_filters_texturepool)
2629                         r_shadow_filters_texturepool = R_AllocTexturePool();
2630                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2631                 Mem_Free(cubemappixels);
2632         }
2633         else
2634         {
2635                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2636                 for (j = 0;j < 3;j++)
2637                         for (i = 0;i < 6;i++)
2638                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2639                 Con_Print(" and was unable to find any of them.\n");
2640         }
2641         return cubemaptexture;
2642 }
2643
2644 rtexture_t *R_Shadow_Cubemap(const char *basename)
2645 {
2646         int i;
2647         for (i = 0;i < numcubemaps;i++)
2648                 if (!strcasecmp(cubemaps[i].basename, basename))
2649                         return cubemaps[i].texture;
2650         if (i >= MAX_CUBEMAPS)
2651                 return NULL;
2652         numcubemaps++;
2653         strcpy(cubemaps[i].basename, basename);
2654         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2655         return cubemaps[i].texture;
2656 }
2657
2658 void R_Shadow_FreeCubemaps(void)
2659 {
2660         numcubemaps = 0;
2661         R_FreeTexturePool(&r_shadow_filters_texturepool);
2662 }
2663
2664 dlight_t *R_Shadow_NewWorldLight(void)
2665 {
2666         dlight_t *light;
2667         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2668         light->next = r_shadow_worldlightchain;
2669         r_shadow_worldlightchain = light;
2670         return light;
2671 }
2672
2673 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, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
2674 {
2675         VectorCopy(origin, light->origin);
2676         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2677         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2678         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2679         light->color[0] = max(color[0], 0);
2680         light->color[1] = max(color[1], 0);
2681         light->color[2] = max(color[2], 0);
2682         light->radius = max(radius, 0);
2683         light->style = style;
2684         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2685         {
2686                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2687                 light->style = 0;
2688         }
2689         light->shadow = shadowenable;
2690         light->corona = corona;
2691         if (!cubemapname)
2692                 cubemapname = "";
2693         strlcpy(light->cubemapname, cubemapname, strlen(light->cubemapname));
2694         light->coronasizescale = coronasizescale;
2695         light->ambientscale = ambientscale;
2696         light->diffusescale = diffusescale;
2697         light->specularscale = specularscale;
2698         light->flags = flags;
2699         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2700
2701         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2702 }
2703
2704 void R_Shadow_FreeWorldLight(dlight_t *light)
2705 {
2706         dlight_t **lightpointer;
2707         R_RTLight_Uncompile(&light->rtlight);
2708         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2709         if (*lightpointer != light)
2710                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2711         *lightpointer = light->next;
2712         Mem_Free(light);
2713 }
2714
2715 void R_Shadow_ClearWorldLights(void)
2716 {
2717         while (r_shadow_worldlightchain)
2718                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2719         r_shadow_selectedlight = NULL;
2720         R_Shadow_FreeCubemaps();
2721 }
2722
2723 void R_Shadow_SelectLight(dlight_t *light)
2724 {
2725         if (r_shadow_selectedlight)
2726                 r_shadow_selectedlight->selected = false;
2727         r_shadow_selectedlight = light;
2728         if (r_shadow_selectedlight)
2729                 r_shadow_selectedlight->selected = true;
2730 }
2731
2732 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2733 {
2734         float scale = r_editlights_cursorgrid.value * 0.5f;
2735         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);
2736 }
2737
2738 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2739 {
2740         float intensity;
2741         const dlight_t *light;
2742         light = calldata1;
2743         intensity = 0.5;
2744         if (light->selected)
2745                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2746         if (!light->shadow)
2747                 intensity *= 0.5f;
2748         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);
2749 }
2750
2751 void R_Shadow_DrawLightSprites(void)
2752 {
2753         int i;
2754         cachepic_t *pic;
2755         dlight_t *light;
2756
2757         for (i = 0;i < 5;i++)
2758         {
2759                 lighttextures[i] = NULL;
2760                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2761                         lighttextures[i] = pic->tex;
2762         }
2763
2764         for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
2765                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5);
2766         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2767 }
2768
2769 void R_Shadow_SelectLightInView(void)
2770 {
2771         float bestrating, rating, temp[3];
2772         dlight_t *best, *light;
2773         best = NULL;
2774         bestrating = 0;
2775         for (light = r_shadow_worldlightchain;light;light = light->next)
2776         {
2777                 VectorSubtract(light->origin, r_vieworigin, temp);
2778                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2779                 if (rating >= 0.95)
2780                 {
2781                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2782                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2783                         {
2784                                 bestrating = rating;
2785                                 best = light;
2786                         }
2787                 }
2788         }
2789         R_Shadow_SelectLight(best);
2790 }
2791
2792 void R_Shadow_LoadWorldLights(void)
2793 {
2794         int n, a, style, shadow, flags;
2795         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2796         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
2797         if (cl.worldmodel == NULL)
2798         {
2799                 Con_Print("No map loaded.\n");
2800                 return;
2801         }
2802         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2803         strlcat (name, ".rtlights", sizeof (name));
2804         lightsstring = FS_LoadFile(name, tempmempool, false);
2805         if (lightsstring)
2806         {
2807                 s = lightsstring;
2808                 n = 0;
2809                 while (*s)
2810                 {
2811                         t = s;
2812                         /*
2813                         shadow = true;
2814                         for (;COM_Parse(t, true) && strcmp(
2815                         if (COM_Parse(t, true))
2816                         {
2817                                 if (com_token[0] == '!')
2818                                 {
2819                                         shadow = false;
2820                                         origin[0] = atof(com_token+1);
2821                                 }
2822                                 else
2823                                         origin[0] = atof(com_token);
2824                                 if (Com_Parse(t
2825                         }
2826                         */
2827                         t = s;
2828                         while (*s && *s != '\n')
2829                                 s++;
2830                         if (!*s)
2831                                 break;
2832                         *s = 0;
2833                         shadow = true;
2834                         // check for modifier flags
2835                         if (*t == '!')
2836                         {
2837                                 shadow = false;
2838                                 t++;
2839                         }
2840                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f %f %f %f %f %i", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2], &coronasizescale, &ambientscale, &diffusescale, &specularscale, &flags);
2841                         if (a < 18)
2842                                 flags = LIGHTFLAG_REALTIMEMODE;
2843                         if (a < 17)
2844                                 specularscale = 1;
2845                         if (a < 16)
2846                                 diffusescale = 1;
2847                         if (a < 15)
2848                                 ambientscale = 0;
2849                         if (a < 14)
2850                                 coronasizescale = 0.25f;
2851                         if (a < 13)
2852                                 VectorClear(angles);
2853                         if (a < 10)
2854                                 corona = 0;
2855                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2856                                 cubemapname[0] = 0;
2857                         *s = '\n';
2858                         if (a < 8)
2859                         {
2860                                 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] coronasizescale ambientscale diffusescale specularscale flags)\n", a, n + 1);
2861                                 break;
2862                         }
2863                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2864                         radius *= r_editlights_rtlightssizescale.value;
2865                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
2866                         s++;
2867                         n++;
2868                 }
2869                 if (*s)
2870                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2871                 Mem_Free(lightsstring);
2872         }
2873 }
2874
2875 void R_Shadow_SaveWorldLights(void)
2876 {
2877         dlight_t *light;
2878         int bufchars, bufmaxchars;
2879         char *buf, *oldbuf;
2880         char name[MAX_QPATH];
2881         char line[1024];
2882         if (!r_shadow_worldlightchain)
2883                 return;
2884         if (cl.worldmodel == NULL)
2885         {
2886                 Con_Print("No map loaded.\n");
2887                 return;
2888         }
2889         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2890         strlcat (name, ".rtlights", sizeof (name));
2891         bufchars = bufmaxchars = 0;
2892         buf = NULL;
2893         for (light = r_shadow_worldlightchain;light;light = light->next)
2894         {
2895                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
2896                         sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\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], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
2897                 if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
2898                         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]);
2899                 else
2900                         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);
2901                 if (bufchars + (int) strlen(line) > bufmaxchars)
2902                 {
2903                         bufmaxchars = bufchars + strlen(line) + 2048;
2904                         oldbuf = buf;
2905                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2906                         if (oldbuf)
2907                         {
2908                                 if (bufchars)
2909                                         memcpy(buf, oldbuf, bufchars);
2910                                 Mem_Free(oldbuf);
2911                         }
2912                 }
2913                 if (strlen(line))
2914                 {
2915                         memcpy(buf + bufchars, line, strlen(line));
2916                         bufchars += strlen(line);
2917                 }
2918         }
2919         if (bufchars)
2920                 FS_WriteFile(name, buf, bufchars);
2921         if (buf)
2922                 Mem_Free(buf);
2923 }
2924
2925 void R_Shadow_LoadLightsFile(void)
2926 {
2927         int n, a, style;
2928         char name[MAX_QPATH], *lightsstring, *s, *t;
2929         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2930         if (cl.worldmodel == NULL)
2931         {
2932                 Con_Print("No map loaded.\n");
2933                 return;
2934         }
2935         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2936         strlcat (name, ".lights", sizeof (name));
2937         lightsstring = FS_LoadFile(name, tempmempool, false);
2938         if (lightsstring)
2939         {
2940                 s = lightsstring;
2941                 n = 0;
2942                 while (*s)
2943                 {
2944                         t = s;
2945                         while (*s && *s != '\n')
2946                                 s++;
2947                         if (!*s)
2948                                 break;
2949                         *s = 0;
2950                         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);
2951                         *s = '\n';
2952                         if (a < 14)
2953                         {
2954                                 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);
2955                                 break;
2956                         }
2957                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2958                         radius = bound(15, radius, 4096);
2959                         VectorScale(color, (2.0f / (8388608.0f)), color);
2960                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
2961                         s++;
2962                         n++;
2963                 }
2964                 if (*s)
2965                         Con_Printf("invalid lights file \"%s\"\n", name);
2966                 Mem_Free(lightsstring);
2967         }
2968 }
2969
2970 // tyrlite/hmap2 light types in the delay field
2971 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
2972
2973 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2974 {
2975         int entnum, style, islight, skin, pflags, effects, type, n;
2976         char *entfiledata;
2977         const char *data;
2978         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
2979         char key[256], value[1024];
2980
2981         if (cl.worldmodel == NULL)
2982         {
2983                 Con_Print("No map loaded.\n");
2984                 return;
2985         }
2986         // try to load a .ent file first
2987         FS_StripExtension (cl.worldmodel->name, key, sizeof (key));
2988         strlcat (key, ".ent", sizeof (key));
2989         data = entfiledata = FS_LoadFile(key, tempmempool, true);
2990         // and if that is not found, fall back to the bsp file entity string
2991         if (!data)
2992                 data = cl.worldmodel->brush.entities;
2993         if (!data)
2994                 return;
2995         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2996         {
2997                 type = LIGHTTYPE_MINUSX;
2998                 origin[0] = origin[1] = origin[2] = 0;
2999                 originhack[0] = originhack[1] = originhack[2] = 0;
3000                 angles[0] = angles[1] = angles[2] = 0;
3001                 color[0] = color[1] = color[2] = 1;
3002                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3003                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3004                 fadescale = 1;
3005                 lightscale = 1;
3006                 style = 0;
3007                 skin = 0;
3008                 pflags = 0;
3009                 effects = 0;
3010                 islight = false;
3011                 while (1)
3012                 {
3013                         if (!COM_ParseToken(&data, false))
3014                                 break; // error
3015                         if (com_token[0] == '}')
3016                                 break; // end of entity
3017                         if (com_token[0] == '_')
3018                                 strcpy(key, com_token + 1);
3019                         else
3020                                 strcpy(key, com_token);
3021                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3022                                 key[strlen(key)-1] = 0;
3023                         if (!COM_ParseToken(&data, false))
3024                                 break; // error
3025                         strcpy(value, com_token);
3026
3027                         // now that we have the key pair worked out...
3028                         if (!strcmp("light", key))
3029                         {
3030                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3031                                 if (n == 1)
3032                                 {
3033                                         // quake
3034                                         light[0] = vec[0] * (1.0f / 256.0f);
3035                                         light[1] = vec[0] * (1.0f / 256.0f);
3036                                         light[2] = vec[0] * (1.0f / 256.0f);
3037                                         light[3] = vec[0];
3038                                 }
3039                                 else if (n == 4)
3040                                 {
3041                                         // halflife
3042                                         light[0] = vec[0] * (1.0f / 255.0f);
3043                                         light[1] = vec[1] * (1.0f / 255.0f);
3044                                         light[2] = vec[2] * (1.0f / 255.0f);
3045                                         light[3] = vec[3];
3046                                 }
3047                         }
3048                         else if (!strcmp("delay", key))
3049                                 type = atoi(value);
3050                         else if (!strcmp("origin", key))
3051                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3052                         else if (!strcmp("angle", key))
3053                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3054                         else if (!strcmp("angles", key))
3055                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3056                         else if (!strcmp("color", key))
3057                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3058                         else if (!strcmp("wait", key))
3059                                 fadescale = atof(value);
3060                         else if (!strcmp("classname", key))
3061                         {
3062                                 if (!strncmp(value, "light", 5))
3063                                 {
3064                                         islight = true;
3065                                         if (!strcmp(value, "light_fluoro"))
3066                                         {
3067                                                 originhack[0] = 0;
3068                                                 originhack[1] = 0;
3069                                                 originhack[2] = 0;
3070                                                 overridecolor[0] = 1;
3071                                                 overridecolor[1] = 1;
3072                                                 overridecolor[2] = 1;
3073                                         }
3074                                         if (!strcmp(value, "light_fluorospark"))
3075                                         {
3076                                                 originhack[0] = 0;
3077                                                 originhack[1] = 0;
3078                                                 originhack[2] = 0;
3079                                                 overridecolor[0] = 1;
3080                                                 overridecolor[1] = 1;
3081                                                 overridecolor[2] = 1;
3082                                         }
3083                                         if (!strcmp(value, "light_globe"))
3084                                         {
3085                                                 originhack[0] = 0;
3086                                                 originhack[1] = 0;
3087                                                 originhack[2] = 0;
3088                                                 overridecolor[0] = 1;
3089                                                 overridecolor[1] = 0.8;
3090                                                 overridecolor[2] = 0.4;
3091                                         }
3092                                         if (!strcmp(value, "light_flame_large_yellow"))
3093                                         {
3094                                                 originhack[0] = 0;
3095                                                 originhack[1] = 0;
3096                                                 originhack[2] = 48;
3097                                                 overridecolor[0] = 1;
3098                                                 overridecolor[1] = 0.5;
3099                                                 overridecolor[2] = 0.1;
3100                                         }
3101                                         if (!strcmp(value, "light_flame_small_yellow"))
3102                                         {
3103                                                 originhack[0] = 0;
3104                                                 originhack[1] = 0;
3105                                                 originhack[2] = 40;
3106                                                 overridecolor[0] = 1;
3107                                                 overridecolor[1] = 0.5;
3108                                                 overridecolor[2] = 0.1;
3109                                         }
3110                                         if (!strcmp(value, "light_torch_small_white"))
3111                                         {
3112                                                 originhack[0] = 0;
3113                                                 originhack[1] = 0;
3114                                                 originhack[2] = 40;
3115                                                 overridecolor[0] = 1;
3116                                                 overridecolor[1] = 0.5;
3117                                                 overridecolor[2] = 0.1;
3118                                         }
3119                                         if (!strcmp(value, "light_torch_small_walltorch"))
3120                                         {
3121                                                 originhack[0] = 0;
3122                                                 originhack[1] = 0;
3123                                                 originhack[2] = 40;
3124                                                 overridecolor[0] = 1;
3125                                                 overridecolor[1] = 0.5;
3126                                                 overridecolor[2] = 0.1;
3127                                         }
3128                                 }
3129                         }
3130                         else if (!strcmp("style", key))
3131                                 style = atoi(value);
3132                         else if (cl.worldmodel->type == mod_brushq3)
3133                         {
3134                                 if (!strcmp("scale", key))
3135                                         lightscale = atof(value);
3136                                 if (!strcmp("fade", key))
3137                                         fadescale = atof(value);
3138                         }
3139                         else if (!strcmp("skin", key))
3140                                 skin = (int)atof(value);
3141                         else if (!strcmp("pflags", key))
3142                                 pflags = (int)atof(value);
3143                         else if (!strcmp("effects", key))
3144                                 effects = (int)atof(value);
3145                 }
3146                 if (!islight)
3147                         continue;
3148                 if (lightscale <= 0)
3149                         lightscale = 1;
3150                 if (fadescale <= 0)
3151                         fadescale = 1;
3152                 if (color[0] == color[1] && color[0] == color[2])
3153                 {
3154                         color[0] *= overridecolor[0];
3155                         color[1] *= overridecolor[1];
3156                         color[2] *= overridecolor[2];
3157                 }
3158                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3159                 color[0] = color[0] * light[0];
3160                 color[1] = color[1] * light[1];
3161                 color[2] = color[2] * light[2];
3162                 switch (type)
3163                 {
3164                 case LIGHTTYPE_MINUSX:
3165                         break;
3166                 case LIGHTTYPE_RECIPX:
3167                         radius *= 2;
3168                         VectorScale(color, (1.0f / 16.0f), color);
3169                         break;
3170                 case LIGHTTYPE_RECIPXX:
3171                         radius *= 2;
3172                         VectorScale(color, (1.0f / 16.0f), color);
3173                         break;
3174                 default:
3175                 case LIGHTTYPE_NONE:
3176                         break;
3177                 case LIGHTTYPE_SUN:
3178                         break;
3179                 case LIGHTTYPE_MINUSXX:
3180                         break;
3181                 }
3182                 VectorAdd(origin, originhack, origin);
3183                 if (radius >= 1)
3184                         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, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3185         }
3186         if (entfiledata)
3187                 Mem_Free(entfiledata);
3188 }
3189
3190
3191 void R_Shadow_SetCursorLocationForView(void)
3192 {
3193         vec_t dist, push, frac;
3194         vec3_t dest, endpos, normal;
3195         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
3196         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
3197         if (frac < 1)
3198         {
3199                 dist = frac * r_editlights_cursordistance.value;
3200                 push = r_editlights_cursorpushback.value;
3201                 if (push > dist)
3202                         push = dist;
3203                 push = -push;
3204                 VectorMA(endpos, push, r_viewforward, endpos);
3205                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
3206         }
3207         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3208         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3209         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3210 }
3211
3212 void R_Shadow_UpdateWorldLightSelection(void)
3213 {
3214         if (r_editlights.integer)
3215         {
3216                 R_Shadow_SetCursorLocationForView();
3217                 R_Shadow_SelectLightInView();
3218                 R_Shadow_DrawLightSprites();
3219         }
3220         else
3221                 R_Shadow_SelectLight(NULL);
3222 }
3223
3224 void R_Shadow_EditLights_Clear_f(void)
3225 {
3226         R_Shadow_ClearWorldLights();
3227 }
3228
3229 void R_Shadow_EditLights_Reload_f(void)
3230 {
3231         if (!cl.worldmodel)
3232                 return;
3233         strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
3234         R_Shadow_ClearWorldLights();
3235         R_Shadow_LoadWorldLights();
3236         if (r_shadow_worldlightchain == NULL)
3237         {
3238                 R_Shadow_LoadLightsFile();
3239                 if (r_shadow_worldlightchain == NULL)
3240                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3241         }
3242 }
3243
3244 void R_Shadow_EditLights_Save_f(void)
3245 {
3246         if (!cl.worldmodel)
3247                 return;
3248         R_Shadow_SaveWorldLights();
3249 }
3250
3251 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3252 {
3253         R_Shadow_ClearWorldLights();
3254         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3255 }
3256
3257 void R_Shadow_EditLights_ImportLightsFile_f(void)
3258 {
3259         R_Shadow_ClearWorldLights();
3260         R_Shadow_LoadLightsFile();
3261 }
3262
3263 void R_Shadow_EditLights_Spawn_f(void)
3264 {
3265         vec3_t color;
3266         if (!r_editlights.integer)
3267         {
3268                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3269                 return;
3270         }
3271         if (Cmd_Argc() != 1)
3272         {
3273                 Con_Print("r_editlights_spawn does not take parameters\n");
3274                 return;
3275         }
3276         color[0] = color[1] = color[2] = 1;
3277         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3278 }
3279
3280 void R_Shadow_EditLights_Edit_f(void)
3281 {
3282         vec3_t origin, angles, color;
3283         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3284         int style, shadows, flags, normalmode, realtimemode;
3285         char cubemapname[1024];
3286         if (!r_editlights.integer)
3287         {
3288                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3289                 return;
3290         }
3291         if (!r_shadow_selectedlight)
3292         {
3293                 Con_Print("No selected light.\n");
3294                 return;
3295         }
3296         VectorCopy(r_shadow_selectedlight->origin, origin);
3297         VectorCopy(r_shadow_selectedlight->angles, angles);
3298         VectorCopy(r_shadow_selectedlight->color, color);
3299         radius = r_shadow_selectedlight->radius;
3300         style = r_shadow_selectedlight->style;
3301         if (r_shadow_selectedlight->cubemapname)
3302                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
3303         else
3304                 cubemapname[0] = 0;
3305         shadows = r_shadow_selectedlight->shadow;
3306         corona = r_shadow_selectedlight->corona;
3307         coronasizescale = r_shadow_selectedlight->coronasizescale;
3308         ambientscale = r_shadow_selectedlight->ambientscale;
3309         diffusescale = r_shadow_selectedlight->diffusescale;
3310         specularscale = r_shadow_selectedlight->specularscale;
3311         flags = r_shadow_selectedlight->flags;
3312         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3313         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3314         if (!strcmp(Cmd_Argv(1), "origin"))
3315         {
3316                 if (Cmd_Argc() != 5)
3317                 {
3318                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3319                         return;
3320                 }
3321                 origin[0] = atof(Cmd_Argv(2));
3322                 origin[1] = atof(Cmd_Argv(3));
3323                 origin[2] = atof(Cmd_Argv(4));
3324         }
3325         else if (!strcmp(Cmd_Argv(1), "originx"))
3326         {
3327                 if (Cmd_Argc() != 3)
3328                 {
3329                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3330                         return;
3331                 }
3332                 origin[0] = atof(Cmd_Argv(2));
3333         }
3334         else if (!strcmp(Cmd_Argv(1), "originy"))
3335         {
3336                 if (Cmd_Argc() != 3)
3337                 {
3338                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3339                         return;
3340                 }
3341                 origin[1] = atof(Cmd_Argv(2));
3342         }
3343         else if (!strcmp(Cmd_Argv(1), "originz"))
3344         {
3345                 if (Cmd_Argc() != 3)
3346                 {
3347                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3348                         return;
3349                 }
3350                 origin[2] = atof(Cmd_Argv(2));
3351         }
3352         else if (!strcmp(Cmd_Argv(1), "move"))
3353         {
3354                 if (Cmd_Argc() != 5)
3355                 {
3356                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3357                         return;
3358                 }
3359                 origin[0] += atof(Cmd_Argv(2));
3360                 origin[1] += atof(Cmd_Argv(3));
3361                 origin[2] += atof(Cmd_Argv(4));
3362         }
3363         else if (!strcmp(Cmd_Argv(1), "movex"))
3364         {
3365                 if (Cmd_Argc() != 3)
3366                 {
3367                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3368                         return;
3369                 }
3370                 origin[0] += atof(Cmd_Argv(2));
3371         }
3372         else if (!strcmp(Cmd_Argv(1), "movey"))
3373         {
3374                 if (Cmd_Argc() != 3)
3375                 {
3376                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3377                         return;
3378                 }
3379                 origin[1] += atof(Cmd_Argv(2));
3380         }
3381         else if (!strcmp(Cmd_Argv(1), "movez"))
3382         {
3383                 if (Cmd_Argc() != 3)
3384                 {
3385                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3386                         return;
3387                 }
3388                 origin[2] += atof(Cmd_Argv(2));
3389         }
3390         else if (!strcmp(Cmd_Argv(1), "angles"))
3391         {
3392                 if (Cmd_Argc() != 5)
3393                 {
3394                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3395                         return;
3396                 }
3397                 angles[0] = atof(Cmd_Argv(2));
3398                 angles[1] = atof(Cmd_Argv(3));
3399                 angles[2] = atof(Cmd_Argv(4));
3400         }
3401         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3402         {
3403                 if (Cmd_Argc() != 3)
3404                 {
3405                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3406                         return;
3407                 }
3408                 angles[0] = atof(Cmd_Argv(2));
3409         }
3410         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3411         {
3412                 if (Cmd_Argc() != 3)
3413                 {
3414                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3415                         return;
3416                 }
3417                 angles[1] = atof(Cmd_Argv(2));
3418         }
3419         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3420         {
3421                 if (Cmd_Argc() != 3)
3422                 {
3423                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3424                         return;
3425                 }
3426                 angles[2] = atof(Cmd_Argv(2));
3427         }
3428         else if (!strcmp(Cmd_Argv(1), "color"))
3429         {
3430                 if (Cmd_Argc() != 5)
3431                 {
3432                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3433                         return;
3434                 }
3435                 color[0] = atof(Cmd_Argv(2));
3436                 color[1] = atof(Cmd_Argv(3));
3437                 color[2] = atof(Cmd_Argv(4));
3438         }
3439         else if (!strcmp(Cmd_Argv(1), "radius"))
3440         {
3441                 if (Cmd_Argc() != 3)
3442                 {
3443                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3444                         return;
3445                 }
3446                 radius = atof(Cmd_Argv(2));
3447         }
3448         else if (!strcmp(Cmd_Argv(1), "style"))
3449         {
3450                 if (Cmd_Argc() != 3)
3451                 {
3452                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3453                         return;
3454                 }
3455                 style = atoi(Cmd_Argv(2));
3456         }
3457         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3458         {
3459                 if (Cmd_Argc() > 3)
3460                 {
3461                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3462                         return;
3463                 }
3464                 if (Cmd_Argc() == 3)
3465                         strcpy(cubemapname, Cmd_Argv(2));
3466                 else
3467                         cubemapname[0] = 0;
3468         }
3469         else if (!strcmp(Cmd_Argv(1), "shadows"))
3470         {
3471                 if (Cmd_Argc() != 3)
3472                 {
3473                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3474                         return;
3475                 }
3476                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3477         }
3478         else if (!strcmp(Cmd_Argv(1), "corona"))
3479         {
3480                 if (Cmd_Argc() != 3)
3481                 {
3482                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3483                         return;
3484                 }
3485                 corona = atof(Cmd_Argv(2));
3486         }
3487         else if (!strcmp(Cmd_Argv(1), "coronasize"))
3488         {
3489                 if (Cmd_Argc() != 3)
3490                 {
3491                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3492                         return;
3493                 }
3494                 coronasizescale = atof(Cmd_Argv(2));
3495         }
3496         else if (!strcmp(Cmd_Argv(1), "ambient"))
3497         {
3498                 if (Cmd_Argc() != 3)
3499                 {
3500                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3501                         return;
3502                 }
3503                 ambientscale = atof(Cmd_Argv(2));
3504         }
3505         else if (!strcmp(Cmd_Argv(1), "diffuse"))
3506         {
3507                 if (Cmd_Argc() != 3)
3508                 {
3509                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3510                         return;
3511                 }
3512                 diffusescale = atof(Cmd_Argv(2));
3513         }
3514         else if (!strcmp(Cmd_Argv(1), "specular"))
3515         {
3516                 if (Cmd_Argc() != 3)
3517                 {
3518                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3519                         return;
3520                 }
3521                 specularscale = atof(Cmd_Argv(2));
3522         }
3523         else if (!strcmp(Cmd_Argv(1), "normalmode"))
3524         {
3525                 if (Cmd_Argc() != 3)
3526                 {
3527                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3528                         return;
3529                 }
3530                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3531         }
3532         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3533         {
3534                 if (Cmd_Argc() != 3)
3535                 {
3536                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3537                         return;
3538                 }
3539                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3540         }
3541         else
3542         {
3543                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3544                 Con_Print("Selected light's properties:\n");
3545                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3546                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3547                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3548                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
3549                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
3550                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
3551                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3552                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
3553                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
3554                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
3555                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
3556                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
3557                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
3558                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
3559                 return;
3560         }
3561         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
3562         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3563 }
3564
3565 void R_Shadow_EditLights_EditAll_f(void)
3566 {
3567         dlight_t *light;
3568
3569         if (!r_editlights.integer)
3570         {
3571                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
3572                 return;
3573         }
3574
3575         for (light = r_shadow_worldlightchain;light;light = light->next)
3576         {
3577                 R_Shadow_SelectLight(light);
3578                 R_Shadow_EditLights_Edit_f();
3579         }
3580 }
3581
3582 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3583 {
3584         int lightnumber, lightcount;
3585         dlight_t *light;
3586         float x, y;
3587         char temp[256];
3588         if (!r_editlights.integer)
3589                 return;
3590         x = 0;
3591         y = con_vislines;
3592         lightnumber = -1;
3593         lightcount = 0;
3594         for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
3595                 if (light == r_shadow_selectedlight)
3596                         lightnumber = lightcount;
3597         sprintf(temp, "Cursor  %f %f %f  Total Lights %i", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2], lightcount);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3598         if (r_shadow_selectedlight == NULL)
3599                 return;
3600         sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3601         sprintf(temp, "Origin       : %f %f %f\n", 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;
3602         sprintf(temp, "Angles       : %f %f %f\n", 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;
3603         sprintf(temp, "Color        : %f %f %f\n", 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;
3604         sprintf(temp, "Radius       : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3605         sprintf(temp, "Corona       : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3606         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3607         sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3608         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3609         sprintf(temp, "CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3610         sprintf(temp, "Ambient      : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3611         sprintf(temp, "Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3612         sprintf(temp, "Specular     : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3613         sprintf(temp, "NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3614         sprintf(temp, "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3615 }                 
3616
3617 void R_Shadow_EditLights_ToggleShadow_f(void)
3618 {
3619         if (!r_editlights.integer)
3620         {
3621                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3622                 return;
3623         }
3624         if (!r_shadow_selectedlight)
3625         {
3626                 Con_Print("No selected light.\n");
3627                 return;
3628         }
3629         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, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
3630 }
3631
3632 void R_Shadow_EditLights_ToggleCorona_f(void)
3633 {
3634         if (!r_editlights.integer)
3635         {
3636                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3637                 return;
3638         }
3639         if (!r_shadow_selectedlight)
3640         {
3641                 Con_Print("No selected light.\n");
3642                 return;
3643         }
3644         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, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
3645 }
3646
3647 void R_Shadow_EditLights_Remove_f(void)
3648 {
3649         if (!r_editlights.integer)
3650         {
3651                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3652                 return;
3653         }
3654         if (!r_shadow_selectedlight)
3655         {
3656                 Con_Print("No selected light.\n");
3657                 return;
3658         }
3659         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3660         r_shadow_selectedlight = NULL;
3661 }
3662
3663 void R_Shadow_EditLights_Help_f(void)
3664 {
3665         Con_Print(
3666 "Documentation on r_editlights system:\n"
3667 "Settings:\n"
3668 "r_editlights : enable/disable editing mode\n"
3669 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3670 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3671 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3672 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3673 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3674 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3675 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3676 "Commands:\n"
3677 "r_editlights_help : this help\n"
3678 "r_editlights_clear : remove all lights\n"
3679 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3680 "r_editlights_save : save to .rtlights file\n"
3681 "r_editlights_spawn : create a light with default settings\n"
3682 "r_editlights_edit command : edit selected light - more documentation below\n"
3683 "r_editlights_remove : remove selected light\n"
3684 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3685 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3686 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3687 "Edit commands:\n"
3688 "origin x y z : set light location\n"
3689 "originx x: set x component of light location\n"
3690 "originy y: set y component of light location\n"
3691 "originz z: set z component of light location\n"
3692 "move x y z : adjust light location\n"
3693 "movex x: adjust x component of light location\n"
3694 "movey y: adjust y component of light location\n"
3695 "movez z: adjust z component of light location\n"
3696 "angles x y z : set light angles\n"
3697 "anglesx x: set x component of light angles\n"
3698 "anglesy y: set y component of light angles\n"
3699 "anglesz z: set z component of light angles\n"
3700 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3701 "radius radius : set radius (size) of light\n"
3702 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3703 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3704 "shadows 1/0 : turn on/off shadows\n"
3705 "corona n : set corona intensity\n"
3706 "coronasize n : set corona size (0-1)\n"
3707 "ambient n : set ambient intensity (0-1)\n"
3708 "diffuse n : set diffuse intensity (0-1)\n"
3709 "specular n : set specular intensity (0-1)\n"
3710 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
3711 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
3712 "<nothing> : print light properties to console\n"
3713         );
3714 }
3715
3716 void R_Shadow_EditLights_CopyInfo_f(void)
3717 {
3718         if (!r_editlights.integer)
3719         {
3720                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
3721                 return;
3722         }
3723         if (!r_shadow_selectedlight)
3724         {
3725                 Con_Print("No selected light.\n");
3726                 return;
3727         }
3728         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
3729         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
3730         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
3731         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
3732         if (r_shadow_selectedlight->cubemapname)
3733                 strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
3734         else
3735                 r_shadow_bufferlight.cubemapname[0] = 0;
3736         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
3737         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
3738         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
3739         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
3740         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
3741         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
3742         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
3743 }
3744
3745 void R_Shadow_EditLights_PasteInfo_f(void)
3746 {
3747         if (!r_editlights.integer)
3748         {
3749                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
3750                 return;
3751         }
3752         if (!r_shadow_selectedlight)
3753         {
3754                 Con_Print("No selected light.\n");
3755                 return;
3756         }
3757         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, r_shadow_bufferlight.coronasizescale, r_shadow_bufferlight.ambientscale, r_shadow_bufferlight.diffusescale, r_shadow_bufferlight.specularscale, r_shadow_bufferlight.flags);
3758 }
3759
3760 void R_Shadow_EditLights_Init(void)
3761 {
3762         Cvar_RegisterVariable(&r_editlights);
3763         Cvar_RegisterVariable(&r_editlights_cursordistance);
3764         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3765         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3766         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3767         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3768         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3769         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3770         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3771         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3772         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3773         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3774         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3775         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3776         Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
3777         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3778         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3779         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3780         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3781         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3782         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f);
3783         Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f);
3784 }
3785