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