]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
improved error message for missing cubemap images, no longer complains during cubemap...
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
20
21 Patent warning:
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
26 shadows do not lie.
27
28
29
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
36
37
38
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
44 in some ideal cases).
45
46
47
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however.  Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
57
58
59
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
65
66
67
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
76
77
78
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
82
83
84
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
89
90
91
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
96
97
98
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
105
106
107
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
112 */
113
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
117 #include "portals.h"
118 #include "image.h"
119
120 extern void R_Shadow_EditLights_Init(void);
121
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_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
1846 typedef struct cubemapinfo_s
1847 {
1848         char basename[64];
1849         rtexture_t *texture;
1850 }
1851 cubemapinfo_t;
1852
1853 #define MAX_CUBEMAPS 128
1854 static int numcubemaps;
1855 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1856
1857 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1858 typedef struct suffixinfo_s
1859 {
1860         char *suffix;
1861         int flipx, flipy, flipdiagonal;
1862 }
1863 suffixinfo_t;
1864 static suffixinfo_t suffix[3][6] =
1865 {
1866         {
1867                 {"posx", false, false, false},
1868                 {"negx", false, false, false},
1869                 {"posy", false, false, false},
1870                 {"negy", false, false, false},
1871                 {"posz", false, false, false},
1872                 {"negz", false, false, false}
1873         },
1874         {
1875                 {"px", false, false, false},
1876                 {"nx", false, false, false},
1877                 {"py", false, false, false},
1878                 {"ny", false, false, false},
1879                 {"pz", false, false, false},
1880                 {"nz", false, false, false}
1881         },
1882         {
1883                 {"ft", true, false, true},
1884                 {"bk", false, true, true},
1885                 {"lf", true, true, false},
1886                 {"rt", false, false, false},
1887                 {"up", false, false, false},
1888                 {"dn", false, false, false}
1889         }
1890 };
1891
1892 static int componentorder[4] = {0, 1, 2, 3};
1893
1894 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1895 {
1896         int i, j, cubemapsize;
1897         qbyte *cubemappixels, *image_rgba;
1898         rtexture_t *cubemaptexture;
1899         char name[256];
1900         // must start 0 so the first loadimagepixels has no requested width/height
1901         cubemapsize = 0;
1902         cubemappixels = NULL;
1903         cubemaptexture = NULL;
1904         for (j = 0;j < 3 && !cubemappixels;j++)
1905         {
1906                 for (i = 0;i < 6;i++)
1907                 {
1908                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1909                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
1910                         {
1911                                 if (image_width == image_height)
1912                                 {
1913                                         if (!cubemappixels && image_width >= 1)
1914                                         {
1915                                                 cubemapsize = image_width;
1916                                                 // note this clears to black, so unavailable sizes are black
1917                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1918                                         }
1919                                         if (cubemappixels)
1920                                                 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);
1921                                 }
1922                                 else
1923                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1924                                 Mem_Free(image_rgba);
1925                         }
1926                 }
1927         }
1928         if (cubemappixels)
1929         {
1930                 if (!r_shadow_filters_texturepool)
1931                         r_shadow_filters_texturepool = R_AllocTexturePool();
1932                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1933                 Mem_Free(cubemappixels);
1934         }
1935         else
1936         {
1937                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1938                 for (j = 0;j < 3;j++)
1939                         for (i = 0;i < 6;i++)
1940                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1941                 Con_Printf(" and was unable to find any of them.\n");
1942         }
1943         return cubemaptexture;
1944 }
1945
1946 rtexture_t *R_Shadow_Cubemap(const char *basename)
1947 {
1948         int i;
1949         for (i = 0;i < numcubemaps;i++)
1950                 if (!strcasecmp(cubemaps[i].basename, basename))
1951                         return cubemaps[i].texture;
1952         if (i >= MAX_CUBEMAPS)
1953                 return NULL;
1954         numcubemaps++;
1955         strcpy(cubemaps[i].basename, basename);
1956         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1957         return cubemaps[i].texture;
1958 }
1959
1960 void R_Shadow_FreeCubemaps(void)
1961 {
1962         numcubemaps = 0;
1963         R_FreeTexturePool(&r_shadow_filters_texturepool);
1964 }
1965
1966 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1967 {
1968         int i, j, k, l, maxverts = 256, tris;
1969         float *vertex3f = NULL, mins[3], maxs[3];
1970         worldlight_t *e;
1971         shadowmesh_t *mesh, *castmesh = NULL;
1972
1973         if (radius < 15 || DotProduct(color, color) < 0.03)
1974         {
1975                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1976                 return;
1977         }
1978
1979         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1980         VectorCopy(origin, e->origin);
1981         VectorCopy(color, e->light);
1982         e->lightradius = radius;
1983         e->style = style;
1984         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1985         {
1986                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1987                 e->style = 0;
1988         }
1989         e->castshadows = castshadow;
1990
1991         e->cullradius = e->lightradius;
1992         for (k = 0;k < 3;k++)
1993         {
1994                 mins[k] = e->origin[k] - e->lightradius;
1995                 maxs[k] = e->origin[k] + e->lightradius;
1996         }
1997
1998         e->next = r_shadow_worldlightchain;
1999         r_shadow_worldlightchain = e;
2000         if (cubemapname && cubemapname[0])
2001         {
2002                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2003                 strcpy(e->cubemapname, cubemapname);
2004                 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2005         }
2006         // FIXME: rewrite this to store ALL geometry into a cache in the light
2007         if (e->castshadows)
2008                 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2009         e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2010         if (cl.worldmodel)
2011         {
2012                 if (cl.worldmodel->brushq3.num_leafs)
2013                 {
2014                         q3mleaf_t *leaf;
2015                         q3mface_t *face;
2016                         lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2017                         VectorCopy(e->origin, e->mins);
2018                         VectorCopy(e->origin, e->maxs);
2019                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2020                                 face->lighttemp_castshadow = false;
2021                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2022                         {
2023                                 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2024                                 {
2025                                         for (k = 0;k < 3;k++)
2026                                         {
2027                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2028                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2029                                         }
2030                                         for (j = 0;j < leaf->numleaffaces;j++)
2031                                         {
2032                                                 face = leaf->firstleafface[j];
2033                                                 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2034                                                         face->lighttemp_castshadow = true;
2035                                         }
2036                                 }
2037                         }
2038
2039                         // add surfaces to shadow casting mesh and light mesh
2040                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2041                         {
2042                                 if (face->lighttemp_castshadow)
2043                                 {
2044                                         face->lighttemp_castshadow = false;
2045                                         if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2046                                         {
2047                                                 if (e->castshadows)
2048                                                         if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2049                                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2050                                                 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2051                                                         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);
2052                                         }
2053                                 }
2054                         }
2055                 }
2056                 else if (cl.worldmodel->brushq1.numleafs)
2057                 {
2058                         mleaf_t *leaf;
2059                         msurface_t *surf;
2060                         VectorCopy(e->origin, e->mins);
2061                         VectorCopy(e->origin, e->maxs);
2062                         i = CL_PointQ1Contents(e->origin);
2063
2064                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2065                                 surf->lighttemp_castshadow = false;
2066
2067                         if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2068                         {
2069                                 qbyte *byteleafpvs;
2070                                 qbyte *bytesurfacepvs;
2071
2072                                 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
2073                                 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2074
2075                                 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2076
2077                                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
2078                                 {
2079                                         if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2080                                         {
2081                                                 for (k = 0;k < 3;k++)
2082                                                 {
2083                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2084                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2085                                                 }
2086                                         }
2087                                 }
2088
2089                                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2090                                         if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2091                                                 surf->lighttemp_castshadow = true;
2092
2093                                 Mem_Free(byteleafpvs);
2094                                 Mem_Free(bytesurfacepvs);
2095                         }
2096                         else
2097                         {
2098                                 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2099                                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
2100                                 {
2101                                         if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2102                                         {
2103                                                 for (k = 0;k < 3;k++)
2104                                                 {
2105                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2106                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2107                                                 }
2108                                                 for (j = 0;j < leaf->nummarksurfaces;j++)
2109                                                 {
2110                                                         surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2111                                                         if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2112                                                                 surf->lighttemp_castshadow = true;
2113                                                 }
2114                                         }
2115                                 }
2116                         }
2117
2118                         // add surfaces to shadow casting mesh and light mesh
2119                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2120                         {
2121                                 if (surf->lighttemp_castshadow)
2122                                 {
2123                                         surf->lighttemp_castshadow = false;
2124                                         if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
2125                                                 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);
2126                                         if (!(surf->flags & SURF_DRAWSKY))
2127                                                 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);
2128                                 }
2129                         }
2130                 }
2131         }
2132
2133         // limit box to light bounds (in case it grew larger)
2134         for (k = 0;k < 3;k++)
2135         {
2136                 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
2137                 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
2138         }
2139         e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2140
2141         // cast shadow volume from castmesh
2142         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2143         if (castmesh)
2144         {
2145                 maxverts = 0;
2146                 for (mesh = castmesh;mesh;mesh = mesh->next)
2147                 {
2148                         R_Shadow_ResizeShadowElements(mesh->numtriangles);
2149                         maxverts = max(maxverts, mesh->numverts * 2);
2150                 }
2151
2152                 if (maxverts > 0)
2153                 {
2154                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2155                         // now that we have the buffers big enough, construct and add
2156                         // the shadow volume mesh
2157                         if (e->castshadows)
2158                                 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2159                         for (mesh = castmesh;mesh;mesh = mesh->next)
2160                         {
2161                                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2162                                 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)))
2163                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2164                         }
2165                         Mem_Free(vertex3f);
2166                         vertex3f = NULL;
2167                 }
2168                 // we're done with castmesh now
2169                 Mod_ShadowMesh_Free(castmesh);
2170         }
2171
2172         e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2173         e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2174
2175         k = 0;
2176         if (e->meshchain_shadow)
2177                 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2178                         k += mesh->numtriangles;
2179         l = 0;
2180         if (e->meshchain_light)
2181                 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2182                         l += mesh->numtriangles;
2183         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);
2184 }
2185
2186 void R_Shadow_FreeWorldLight(worldlight_t *light)
2187 {
2188         worldlight_t **lightpointer;
2189         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2190         if (*lightpointer != light)
2191                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2192         *lightpointer = light->next;
2193         if (light->cubemapname)
2194                 Mem_Free(light->cubemapname);
2195         if (light->meshchain_shadow)
2196                 Mod_ShadowMesh_Free(light->meshchain_shadow);
2197         if (light->meshchain_light)
2198                 Mod_ShadowMesh_Free(light->meshchain_light);
2199         Mem_Free(light);
2200 }
2201
2202 void R_Shadow_ClearWorldLights(void)
2203 {
2204         while (r_shadow_worldlightchain)
2205                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2206         r_shadow_selectedlight = NULL;
2207         R_Shadow_FreeCubemaps();
2208 }
2209
2210 void R_Shadow_SelectLight(worldlight_t *light)
2211 {
2212         if (r_shadow_selectedlight)
2213                 r_shadow_selectedlight->selected = false;
2214         r_shadow_selectedlight = light;
2215         if (r_shadow_selectedlight)
2216                 r_shadow_selectedlight->selected = true;
2217 }
2218
2219 rtexture_t *lighttextures[5];
2220
2221 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2222 {
2223         float scale = r_editlights_cursorgrid.value * 0.5f;
2224         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);
2225 }
2226
2227 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2228 {
2229         float intensity;
2230         const worldlight_t *light;
2231         light = calldata1;
2232         intensity = 0.5;
2233         if (light->selected)
2234                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2235         if (!light->meshchain_shadow)
2236                 intensity *= 0.5f;
2237         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);
2238 }
2239
2240 void R_Shadow_DrawLightSprites(void)
2241 {
2242         int i;
2243         cachepic_t *pic;
2244         worldlight_t *light;
2245
2246         for (i = 0;i < 5;i++)
2247         {
2248                 lighttextures[i] = NULL;
2249                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2250                         lighttextures[i] = pic->tex;
2251         }
2252
2253         for (light = r_shadow_worldlightchain;light;light = light->next)
2254                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2255         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2256 }
2257
2258 void R_Shadow_SelectLightInView(void)
2259 {
2260         float bestrating, rating, temp[3];
2261         worldlight_t *best, *light;
2262         best = NULL;
2263         bestrating = 0;
2264         for (light = r_shadow_worldlightchain;light;light = light->next)
2265         {
2266                 VectorSubtract(light->origin, r_vieworigin, temp);
2267                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2268                 if (rating >= 0.95)
2269                 {
2270                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2271                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2272                         {
2273                                 bestrating = rating;
2274                                 best = light;
2275                         }
2276                 }
2277         }
2278         R_Shadow_SelectLight(best);
2279 }
2280
2281 void R_Shadow_LoadWorldLights(void)
2282 {
2283         int n, a, style, shadow;
2284         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2285         float origin[3], radius, color[3];
2286         if (cl.worldmodel == NULL)
2287         {
2288                 Con_Printf("No map loaded.\n");
2289                 return;
2290         }
2291         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2292         strlcat (name, ".rtlights", sizeof (name));
2293         lightsstring = FS_LoadFile(name, false);
2294         if (lightsstring)
2295         {
2296                 s = lightsstring;
2297                 n = 0;
2298                 while (*s)
2299                 {
2300                         t = s;
2301                         while (*s && *s != '\n')
2302                                 s++;
2303                         if (!*s)
2304                                 break;
2305                         *s = 0;
2306                         shadow = true;
2307                         // check for modifier flags
2308                         if (*t == '!')
2309                         {
2310                                 shadow = false;
2311                                 t++;
2312                         }
2313                         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);
2314                         if (a < 9)
2315                                 cubemapname[0] = 0;
2316                         *s = '\n';
2317                         if (a < 8)
2318                         {
2319                                 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);
2320                                 break;
2321                         }
2322                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2323                         radius *= r_editlights_rtlightssizescale.value;
2324                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2325                         s++;
2326                         n++;
2327                 }
2328                 if (*s)
2329                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2330                 Mem_Free(lightsstring);
2331         }
2332 }
2333
2334 void R_Shadow_SaveWorldLights(void)
2335 {
2336         worldlight_t *light;
2337         int bufchars, bufmaxchars;
2338         char *buf, *oldbuf;
2339         char name[MAX_QPATH];
2340         char line[1024];
2341         if (!r_shadow_worldlightchain)
2342                 return;
2343         if (cl.worldmodel == NULL)
2344         {
2345                 Con_Printf("No map loaded.\n");
2346                 return;
2347         }
2348         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2349         strlcat (name, ".rtlights", sizeof (name));
2350         bufchars = bufmaxchars = 0;
2351         buf = NULL;
2352         for (light = r_shadow_worldlightchain;light;light = light->next)
2353         {
2354                 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 : "");
2355                 if (bufchars + (int) strlen(line) > bufmaxchars)
2356                 {
2357                         bufmaxchars = bufchars + strlen(line) + 2048;
2358                         oldbuf = buf;
2359                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2360                         if (oldbuf)
2361                         {
2362                                 if (bufchars)
2363                                         memcpy(buf, oldbuf, bufchars);
2364                                 Mem_Free(oldbuf);
2365                         }
2366                 }
2367                 if (strlen(line))
2368                 {
2369                         memcpy(buf + bufchars, line, strlen(line));
2370                         bufchars += strlen(line);
2371                 }
2372         }
2373         if (bufchars)
2374                 FS_WriteFile(name, buf, bufchars);
2375         if (buf)
2376                 Mem_Free(buf);
2377 }
2378
2379 void R_Shadow_LoadLightsFile(void)
2380 {
2381         int n, a, style;
2382         char name[MAX_QPATH], *lightsstring, *s, *t;
2383         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2384         if (cl.worldmodel == NULL)
2385         {
2386                 Con_Printf("No map loaded.\n");
2387                 return;
2388         }
2389         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2390         strlcat (name, ".lights", sizeof (name));
2391         lightsstring = FS_LoadFile(name, false);
2392         if (lightsstring)
2393         {
2394                 s = lightsstring;
2395                 n = 0;
2396                 while (*s)
2397                 {
2398                         t = s;
2399                         while (*s && *s != '\n')
2400                                 s++;
2401                         if (!*s)
2402                                 break;
2403                         *s = 0;
2404                         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);
2405                         *s = '\n';
2406                         if (a < 14)
2407                         {
2408                                 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);
2409                                 break;
2410                         }
2411                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2412                         radius = bound(15, radius, 4096);
2413                         VectorScale(color, (2.0f / (8388608.0f)), color);
2414                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2415                         s++;
2416                         n++;
2417                 }
2418                 if (*s)
2419                         Con_Printf("invalid lights file \"%s\"\n", name);
2420                 Mem_Free(lightsstring);
2421         }
2422 }
2423
2424 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2425 {
2426         int entnum, style, islight;
2427         char key[256], value[1024];
2428         float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2429         const char *data;
2430
2431         if (cl.worldmodel == NULL)
2432         {
2433                 Con_Printf("No map loaded.\n");
2434                 return;
2435         }
2436         data = cl.worldmodel->brush.entities;
2437         if (!data)
2438                 return;
2439         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2440         {
2441                 light = 0;
2442                 origin[0] = origin[1] = origin[2] = 0;
2443                 originhack[0] = originhack[1] = originhack[2] = 0;
2444                 color[0] = color[1] = color[2] = 1;
2445                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2446                 fadescale = 1;
2447                 lightscale = 1;
2448                 style = 0;
2449                 islight = false;
2450                 while (1)
2451                 {
2452                         if (!COM_ParseToken(&data, false))
2453                                 break; // error
2454                         if (com_token[0] == '}')
2455                                 break; // end of entity
2456                         if (com_token[0] == '_')
2457                                 strcpy(key, com_token + 1);
2458                         else
2459                                 strcpy(key, com_token);
2460                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2461                                 key[strlen(key)-1] = 0;
2462                         if (!COM_ParseToken(&data, false))
2463                                 break; // error
2464                         strcpy(value, com_token);
2465
2466                         // now that we have the key pair worked out...
2467                         if (!strcmp("light", key))
2468                                 light = atof(value);
2469                         else if (!strcmp("origin", key))
2470                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2471                         else if (!strcmp("color", key))
2472                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2473                         else if (!strcmp("wait", key))
2474                                 fadescale = atof(value);
2475                         else if (!strcmp("classname", key))
2476                         {
2477                                 if (!strncmp(value, "light", 5))
2478                                 {
2479                                         islight = true;
2480                                         if (!strcmp(value, "light_fluoro"))
2481                                         {
2482                                                 originhack[0] = 0;
2483                                                 originhack[1] = 0;
2484                                                 originhack[2] = 0;
2485                                                 overridecolor[0] = 1;
2486                                                 overridecolor[1] = 1;
2487                                                 overridecolor[2] = 1;
2488                                         }
2489                                         if (!strcmp(value, "light_fluorospark"))
2490                                         {
2491                                                 originhack[0] = 0;
2492                                                 originhack[1] = 0;
2493                                                 originhack[2] = 0;
2494                                                 overridecolor[0] = 1;
2495                                                 overridecolor[1] = 1;
2496                                                 overridecolor[2] = 1;
2497                                         }
2498                                         if (!strcmp(value, "light_globe"))
2499                                         {
2500                                                 originhack[0] = 0;
2501                                                 originhack[1] = 0;
2502                                                 originhack[2] = 0;
2503                                                 overridecolor[0] = 1;
2504                                                 overridecolor[1] = 0.8;
2505                                                 overridecolor[2] = 0.4;
2506                                         }
2507                                         if (!strcmp(value, "light_flame_large_yellow"))
2508                                         {
2509                                                 originhack[0] = 0;
2510                                                 originhack[1] = 0;
2511                                                 originhack[2] = 48;
2512                                                 overridecolor[0] = 1;
2513                                                 overridecolor[1] = 0.5;
2514                                                 overridecolor[2] = 0.1;
2515                                         }
2516                                         if (!strcmp(value, "light_flame_small_yellow"))
2517                                         {
2518                                                 originhack[0] = 0;
2519                                                 originhack[1] = 0;
2520                                                 originhack[2] = 40;
2521                                                 overridecolor[0] = 1;
2522                                                 overridecolor[1] = 0.5;
2523                                                 overridecolor[2] = 0.1;
2524                                         }
2525                                         if (!strcmp(value, "light_torch_small_white"))
2526                                         {
2527                                                 originhack[0] = 0;
2528                                                 originhack[1] = 0;
2529                                                 originhack[2] = 40;
2530                                                 overridecolor[0] = 1;
2531                                                 overridecolor[1] = 0.5;
2532                                                 overridecolor[2] = 0.1;
2533                                         }
2534                                         if (!strcmp(value, "light_torch_small_walltorch"))
2535                                         {
2536                                                 originhack[0] = 0;
2537                                                 originhack[1] = 0;
2538                                                 originhack[2] = 40;
2539                                                 overridecolor[0] = 1;
2540                                                 overridecolor[1] = 0.5;
2541                                                 overridecolor[2] = 0.1;
2542                                         }
2543                                 }
2544                         }
2545                         else if (!strcmp("style", key))
2546                                 style = atoi(value);
2547                         else if (cl.worldmodel->type == mod_brushq3)
2548                         {
2549                                 if (!strcmp("scale", key))
2550                                         lightscale = atof(value);
2551                                 if (!strcmp("fade", key))
2552                                         fadescale = atof(value);
2553                         }
2554                 }
2555                 if (light <= 0 && islight)
2556                         light = 300;
2557                 if (lightscale <= 0)
2558                         lightscale = 1;
2559                 if (fadescale <= 0)
2560                         fadescale = 1;
2561                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2562                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2563                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2564                         VectorCopy(overridecolor, color);
2565                 VectorScale(color, light, color);
2566                 VectorAdd(origin, originhack, origin);
2567                 if (radius >= 15)
2568                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2569         }
2570 }
2571
2572
2573 void R_Shadow_SetCursorLocationForView(void)
2574 {
2575         vec_t dist, push, frac;
2576         vec3_t dest, endpos, normal;
2577         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2578         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2579         if (frac < 1)
2580         {
2581                 dist = frac * r_editlights_cursordistance.value;
2582                 push = r_editlights_cursorpushback.value;
2583                 if (push > dist)
2584                         push = dist;
2585                 push = -push;
2586                 VectorMA(endpos, push, r_viewforward, endpos);
2587                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2588         }
2589         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2590         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2591         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2592 }
2593
2594 void R_Shadow_UpdateWorldLightSelection(void)
2595 {
2596         if (r_editlights.integer)
2597         {
2598                 R_Shadow_SetCursorLocationForView();
2599                 R_Shadow_SelectLightInView();
2600                 R_Shadow_DrawLightSprites();
2601         }
2602         else
2603                 R_Shadow_SelectLight(NULL);
2604 }
2605
2606 void R_Shadow_EditLights_Clear_f(void)
2607 {
2608         R_Shadow_ClearWorldLights();
2609 }
2610
2611 void R_Shadow_EditLights_Reload_f(void)
2612 {
2613         r_shadow_reloadlights = true;
2614 }
2615
2616 void R_Shadow_EditLights_Save_f(void)
2617 {
2618         if (cl.worldmodel)
2619                 R_Shadow_SaveWorldLights();
2620 }
2621
2622 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2623 {
2624         R_Shadow_ClearWorldLights();
2625         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2626 }
2627
2628 void R_Shadow_EditLights_ImportLightsFile_f(void)
2629 {
2630         R_Shadow_ClearWorldLights();
2631         R_Shadow_LoadLightsFile();
2632 }
2633
2634 void R_Shadow_EditLights_Spawn_f(void)
2635 {
2636         vec3_t color;
2637         if (!r_editlights.integer)
2638         {
2639                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2640                 return;
2641         }
2642         if (Cmd_Argc() != 1)
2643         {
2644                 Con_Printf("r_editlights_spawn does not take parameters\n");
2645                 return;
2646         }
2647         color[0] = color[1] = color[2] = 1;
2648         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2649 }
2650
2651 void R_Shadow_EditLights_Edit_f(void)
2652 {
2653         vec3_t origin, color;
2654         vec_t radius;
2655         int style, shadows;
2656         char cubemapname[1024];
2657         if (!r_editlights.integer)
2658         {
2659                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2660                 return;
2661         }
2662         if (!r_shadow_selectedlight)
2663         {
2664                 Con_Printf("No selected light.\n");
2665                 return;
2666         }
2667         VectorCopy(r_shadow_selectedlight->origin, origin);
2668         radius = r_shadow_selectedlight->lightradius;
2669         VectorCopy(r_shadow_selectedlight->light, color);
2670         style = r_shadow_selectedlight->style;
2671         if (r_shadow_selectedlight->cubemapname)
2672                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2673         else
2674                 cubemapname[0] = 0;
2675         shadows = r_shadow_selectedlight->castshadows;
2676         if (!strcmp(Cmd_Argv(1), "origin"))
2677         {
2678                 if (Cmd_Argc() != 5)
2679                 {
2680                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2681                         return;
2682                 }
2683                 origin[0] = atof(Cmd_Argv(2));
2684                 origin[1] = atof(Cmd_Argv(3));
2685                 origin[2] = atof(Cmd_Argv(4));
2686         }
2687         else if (!strcmp(Cmd_Argv(1), "originx"))
2688         {
2689                 if (Cmd_Argc() != 3)
2690                 {
2691                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2692                         return;
2693                 }
2694                 origin[0] = atof(Cmd_Argv(2));
2695         }
2696         else if (!strcmp(Cmd_Argv(1), "originy"))
2697         {
2698                 if (Cmd_Argc() != 3)
2699                 {
2700                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2701                         return;
2702                 }
2703                 origin[1] = atof(Cmd_Argv(2));
2704         }
2705         else if (!strcmp(Cmd_Argv(1), "originz"))
2706         {
2707                 if (Cmd_Argc() != 3)
2708                 {
2709                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2710                         return;
2711                 }
2712                 origin[2] = atof(Cmd_Argv(2));
2713         }
2714         else if (!strcmp(Cmd_Argv(1), "move"))
2715         {
2716                 if (Cmd_Argc() != 5)
2717                 {
2718                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2719                         return;
2720                 }
2721                 origin[0] += atof(Cmd_Argv(2));
2722                 origin[1] += atof(Cmd_Argv(3));
2723                 origin[2] += atof(Cmd_Argv(4));
2724         }
2725         else if (!strcmp(Cmd_Argv(1), "movex"))
2726         {
2727                 if (Cmd_Argc() != 3)
2728                 {
2729                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2730                         return;
2731                 }
2732                 origin[0] += atof(Cmd_Argv(2));
2733         }
2734         else if (!strcmp(Cmd_Argv(1), "movey"))
2735         {
2736                 if (Cmd_Argc() != 3)
2737                 {
2738                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2739                         return;
2740                 }
2741                 origin[1] += atof(Cmd_Argv(2));
2742         }
2743         else if (!strcmp(Cmd_Argv(1), "movez"))
2744         {
2745                 if (Cmd_Argc() != 3)
2746                 {
2747                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2748                         return;
2749                 }
2750                 origin[2] += atof(Cmd_Argv(2));
2751         }
2752         else if (!strcmp(Cmd_Argv(1), "color"))
2753         {
2754                 if (Cmd_Argc() != 5)
2755                 {
2756                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2757                         return;
2758                 }
2759                 color[0] = atof(Cmd_Argv(2));
2760                 color[1] = atof(Cmd_Argv(3));
2761                 color[2] = atof(Cmd_Argv(4));
2762         }
2763         else if (!strcmp(Cmd_Argv(1), "radius"))
2764         {
2765                 if (Cmd_Argc() != 3)
2766                 {
2767                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2768                         return;
2769                 }
2770                 radius = atof(Cmd_Argv(2));
2771         }
2772         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2773         {
2774                 if (Cmd_Argc() != 3)
2775                 {
2776                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2777                         return;
2778                 }
2779                 style = atoi(Cmd_Argv(2));
2780         }
2781         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2782         {
2783                 if (Cmd_Argc() > 3)
2784                 {
2785                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2786                         return;
2787                 }
2788                 if (Cmd_Argc() == 3)
2789                         strcpy(cubemapname, Cmd_Argv(2));
2790                 else
2791                         cubemapname[0] = 0;
2792         }
2793         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2794         {
2795                 if (Cmd_Argc() != 3)
2796                 {
2797                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2798                         return;
2799                 }
2800                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2801         }
2802         else
2803         {
2804                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2805                 Con_Printf("Selected light's properties:\n");
2806                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2807                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2808                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2809                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2810                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2811                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2812                 return;
2813         }
2814         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2815         r_shadow_selectedlight = NULL;
2816         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2817 }
2818
2819 extern int con_vislines;
2820 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2821 {
2822         float x, y;
2823         char temp[256];
2824         if (r_shadow_selectedlight == NULL)
2825                 return;
2826         x = 0;
2827         y = con_vislines;
2828         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2829         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;
2830         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2831         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;
2832         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2833         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2834         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;
2835 }
2836
2837 void R_Shadow_EditLights_ToggleShadow_f(void)
2838 {
2839         if (!r_editlights.integer)
2840         {
2841                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2842                 return;
2843         }
2844         if (!r_shadow_selectedlight)
2845         {
2846                 Con_Printf("No selected light.\n");
2847                 return;
2848         }
2849         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);
2850         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2851         r_shadow_selectedlight = NULL;
2852 }
2853
2854 void R_Shadow_EditLights_Remove_f(void)
2855 {
2856         if (!r_editlights.integer)
2857         {
2858                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2859                 return;
2860         }
2861         if (!r_shadow_selectedlight)
2862         {
2863                 Con_Printf("No selected light.\n");
2864                 return;
2865         }
2866         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2867         r_shadow_selectedlight = NULL;
2868 }
2869
2870 void R_Shadow_EditLights_Help_f(void)
2871 {
2872         Con_Printf(
2873 "Documentation on r_editlights system:\n"
2874 "Settings:\n"
2875 "r_editlights : enable/disable editing mode\n"
2876 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2877 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2878 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2879 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2880 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2881 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2882 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2883 "Commands:\n"
2884 "r_editlights_help : this help\n"
2885 "r_editlights_clear : remove all lights\n"
2886 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2887 "r_editlights_save : save to .rtlights file\n"
2888 "r_editlights_spawn : create a light with default settings\n"
2889 "r_editlights_edit command : edit selected light - more documentation below\n"
2890 "r_editlights_remove : remove selected light\n"
2891 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2892 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2893 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2894 "Edit commands:\n"
2895 "origin x y z : set light location\n"
2896 "originx x: set x component of light location\n"
2897 "originy y: set y component of light location\n"
2898 "originz z: set z component of light location\n"
2899 "move x y z : adjust light location\n"
2900 "movex x: adjust x component of light location\n"
2901 "movey y: adjust y component of light location\n"
2902 "movez z: adjust z component of light location\n"
2903 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2904 "radius radius : set radius (size) of light\n"
2905 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2906 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2907 "shadows 1/0 : turn on/off shadows\n"
2908 "<nothing> : print light properties to console\n"
2909         );
2910 }
2911
2912 void R_Shadow_EditLights_Init(void)
2913 {
2914         Cvar_RegisterVariable(&r_editlights);
2915         Cvar_RegisterVariable(&r_editlights_cursordistance);
2916         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2917         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2918         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2919         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2920         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2921         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2922         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2923         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2924         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2925         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2926         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2927         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2928         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2929         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2930         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2931         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2932 }