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