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