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