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