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