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