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