fixed GL_Scissor call in rtlight code (apparently I need to feed it a top to bottom...
[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, vid.realheight - iy2, 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 static qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1867
1868 typedef struct cubemapinfo_s
1869 {
1870         char basename[64];
1871         rtexture_t *texture;
1872 }
1873 cubemapinfo_t;
1874
1875 #define MAX_CUBEMAPS 128
1876 static int numcubemaps;
1877 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1878
1879 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1880 typedef struct suffixinfo_s
1881 {
1882         char *suffix;
1883         int flipx, flipy, flipdiagonal;
1884 }
1885 suffixinfo_t;
1886 static suffixinfo_t suffix[3][6] =
1887 {
1888         {
1889                 {"posx", false, false, false},
1890                 {"negx", false, false, false},
1891                 {"posy", false, false, false},
1892                 {"negy", false, false, false},
1893                 {"posz", false, false, false},
1894                 {"negz", false, false, false}
1895         },
1896         {
1897                 {"px", false, false, false},
1898                 {"nx", false, false, false},
1899                 {"py", false, false, false},
1900                 {"ny", false, false, false},
1901                 {"pz", false, false, false},
1902                 {"nz", false, false, false}
1903         },
1904         {
1905                 {"ft", true, false, true},
1906                 {"bk", false, true, true},
1907                 {"lf", true, true, false},
1908                 {"rt", false, false, false},
1909                 {"up", false, false, false},
1910                 {"dn", false, false, false}
1911         }
1912 };
1913
1914 static int componentorder[4] = {0, 1, 2, 3};
1915
1916 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1917 {
1918         int i, j, cubemapsize;
1919         qbyte *cubemappixels, *image_rgba;
1920         rtexture_t *cubemaptexture;
1921         char name[256];
1922         // must start 0 so the first loadimagepixels has no requested width/height
1923         cubemapsize = 0;
1924         cubemappixels = NULL;
1925         cubemaptexture = NULL;
1926         for (j = 0;j < 3 && !cubemappixels;j++)
1927         {
1928                 for (i = 0;i < 6;i++)
1929                 {
1930                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1931                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
1932                         {
1933                                 if (image_width == image_height)
1934                                 {
1935                                         if (!cubemappixels && image_width >= 1)
1936                                         {
1937                                                 cubemapsize = image_width;
1938                                                 // note this clears to black, so unavailable sizes are black
1939                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1940                                         }
1941                                         if (cubemappixels)
1942                                                 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);
1943                                 }
1944                                 else
1945                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1946                                 Mem_Free(image_rgba);
1947                         }
1948                 }
1949         }
1950         if (cubemappixels)
1951         {
1952                 if (!r_shadow_filters_texturepool)
1953                         r_shadow_filters_texturepool = R_AllocTexturePool();
1954                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1955                 Mem_Free(cubemappixels);
1956         }
1957         else
1958         {
1959                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1960                 for (j = 0;j < 3;j++)
1961                         for (i = 0;i < 6;i++)
1962                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1963                 Con_Printf(" and was unable to find any of them.\n");
1964         }
1965         return cubemaptexture;
1966 }
1967
1968 rtexture_t *R_Shadow_Cubemap(const char *basename)
1969 {
1970         int i;
1971         for (i = 0;i < numcubemaps;i++)
1972                 if (!strcasecmp(cubemaps[i].basename, basename))
1973                         return cubemaps[i].texture;
1974         if (i >= MAX_CUBEMAPS)
1975                 return NULL;
1976         numcubemaps++;
1977         strcpy(cubemaps[i].basename, basename);
1978         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1979         return cubemaps[i].texture;
1980 }
1981
1982 void R_Shadow_FreeCubemaps(void)
1983 {
1984         numcubemaps = 0;
1985         R_FreeTexturePool(&r_shadow_filters_texturepool);
1986 }
1987
1988 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)
1989 {
1990         int i, j, k, l, maxverts = 256, tris;
1991         float *vertex3f = NULL, mins[3], maxs[3];
1992         worldlight_t *e;
1993         shadowmesh_t *mesh, *castmesh = NULL;
1994
1995         if (radius < 15 || DotProduct(color, color) < 0.03)
1996         {
1997                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1998                 return;
1999         }
2000
2001         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
2002         VectorCopy(origin, e->origin);
2003         VectorCopy(angles, e->angles);
2004         VectorCopy(color, e->color);
2005         e->radius = radius;
2006         e->style = style;
2007         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
2008         {
2009                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
2010                 e->style = 0;
2011         }
2012         e->drawshadows = shadowenable;
2013         e->corona = corona;
2014
2015         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);
2016         Matrix4x4_Invert_Simple(&e->matrix_worldtolight, &e->matrix_lighttoworld);
2017         Matrix4x4_Concat(&e->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &e->matrix_worldtolight);
2018         Matrix4x4_Concat(&e->matrix_worldtoattenuationz, &matrix_attenuationz, &e->matrix_worldtolight);
2019
2020         e->cullradius = e->radius;
2021         for (k = 0;k < 3;k++)
2022         {
2023                 mins[k] = e->origin[k] - e->radius;
2024                 maxs[k] = e->origin[k] + e->radius;
2025         }
2026
2027         e->next = r_shadow_worldlightchain;
2028         r_shadow_worldlightchain = e;
2029         if (cubemapname && cubemapname[0])
2030         {
2031                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2032                 strcpy(e->cubemapname, cubemapname);
2033                 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2034         }
2035         // FIXME: rewrite this to store ALL geometry into a cache in the light
2036         if (e->drawshadows)
2037                 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2038         e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2039         if (cl.worldmodel)
2040         {
2041                 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightfullpvs, sizeof(lightfullpvs));
2042                 memset(lightpvs, 0, lightpvsbytes);
2043                 if (cl.worldmodel->brushq3.num_leafs)
2044                 {
2045                         q3mleaf_t *leaf;
2046                         q3mface_t *face;
2047
2048                         // make a pvs that only includes things within the box
2049                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2050                                 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2051                                         SETPVSBIT(lightpvs, leaf->clusterindex);
2052
2053                         // make a cluster list for fast visibility checking during rendering
2054                         for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq3.num_pvsclusters;i++)
2055                                 if (CHECKPVSBIT(lightpvs, i))
2056                                         e->numclusters++;
2057                         e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2058                         for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq3.num_pvsclusters;i++)
2059                                 if (CHECKPVSBIT(lightpvs, i))
2060                                         e->clusterindices[e->numclusters++] = i;
2061
2062                         VectorCopy(e->origin, e->mins);
2063                         VectorCopy(e->origin, e->maxs);
2064                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2065                                 face->lighttemp_castshadow = false;
2066                         for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2067                         {
2068                                 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2069                                 {
2070                                         for (k = 0;k < 3;k++)
2071                                         {
2072                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2073                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2074                                         }
2075                                         for (j = 0;j < leaf->numleaffaces;j++)
2076                                         {
2077                                                 face = leaf->firstleafface[j];
2078                                                 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2079                                                         face->lighttemp_castshadow = true;
2080                                         }
2081                                 }
2082                         }
2083
2084                         // add surfaces to shadow casting mesh and light mesh
2085                         for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2086                         {
2087                                 if (face->lighttemp_castshadow)
2088                                 {
2089                                         face->lighttemp_castshadow = false;
2090                                         if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2091                                         {
2092                                                 if (e->drawshadows)
2093                                                         if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2094                                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2095                                                 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2096                                                         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);
2097                                         }
2098                                 }
2099                         }
2100                 }
2101                 else if (cl.worldmodel->brushq1.num_leafs)
2102                 {
2103                         mleaf_t *leaf;
2104                         msurface_t *surf;
2105                         VectorCopy(e->origin, e->mins);
2106                         VectorCopy(e->origin, e->maxs);
2107                         i = CL_PointQ1Contents(e->origin);
2108
2109                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2110                                 surf->lighttemp_castshadow = false;
2111
2112                         if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2113                         {
2114                                 qbyte *byteleafpvs;
2115                                 qbyte *bytesurfacepvs;
2116
2117                                 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
2118                                 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2119
2120                                 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2121
2122                                 // make a pvs that only includes things within the box
2123                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2124                                 {
2125                                         if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2126                                         {
2127                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
2128                                                 for (k = 0;k < 3;k++)
2129                                                 {
2130                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2131                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2132                                                 }
2133                                         }
2134                                 }
2135         
2136                                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2137                                         if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2138                                                 surf->lighttemp_castshadow = true;
2139
2140                                 Mem_Free(byteleafpvs);
2141                                 Mem_Free(bytesurfacepvs);
2142         
2143                                 // make a cluster list for fast visibility checking during rendering
2144                                 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq1.num_pvsclusters;i++)
2145                                         if (CHECKPVSBIT(lightpvs, i))
2146                                                 e->numclusters++;
2147                                 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2148                                 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq1.num_pvsclusters;i++)
2149                                         if (CHECKPVSBIT(lightpvs, i))
2150                                                 e->clusterindices[e->numclusters++] = i;
2151                         }
2152                         else
2153                         {
2154                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2155                                 {
2156                                         if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2157                                         {
2158                                                 // make a pvs that only includes things within the box
2159                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
2160                                                 for (k = 0;k < 3;k++)
2161                                                 {
2162                                                         if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2163                                                         if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2164                                                 }
2165                                                 for (j = 0;j < leaf->nummarksurfaces;j++)
2166                                                 {
2167                                                         surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2168                                                         if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2169                                                                 surf->lighttemp_castshadow = true;
2170                                                 }
2171                                         }
2172                                 }
2173
2174                                 // make a pvs that only includes things within the box
2175                                 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2176                                         if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2177                                                 SETPVSBIT(lightpvs, leaf->clusterindex);
2178
2179                                 // make a cluster list for fast visibility checking during rendering
2180                                 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq1.num_pvsclusters;i++)
2181                                         if (CHECKPVSBIT(lightpvs, i))
2182                                                 e->numclusters++;
2183                                 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2184                                 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brushq1.num_pvsclusters;i++)
2185                                         if (CHECKPVSBIT(lightpvs, i))
2186                                                 e->clusterindices[e->numclusters++] = i;
2187                         }
2188
2189                         // add surfaces to shadow casting mesh and light mesh
2190                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2191                         {
2192                                 if (surf->lighttemp_castshadow)
2193                                 {
2194                                         surf->lighttemp_castshadow = false;
2195                                         if (e->drawshadows && (surf->flags & SURF_SHADOWCAST))
2196                                                 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);
2197                                         if (!(surf->flags & SURF_DRAWSKY))
2198                                                 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);
2199                                 }
2200                         }
2201                 }
2202         }
2203
2204         // limit box to light bounds (in case it grew larger)
2205         for (k = 0;k < 3;k++)
2206         {
2207                 if (e->mins[k] < e->origin[k] - e->radius) e->mins[k] = e->origin[k] - e->radius;
2208                 if (e->maxs[k] > e->origin[k] + e->radius) e->maxs[k] = e->origin[k] + e->radius;
2209         }
2210         e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2211
2212         // cast shadow volume from castmesh
2213         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2214         if (castmesh)
2215         {
2216                 maxverts = 0;
2217                 for (mesh = castmesh;mesh;mesh = mesh->next)
2218                 {
2219                         R_Shadow_ResizeShadowElements(mesh->numtriangles);
2220                         maxverts = max(maxverts, mesh->numverts * 2);
2221                 }
2222
2223                 if (maxverts > 0)
2224                 {
2225                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2226                         // now that we have the buffers big enough, construct and add
2227                         // the shadow volume mesh
2228                         if (e->drawshadows)
2229                                 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2230                         for (mesh = castmesh;mesh;mesh = mesh->next)
2231                         {
2232                                 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2233                                 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)))
2234                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2235                         }
2236                         Mem_Free(vertex3f);
2237                         vertex3f = NULL;
2238                 }
2239                 // we're done with castmesh now
2240                 Mod_ShadowMesh_Free(castmesh);
2241         }
2242
2243         e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2244         e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2245
2246         k = 0;
2247         if (e->meshchain_shadow)
2248                 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2249                         k += mesh->numtriangles;
2250         l = 0;
2251         if (e->meshchain_light)
2252                 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2253                         l += mesh->numtriangles;
2254         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);
2255 }
2256
2257 void R_Shadow_FreeWorldLight(worldlight_t *light)
2258 {
2259         worldlight_t **lightpointer;
2260         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2261         if (*lightpointer != light)
2262                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2263         *lightpointer = light->next;
2264         if (light->cubemapname)
2265                 Mem_Free(light->cubemapname);
2266         if (light->meshchain_shadow)
2267                 Mod_ShadowMesh_Free(light->meshchain_shadow);
2268         if (light->meshchain_light)
2269                 Mod_ShadowMesh_Free(light->meshchain_light);
2270         Mem_Free(light);
2271 }
2272
2273 void R_Shadow_ClearWorldLights(void)
2274 {
2275         while (r_shadow_worldlightchain)
2276                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2277         r_shadow_selectedlight = NULL;
2278         R_Shadow_FreeCubemaps();
2279 }
2280
2281 void R_Shadow_SelectLight(worldlight_t *light)
2282 {
2283         if (r_shadow_selectedlight)
2284                 r_shadow_selectedlight->selected = false;
2285         r_shadow_selectedlight = light;
2286         if (r_shadow_selectedlight)
2287                 r_shadow_selectedlight->selected = true;
2288 }
2289
2290 rtexture_t *lighttextures[5];
2291
2292 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2293 {
2294         float scale = r_editlights_cursorgrid.value * 0.5f;
2295         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);
2296 }
2297
2298 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2299 {
2300         float intensity;
2301         const worldlight_t *light;
2302         light = calldata1;
2303         intensity = 0.5;
2304         if (light->selected)
2305                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2306         if (!light->meshchain_shadow)
2307                 intensity *= 0.5f;
2308         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);
2309 }
2310
2311 void R_Shadow_DrawLightSprites(void)
2312 {
2313         int i;
2314         cachepic_t *pic;
2315         worldlight_t *light;
2316
2317         for (i = 0;i < 5;i++)
2318         {
2319                 lighttextures[i] = NULL;
2320                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2321                         lighttextures[i] = pic->tex;
2322         }
2323
2324         for (light = r_shadow_worldlightchain;light;light = light->next)
2325                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2326         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2327 }
2328
2329 void R_Shadow_SelectLightInView(void)
2330 {
2331         float bestrating, rating, temp[3];
2332         worldlight_t *best, *light;
2333         best = NULL;
2334         bestrating = 0;
2335         for (light = r_shadow_worldlightchain;light;light = light->next)
2336         {
2337                 VectorSubtract(light->origin, r_vieworigin, temp);
2338                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2339                 if (rating >= 0.95)
2340                 {
2341                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2342                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2343                         {
2344                                 bestrating = rating;
2345                                 best = light;
2346                         }
2347                 }
2348         }
2349         R_Shadow_SelectLight(best);
2350 }
2351
2352 void R_Shadow_LoadWorldLights(void)
2353 {
2354         int n, a, style, shadow;
2355         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2356         float origin[3], radius, color[3], angles[3], corona;
2357         if (cl.worldmodel == NULL)
2358         {
2359                 Con_Printf("No map loaded.\n");
2360                 return;
2361         }
2362         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2363         strlcat (name, ".rtlights", sizeof (name));
2364         lightsstring = FS_LoadFile(name, false);
2365         if (lightsstring)
2366         {
2367                 s = lightsstring;
2368                 n = 0;
2369                 while (*s)
2370                 {
2371                         t = s;
2372                         while (*s && *s != '\n')
2373                                 s++;
2374                         if (!*s)
2375                                 break;
2376                         *s = 0;
2377                         shadow = true;
2378                         // check for modifier flags
2379                         if (*t == '!')
2380                         {
2381                                 shadow = false;
2382                                 t++;
2383                         }
2384                         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]);
2385                         if (a < 13)
2386                         {
2387                                 corona = 0;
2388                                 VectorClear(angles);
2389                         }
2390                         if (a < 9)
2391                                 cubemapname[0] = 0;
2392                         *s = '\n';
2393                         if (a < 8)
2394                         {
2395                                 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);
2396                                 break;
2397                         }
2398                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2399                         radius *= r_editlights_rtlightssizescale.value;
2400                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2401                         s++;
2402                         n++;
2403                 }
2404                 if (*s)
2405                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2406                 Mem_Free(lightsstring);
2407         }
2408 }
2409
2410 void R_Shadow_SaveWorldLights(void)
2411 {
2412         worldlight_t *light;
2413         int bufchars, bufmaxchars;
2414         char *buf, *oldbuf;
2415         char name[MAX_QPATH];
2416         char line[1024];
2417         if (!r_shadow_worldlightchain)
2418                 return;
2419         if (cl.worldmodel == NULL)
2420         {
2421                 Con_Printf("No map loaded.\n");
2422                 return;
2423         }
2424         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2425         strlcat (name, ".rtlights", sizeof (name));
2426         bufchars = bufmaxchars = 0;
2427         buf = NULL;
2428         for (light = r_shadow_worldlightchain;light;light = light->next)
2429         {
2430                 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 : "");
2431                 if (bufchars + (int) strlen(line) > bufmaxchars)
2432                 {
2433                         bufmaxchars = bufchars + strlen(line) + 2048;
2434                         oldbuf = buf;
2435                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2436                         if (oldbuf)
2437                         {
2438                                 if (bufchars)
2439                                         memcpy(buf, oldbuf, bufchars);
2440                                 Mem_Free(oldbuf);
2441                         }
2442                 }
2443                 if (strlen(line))
2444                 {
2445                         memcpy(buf + bufchars, line, strlen(line));
2446                         bufchars += strlen(line);
2447                 }
2448         }
2449         if (bufchars)
2450                 FS_WriteFile(name, buf, bufchars);
2451         if (buf)
2452                 Mem_Free(buf);
2453 }
2454
2455 void R_Shadow_LoadLightsFile(void)
2456 {
2457         int n, a, style;
2458         char name[MAX_QPATH], *lightsstring, *s, *t;
2459         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2460         if (cl.worldmodel == NULL)
2461         {
2462                 Con_Printf("No map loaded.\n");
2463                 return;
2464         }
2465         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2466         strlcat (name, ".lights", sizeof (name));
2467         lightsstring = FS_LoadFile(name, false);
2468         if (lightsstring)
2469         {
2470                 s = lightsstring;
2471                 n = 0;
2472                 while (*s)
2473                 {
2474                         t = s;
2475                         while (*s && *s != '\n')
2476                                 s++;
2477                         if (!*s)
2478                                 break;
2479                         *s = 0;
2480                         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);
2481                         *s = '\n';
2482                         if (a < 14)
2483                         {
2484                                 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);
2485                                 break;
2486                         }
2487                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2488                         radius = bound(15, radius, 4096);
2489                         VectorScale(color, (2.0f / (8388608.0f)), color);
2490                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2491                         s++;
2492                         n++;
2493                 }
2494                 if (*s)
2495                         Con_Printf("invalid lights file \"%s\"\n", name);
2496                 Mem_Free(lightsstring);
2497         }
2498 }
2499
2500 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2501 {
2502         int entnum, style, islight;
2503         char key[256], value[1024];
2504         float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2505         const char *data;
2506
2507         if (cl.worldmodel == NULL)
2508         {
2509                 Con_Printf("No map loaded.\n");
2510                 return;
2511         }
2512         data = cl.worldmodel->brush.entities;
2513         if (!data)
2514                 return;
2515         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2516         {
2517                 light = 0;
2518                 origin[0] = origin[1] = origin[2] = 0;
2519                 originhack[0] = originhack[1] = originhack[2] = 0;
2520                 color[0] = color[1] = color[2] = 1;
2521                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2522                 fadescale = 1;
2523                 lightscale = 1;
2524                 style = 0;
2525                 islight = false;
2526                 while (1)
2527                 {
2528                         if (!COM_ParseToken(&data, false))
2529                                 break; // error
2530                         if (com_token[0] == '}')
2531                                 break; // end of entity
2532                         if (com_token[0] == '_')
2533                                 strcpy(key, com_token + 1);
2534                         else
2535                                 strcpy(key, com_token);
2536                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2537                                 key[strlen(key)-1] = 0;
2538                         if (!COM_ParseToken(&data, false))
2539                                 break; // error
2540                         strcpy(value, com_token);
2541
2542                         // now that we have the key pair worked out...
2543                         if (!strcmp("light", key))
2544                                 light = atof(value);
2545                         else if (!strcmp("origin", key))
2546                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2547                         else if (!strcmp("color", key))
2548                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2549                         else if (!strcmp("wait", key))
2550                                 fadescale = atof(value);
2551                         else if (!strcmp("classname", key))
2552                         {
2553                                 if (!strncmp(value, "light", 5))
2554                                 {
2555                                         islight = true;
2556                                         if (!strcmp(value, "light_fluoro"))
2557                                         {
2558                                                 originhack[0] = 0;
2559                                                 originhack[1] = 0;
2560                                                 originhack[2] = 0;
2561                                                 overridecolor[0] = 1;
2562                                                 overridecolor[1] = 1;
2563                                                 overridecolor[2] = 1;
2564                                         }
2565                                         if (!strcmp(value, "light_fluorospark"))
2566                                         {
2567                                                 originhack[0] = 0;
2568                                                 originhack[1] = 0;
2569                                                 originhack[2] = 0;
2570                                                 overridecolor[0] = 1;
2571                                                 overridecolor[1] = 1;
2572                                                 overridecolor[2] = 1;
2573                                         }
2574                                         if (!strcmp(value, "light_globe"))
2575                                         {
2576                                                 originhack[0] = 0;
2577                                                 originhack[1] = 0;
2578                                                 originhack[2] = 0;
2579                                                 overridecolor[0] = 1;
2580                                                 overridecolor[1] = 0.8;
2581                                                 overridecolor[2] = 0.4;
2582                                         }
2583                                         if (!strcmp(value, "light_flame_large_yellow"))
2584                                         {
2585                                                 originhack[0] = 0;
2586                                                 originhack[1] = 0;
2587                                                 originhack[2] = 48;
2588                                                 overridecolor[0] = 1;
2589                                                 overridecolor[1] = 0.5;
2590                                                 overridecolor[2] = 0.1;
2591                                         }
2592                                         if (!strcmp(value, "light_flame_small_yellow"))
2593                                         {
2594                                                 originhack[0] = 0;
2595                                                 originhack[1] = 0;
2596                                                 originhack[2] = 40;
2597                                                 overridecolor[0] = 1;
2598                                                 overridecolor[1] = 0.5;
2599                                                 overridecolor[2] = 0.1;
2600                                         }
2601                                         if (!strcmp(value, "light_torch_small_white"))
2602                                         {
2603                                                 originhack[0] = 0;
2604                                                 originhack[1] = 0;
2605                                                 originhack[2] = 40;
2606                                                 overridecolor[0] = 1;
2607                                                 overridecolor[1] = 0.5;
2608                                                 overridecolor[2] = 0.1;
2609                                         }
2610                                         if (!strcmp(value, "light_torch_small_walltorch"))
2611                                         {
2612                                                 originhack[0] = 0;
2613                                                 originhack[1] = 0;
2614                                                 originhack[2] = 40;
2615                                                 overridecolor[0] = 1;
2616                                                 overridecolor[1] = 0.5;
2617                                                 overridecolor[2] = 0.1;
2618                                         }
2619                                 }
2620                         }
2621                         else if (!strcmp("style", key))
2622                                 style = atoi(value);
2623                         else if (cl.worldmodel->type == mod_brushq3)
2624                         {
2625                                 if (!strcmp("scale", key))
2626                                         lightscale = atof(value);
2627                                 if (!strcmp("fade", key))
2628                                         fadescale = atof(value);
2629                         }
2630                 }
2631                 if (light <= 0 && islight)
2632                         light = 300;
2633                 if (lightscale <= 0)
2634                         lightscale = 1;
2635                 if (fadescale <= 0)
2636                         fadescale = 1;
2637                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2638                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2639                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2640                         VectorCopy(overridecolor, color);
2641                 VectorScale(color, light, color);
2642                 VectorAdd(origin, originhack, origin);
2643                 if (radius >= 15)
2644                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2645         }
2646 }
2647
2648
2649 void R_Shadow_SetCursorLocationForView(void)
2650 {
2651         vec_t dist, push, frac;
2652         vec3_t dest, endpos, normal;
2653         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2654         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2655         if (frac < 1)
2656         {
2657                 dist = frac * r_editlights_cursordistance.value;
2658                 push = r_editlights_cursorpushback.value;
2659                 if (push > dist)
2660                         push = dist;
2661                 push = -push;
2662                 VectorMA(endpos, push, r_viewforward, endpos);
2663                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2664         }
2665         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2666         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2667         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2668 }
2669
2670 void R_Shadow_UpdateWorldLightSelection(void)
2671 {
2672         if (r_editlights.integer)
2673         {
2674                 R_Shadow_SetCursorLocationForView();
2675                 R_Shadow_SelectLightInView();
2676                 R_Shadow_DrawLightSprites();
2677         }
2678         else
2679                 R_Shadow_SelectLight(NULL);
2680 }
2681
2682 void R_Shadow_EditLights_Clear_f(void)
2683 {
2684         R_Shadow_ClearWorldLights();
2685 }
2686
2687 void R_Shadow_EditLights_Reload_f(void)
2688 {
2689         r_shadow_reloadlights = true;
2690 }
2691
2692 void R_Shadow_EditLights_Save_f(void)
2693 {
2694         if (cl.worldmodel)
2695                 R_Shadow_SaveWorldLights();
2696 }
2697
2698 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2699 {
2700         R_Shadow_ClearWorldLights();
2701         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2702 }
2703
2704 void R_Shadow_EditLights_ImportLightsFile_f(void)
2705 {
2706         R_Shadow_ClearWorldLights();
2707         R_Shadow_LoadLightsFile();
2708 }
2709
2710 void R_Shadow_EditLights_Spawn_f(void)
2711 {
2712         vec3_t color;
2713         if (!r_editlights.integer)
2714         {
2715                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2716                 return;
2717         }
2718         if (Cmd_Argc() != 1)
2719         {
2720                 Con_Printf("r_editlights_spawn does not take parameters\n");
2721                 return;
2722         }
2723         color[0] = color[1] = color[2] = 1;
2724         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2725 }
2726
2727 void R_Shadow_EditLights_Edit_f(void)
2728 {
2729         vec3_t origin, angles, color;
2730         vec_t radius, corona;
2731         int style, shadows;
2732         char cubemapname[1024];
2733         if (!r_editlights.integer)
2734         {
2735                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2736                 return;
2737         }
2738         if (!r_shadow_selectedlight)
2739         {
2740                 Con_Printf("No selected light.\n");
2741                 return;
2742         }
2743         VectorCopy(r_shadow_selectedlight->origin, origin);
2744         VectorCopy(r_shadow_selectedlight->angles, angles);
2745         VectorCopy(r_shadow_selectedlight->color, color);
2746         radius = r_shadow_selectedlight->radius;
2747         style = r_shadow_selectedlight->style;
2748         if (r_shadow_selectedlight->cubemapname)
2749                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2750         else
2751                 cubemapname[0] = 0;
2752         shadows = r_shadow_selectedlight->drawshadows;
2753         corona = r_shadow_selectedlight->corona;
2754         if (!strcmp(Cmd_Argv(1), "origin"))
2755         {
2756                 if (Cmd_Argc() != 5)
2757                 {
2758                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2759                         return;
2760                 }
2761                 origin[0] = atof(Cmd_Argv(2));
2762                 origin[1] = atof(Cmd_Argv(3));
2763                 origin[2] = atof(Cmd_Argv(4));
2764         }
2765         else if (!strcmp(Cmd_Argv(1), "originx"))
2766         {
2767                 if (Cmd_Argc() != 3)
2768                 {
2769                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2770                         return;
2771                 }
2772                 origin[0] = atof(Cmd_Argv(2));
2773         }
2774         else if (!strcmp(Cmd_Argv(1), "originy"))
2775         {
2776                 if (Cmd_Argc() != 3)
2777                 {
2778                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2779                         return;
2780                 }
2781                 origin[1] = atof(Cmd_Argv(2));
2782         }
2783         else if (!strcmp(Cmd_Argv(1), "originz"))
2784         {
2785                 if (Cmd_Argc() != 3)
2786                 {
2787                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2788                         return;
2789                 }
2790                 origin[2] = atof(Cmd_Argv(2));
2791         }
2792         else if (!strcmp(Cmd_Argv(1), "move"))
2793         {
2794                 if (Cmd_Argc() != 5)
2795                 {
2796                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2797                         return;
2798                 }
2799                 origin[0] += atof(Cmd_Argv(2));
2800                 origin[1] += atof(Cmd_Argv(3));
2801                 origin[2] += atof(Cmd_Argv(4));
2802         }
2803         else if (!strcmp(Cmd_Argv(1), "movex"))
2804         {
2805                 if (Cmd_Argc() != 3)
2806                 {
2807                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2808                         return;
2809                 }
2810                 origin[0] += atof(Cmd_Argv(2));
2811         }
2812         else if (!strcmp(Cmd_Argv(1), "movey"))
2813         {
2814                 if (Cmd_Argc() != 3)
2815                 {
2816                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2817                         return;
2818                 }
2819                 origin[1] += atof(Cmd_Argv(2));
2820         }
2821         else if (!strcmp(Cmd_Argv(1), "movez"))
2822         {
2823                 if (Cmd_Argc() != 3)
2824                 {
2825                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2826                         return;
2827                 }
2828                 origin[2] += atof(Cmd_Argv(2));
2829         }
2830         if (!strcmp(Cmd_Argv(1), "angles"))
2831         {
2832                 if (Cmd_Argc() != 5)
2833                 {
2834                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2835                         return;
2836                 }
2837                 angles[0] = atof(Cmd_Argv(2));
2838                 angles[1] = atof(Cmd_Argv(3));
2839                 angles[2] = atof(Cmd_Argv(4));
2840         }
2841         else if (!strcmp(Cmd_Argv(1), "anglesx"))
2842         {
2843                 if (Cmd_Argc() != 3)
2844                 {
2845                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2846                         return;
2847                 }
2848                 angles[0] = atof(Cmd_Argv(2));
2849         }
2850         else if (!strcmp(Cmd_Argv(1), "anglesy"))
2851         {
2852                 if (Cmd_Argc() != 3)
2853                 {
2854                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2855                         return;
2856                 }
2857                 angles[1] = atof(Cmd_Argv(2));
2858         }
2859         else if (!strcmp(Cmd_Argv(1), "anglesz"))
2860         {
2861                 if (Cmd_Argc() != 3)
2862                 {
2863                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2864                         return;
2865                 }
2866                 angles[2] = atof(Cmd_Argv(2));
2867         }
2868         else if (!strcmp(Cmd_Argv(1), "color"))
2869         {
2870                 if (Cmd_Argc() != 5)
2871                 {
2872                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
2873                         return;
2874                 }
2875                 color[0] = atof(Cmd_Argv(2));
2876                 color[1] = atof(Cmd_Argv(3));
2877                 color[2] = atof(Cmd_Argv(4));
2878         }
2879         else if (!strcmp(Cmd_Argv(1), "radius"))
2880         {
2881                 if (Cmd_Argc() != 3)
2882                 {
2883                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2884                         return;
2885                 }
2886                 radius = atof(Cmd_Argv(2));
2887         }
2888         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2889         {
2890                 if (Cmd_Argc() != 3)
2891                 {
2892                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2893                         return;
2894                 }
2895                 style = atoi(Cmd_Argv(2));
2896         }
2897         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2898         {
2899                 if (Cmd_Argc() > 3)
2900                 {
2901                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2902                         return;
2903                 }
2904                 if (Cmd_Argc() == 3)
2905                         strcpy(cubemapname, Cmd_Argv(2));
2906                 else
2907                         cubemapname[0] = 0;
2908         }
2909         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2910