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