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