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