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)
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.
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).
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
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).
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
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.
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.
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.
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).
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.
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.
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).
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.
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
119 extern void R_Shadow_EditLights_Init(void);
121 #define SHADOWSTAGE_NONE 0
122 #define SHADOWSTAGE_STENCIL 1
123 #define SHADOWSTAGE_LIGHT 2
124 #define SHADOWSTAGE_ERASESTENCIL 3
126 int r_shadowstage = SHADOWSTAGE_NONE;
127 int r_shadow_reloadlights = false;
129 mempool_t *r_shadow_mempool;
131 int maxshadowelements;
133 int maxtrianglefacinglight;
134 qbyte *trianglefacinglight;
135 int *trianglefacinglightlist;
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;
150 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
151 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
152 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
153 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
154 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
155 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
156 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
157 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
158 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
159 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
160 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
161 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
162 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
163 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
164 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
165 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
166 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
167 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
168 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
169 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
170 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
171 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
172 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
174 int c_rt_lights, c_rt_clears, c_rt_scissored;
175 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
176 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
178 void R_Shadow_ClearWorldLights(void);
179 void R_Shadow_SaveWorldLights(void);
180 void R_Shadow_LoadWorldLights(void);
181 void R_Shadow_LoadLightsFile(void);
182 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
184 void r_shadow_start(void)
186 // allocate vertex processing arrays
187 r_shadow_mempool = Mem_AllocPool("R_Shadow");
188 maxshadowelements = 0;
189 shadowelements = NULL;
194 maxtrianglefacinglight = 0;
195 trianglefacinglight = NULL;
196 trianglefacinglightlist = NULL;
197 r_shadow_normalcubetexture = NULL;
198 r_shadow_attenuation2dtexture = NULL;
199 r_shadow_attenuation3dtexture = NULL;
200 r_shadow_blankbumptexture = NULL;
201 r_shadow_blankglosstexture = NULL;
202 r_shadow_blankwhitetexture = NULL;
203 r_shadow_texturepool = NULL;
204 R_Shadow_ClearWorldLights();
205 r_shadow_reloadlights = true;
208 void r_shadow_shutdown(void)
210 R_Shadow_ClearWorldLights();
211 r_shadow_reloadlights = true;
212 r_shadow_normalcubetexture = NULL;
213 r_shadow_attenuation2dtexture = NULL;
214 r_shadow_attenuation3dtexture = NULL;
215 r_shadow_blankbumptexture = NULL;
216 r_shadow_blankglosstexture = NULL;
217 r_shadow_blankwhitetexture = NULL;
218 R_FreeTexturePool(&r_shadow_texturepool);
219 maxshadowelements = 0;
220 shadowelements = NULL;
225 maxtrianglefacinglight = 0;
226 trianglefacinglight = NULL;
227 trianglefacinglightlist = NULL;
228 Mem_FreePool(&r_shadow_mempool);
231 void r_shadow_newmap(void)
233 R_Shadow_ClearWorldLights();
234 r_shadow_reloadlights = true;
237 void R_Shadow_Help_f(void)
240 "Documentation on r_shadow system:\n"
242 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
243 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
244 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
245 "r_shadow_realtime_world : use realtime world light rendering\n"
246 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
247 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
248 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
249 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
250 "r_shadow_glossintensity : brightness of textured gloss\n"
251 "r_shadow_gloss2intensity : brightness of forced gloss\n"
252 "r_shadow_debuglight : render only this light number (-1 = all)\n"
253 "r_shadow_scissor : use scissor optimization\n"
254 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
255 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
256 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
257 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
258 "r_shadow_portallight : use portal visibility for static light precomputation\n"
259 "r_shadow_projectdistance : shadow volume projection distance\n"
260 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
261 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
262 "r_shadow_worldshadows : enable world shadows\n"
263 "r_shadow_dlightshadows : enable dlight shadows\n"
265 "r_shadow_help : this help\n"
269 void R_Shadow_Init(void)
271 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
272 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
273 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
274 Cvar_RegisterVariable(&r_shadow_realtime_world);
275 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
276 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
277 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
278 Cvar_RegisterVariable(&r_shadow_gloss);
279 Cvar_RegisterVariable(&r_shadow_glossintensity);
280 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
281 Cvar_RegisterVariable(&r_shadow_debuglight);
282 Cvar_RegisterVariable(&r_shadow_scissor);
283 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
284 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
285 Cvar_RegisterVariable(&r_shadow_polygonfactor);
286 Cvar_RegisterVariable(&r_shadow_polygonoffset);
287 Cvar_RegisterVariable(&r_shadow_portallight);
288 Cvar_RegisterVariable(&r_shadow_projectdistance);
289 Cvar_RegisterVariable(&r_shadow_texture3d);
290 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
291 Cvar_RegisterVariable(&r_shadow_worldshadows);
292 Cvar_RegisterVariable(&r_shadow_dlightshadows);
293 Cvar_RegisterVariable(&r_shadow_showtris);
294 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
295 R_Shadow_EditLights_Init();
296 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
299 void R_Shadow_ResizeTriangleFacingLight(int numtris)
301 // make sure trianglefacinglight is big enough for this volume
302 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
303 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
304 if (maxtrianglefacinglight < numtris)
306 maxtrianglefacinglight = numtris;
307 if (trianglefacinglight)
308 Mem_Free(trianglefacinglight);
309 if (trianglefacinglightlist)
310 Mem_Free(trianglefacinglightlist);
311 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
312 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
316 int *R_Shadow_ResizeShadowElements(int numtris)
318 // make sure shadowelements is big enough for this volume
319 if (maxshadowelements < numtris * 24)
321 maxshadowelements = numtris * 24;
323 Mem_Free(shadowelements);
324 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
326 return shadowelements;
330 // readable version of some code found below
331 //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]))))
332 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
334 float dir0[3], dir1[3], normal[3];
336 // calculate two mostly perpendicular edge directions
337 VectorSubtract(a, b, dir0);
338 VectorSubtract(c, b, dir1);
340 // we have two edge directions, we can calculate a third vector from
341 // them, which is the direction of the surface normal (it's magnitude
343 CrossProduct(dir0, dir1, normal);
345 // compare distance of light along normal, with distance of any point
346 // of the triangle along the same normal (the triangle is planar,
347 // I.E. flat, so all points give the same answer)
348 return DotProduct(p, normal) > DotProduct(a, normal);
350 int checkcastshadowfromedge(int t, int i)
354 if (t >= trianglerange_start && t < trianglerange_end)
356 if (t < i && !trianglefacinglight[t])
367 te = inelement3i + t * 3;
368 v[0] = invertex3f + te[0] * 3;
369 v[1] = invertex3f + te[1] * 3;
370 v[2] = invertex3f + te[2] * 3;
371 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
380 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)
382 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
384 const int *e, *n, *te;
387 // make sure trianglefacinglight is big enough for this volume
388 if (maxtrianglefacinglight < trianglerange_end)
389 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
391 if (maxvertexupdate < innumvertices)
393 maxvertexupdate = innumvertices;
395 Mem_Free(vertexupdate);
397 Mem_Free(vertexremap);
398 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
399 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
403 if (r_shadow_singlepassvolumegeneration.integer)
405 // one pass approach (identify lit/dark faces and generate sides while doing so)
406 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
408 // calculate triangle facing flag
409 v[0] = invertex3f + e[0] * 3;
410 v[1] = invertex3f + e[1] * 3;
411 v[2] = invertex3f + e[2] * 3;
412 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
414 // make sure the vertices are created
415 for (j = 0;j < 3;j++)
417 if (vertexupdate[e[j]] != vertexupdatenum)
419 vertexupdate[e[j]] = vertexupdatenum;
420 vertexremap[e[j]] = outvertices;
421 VectorCopy(v[j], outvertex3f);
422 VectorSubtract(v[j], relativelightorigin, temp);
423 f = projectdistance / VectorLength(temp);
424 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
429 // output the front and back triangles
430 vr[0] = vertexremap[e[0]];
431 vr[1] = vertexremap[e[1]];
432 vr[2] = vertexremap[e[2]];
433 outelement3i[0] = vr[0];
434 outelement3i[1] = vr[1];
435 outelement3i[2] = vr[2];
436 outelement3i[3] = vr[2] + 1;
437 outelement3i[4] = vr[1] + 1;
438 outelement3i[5] = vr[0] + 1;
441 // output the sides (facing outward from this triangle)
443 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]))))
445 outelement3i[0] = vr[1];
446 outelement3i[1] = vr[0];
447 outelement3i[2] = vr[0] + 1;
448 outelement3i[3] = vr[1];
449 outelement3i[4] = vr[0] + 1;
450 outelement3i[5] = vr[1] + 1;
455 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]))))
457 outelement3i[0] = vr[2];
458 outelement3i[1] = vr[1];
459 outelement3i[2] = vr[1] + 1;
460 outelement3i[3] = vr[2];
461 outelement3i[4] = vr[1] + 1;
462 outelement3i[5] = vr[2] + 1;
467 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]))))
469 outelement3i[0] = vr[0];
470 outelement3i[1] = vr[2];
471 outelement3i[2] = vr[2] + 1;
472 outelement3i[3] = vr[0];
473 outelement3i[4] = vr[2] + 1;
474 outelement3i[5] = vr[0] + 1;
481 // this triangle is not facing the light
482 // output the sides (facing inward to this triangle)
484 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
486 vr[0] = vertexremap[e[0]];
487 vr[1] = vertexremap[e[1]];
488 outelement3i[0] = vr[1];
489 outelement3i[1] = vr[0] + 1;
490 outelement3i[2] = vr[0];
491 outelement3i[3] = vr[1];
492 outelement3i[4] = vr[1] + 1;
493 outelement3i[5] = vr[0] + 1;
498 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
500 vr[1] = vertexremap[e[1]];
501 vr[2] = vertexremap[e[2]];
502 outelement3i[0] = vr[2];
503 outelement3i[1] = vr[1] + 1;
504 outelement3i[2] = vr[1];
505 outelement3i[3] = vr[2];
506 outelement3i[4] = vr[2] + 1;
507 outelement3i[5] = vr[1] + 1;
512 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
514 vr[0] = vertexremap[e[0]];
515 vr[2] = vertexremap[e[2]];
516 outelement3i[0] = vr[0];
517 outelement3i[1] = vr[2] + 1;
518 outelement3i[2] = vr[2];
519 outelement3i[3] = vr[0];
520 outelement3i[4] = vr[0] + 1;
521 outelement3i[5] = vr[2] + 1;
530 // two pass approach (identify lit/dark faces and then generate sides)
531 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
533 // calculate triangle facing flag
534 v[0] = invertex3f + e[0] * 3;
535 v[1] = invertex3f + e[1] * 3;
536 v[2] = invertex3f + e[2] * 3;
537 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
539 trianglefacinglightlist[numfacing++] = i;
540 // make sure the vertices are created
541 for (j = 0;j < 3;j++)
543 if (vertexupdate[e[j]] != vertexupdatenum)
545 vertexupdate[e[j]] = vertexupdatenum;
546 vertexremap[e[j]] = outvertices;
547 VectorSubtract(v[j], relativelightorigin, temp);
548 f = projectdistance / VectorLength(temp);
549 VectorCopy(v[j], outvertex3f);
550 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
555 // output the front and back triangles
556 outelement3i[0] = vertexremap[e[0]];
557 outelement3i[1] = vertexremap[e[1]];
558 outelement3i[2] = vertexremap[e[2]];
559 outelement3i[3] = vertexremap[e[2]] + 1;
560 outelement3i[4] = vertexremap[e[1]] + 1;
561 outelement3i[5] = vertexremap[e[0]] + 1;
566 for (i = 0;i < numfacing;i++)
568 t = trianglefacinglightlist[i];
569 e = inelement3i + t * 3;
570 n = inneighbor3i + t * 3;
571 // output the sides (facing outward from this triangle)
573 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]))))
575 vr[0] = vertexremap[e[0]];
576 vr[1] = vertexremap[e[1]];
577 outelement3i[0] = vr[1];
578 outelement3i[1] = vr[0];
579 outelement3i[2] = vr[0] + 1;
580 outelement3i[3] = vr[1];
581 outelement3i[4] = vr[0] + 1;
582 outelement3i[5] = vr[1] + 1;
587 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]))))
589 vr[1] = vertexremap[e[1]];
590 vr[2] = vertexremap[e[2]];
591 outelement3i[0] = vr[2];
592 outelement3i[1] = vr[1];
593 outelement3i[2] = vr[1] + 1;
594 outelement3i[3] = vr[2];
595 outelement3i[4] = vr[1] + 1;
596 outelement3i[5] = vr[2] + 1;
601 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]))))
603 vr[0] = vertexremap[e[0]];
604 vr[2] = vertexremap[e[2]];
605 outelement3i[0] = vr[0];
606 outelement3i[1] = vr[2];
607 outelement3i[2] = vr[2] + 1;
608 outelement3i[3] = vr[0];
609 outelement3i[4] = vr[2] + 1;
610 outelement3i[5] = vr[0] + 1;
617 *outnumvertices = outvertices;
621 float varray_vertex3f2[65536*3];
623 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
626 if (projectdistance < 0.1)
628 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
634 // make sure shadowelements is big enough for this volume
635 if (maxshadowelements < numtris * 24)
636 R_Shadow_ResizeShadowElements(numtris);
638 // check which triangles are facing the light, and then output
639 // triangle elements and vertices... by clever use of elements we
640 // can construct the whole shadow from the unprojected vertices and
641 // the projected vertices
642 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
644 GL_VertexPointer(varray_vertex3f2);
645 if (r_shadowstage == SHADOWSTAGE_STENCIL)
647 // decrement stencil if frontface is behind depthbuffer
648 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
649 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
650 R_Mesh_Draw(outverts, tris, shadowelements);
652 c_rt_shadowtris += numtris;
653 // increment stencil if backface is behind depthbuffer
654 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
655 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
657 R_Mesh_Draw(outverts, tris, shadowelements);
659 c_rt_shadowtris += numtris;
663 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
666 if (r_shadowstage == SHADOWSTAGE_STENCIL)
668 // decrement stencil if frontface is behind depthbuffer
669 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
670 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
671 for (mesh = firstmesh;mesh;mesh = mesh->next)
673 GL_VertexPointer(mesh->vertex3f);
674 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
675 c_rtcached_shadowmeshes++;
676 c_rtcached_shadowtris += mesh->numtriangles;
678 // increment stencil if backface is behind depthbuffer
679 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
680 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
682 for (mesh = firstmesh;mesh;mesh = mesh->next)
684 GL_VertexPointer(mesh->vertex3f);
685 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
686 c_rtcached_shadowmeshes++;
687 c_rtcached_shadowtris += mesh->numtriangles;
691 float r_shadow_attenpower, r_shadow_attenscale;
692 static void R_Shadow_MakeTextures(void)
694 int x, y, z, d, side;
695 float v[3], s, t, intensity;
697 R_FreeTexturePool(&r_shadow_texturepool);
698 r_shadow_texturepool = R_AllocTexturePool();
699 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
700 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
702 #define ATTEN2DSIZE 64
703 #define ATTEN3DSIZE 32
704 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
709 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
714 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
719 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
720 if (gl_texturecubemap)
722 for (side = 0;side < 6;side++)
724 for (y = 0;y < NORMSIZE;y++)
726 for (x = 0;x < NORMSIZE;x++)
728 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
729 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
763 intensity = 127.0f / sqrt(DotProduct(v, v));
764 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
765 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
766 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
767 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
771 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
774 r_shadow_normalcubetexture = NULL;
775 for (y = 0;y < ATTEN2DSIZE;y++)
777 for (x = 0;x < ATTEN2DSIZE;x++)
779 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
780 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
782 intensity = 1.0f - sqrt(DotProduct(v, v));
784 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
785 d = bound(0, intensity, 255);
786 data[(y*ATTEN2DSIZE+x)*4+0] = d;
787 data[(y*ATTEN2DSIZE+x)*4+1] = d;
788 data[(y*ATTEN2DSIZE+x)*4+2] = d;
789 data[(y*ATTEN2DSIZE+x)*4+3] = d;
792 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
793 if (r_shadow_texture3d.integer)
795 for (z = 0;z < ATTEN3DSIZE;z++)
797 for (y = 0;y < ATTEN3DSIZE;y++)
799 for (x = 0;x < ATTEN3DSIZE;x++)
801 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
802 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
803 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
804 intensity = 1.0f - sqrt(DotProduct(v, v));
806 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
807 d = bound(0, intensity, 255);
808 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
809 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
810 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
811 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
815 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
820 void R_Shadow_Stage_Begin(void)
824 if (r_shadow_texture3d.integer && !gl_texture3d)
825 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
827 if (!r_shadow_attenuation2dtexture
828 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
829 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
830 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
831 R_Shadow_MakeTextures();
833 memset(&m, 0, sizeof(m));
834 GL_BlendFunc(GL_ONE, GL_ZERO);
837 R_Mesh_State_Texture(&m);
838 GL_Color(0, 0, 0, 1);
839 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
840 qglDisable(GL_SCISSOR_TEST);
841 r_shadowstage = SHADOWSTAGE_NONE;
843 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
844 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
845 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
848 void R_Shadow_LoadWorldLightsIfNeeded(void)
850 if (r_shadow_reloadlights && cl.worldmodel)
852 R_Shadow_ClearWorldLights();
853 r_shadow_reloadlights = false;
854 R_Shadow_LoadWorldLights();
855 if (r_shadow_worldlightchain == NULL)
857 R_Shadow_LoadLightsFile();
858 if (r_shadow_worldlightchain == NULL)
859 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
864 void R_Shadow_Stage_ShadowVolumes(void)
867 memset(&m, 0, sizeof(m));
868 R_Mesh_State_Texture(&m);
869 GL_Color(1, 1, 1, 1);
870 qglColorMask(0, 0, 0, 0);
871 GL_BlendFunc(GL_ONE, GL_ZERO);
874 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
875 //if (r_shadow_polygonoffset.value != 0)
877 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
878 // qglEnable(GL_POLYGON_OFFSET_FILL);
881 // qglDisable(GL_POLYGON_OFFSET_FILL);
882 qglDepthFunc(GL_LESS);
883 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
884 qglEnable(GL_STENCIL_TEST);
885 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
886 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
887 r_shadowstage = SHADOWSTAGE_STENCIL;
888 qglClear(GL_STENCIL_BUFFER_BIT);
890 // LordHavoc note: many shadow volumes reside entirely inside the world
891 // (that is to say they are entirely bounded by their lit surfaces),
892 // which can be optimized by handling things as an inverted light volume,
893 // with the shadow boundaries of the world being simulated by an altered
894 // (129) bias to stencil clearing on such lights
895 // FIXME: generate inverted light volumes for use as shadow volumes and
896 // optimize for them as noted above
899 void R_Shadow_Stage_LightWithoutShadows(void)
902 memset(&m, 0, sizeof(m));
903 R_Mesh_State_Texture(&m);
904 GL_BlendFunc(GL_ONE, GL_ONE);
907 qglPolygonOffset(0, 0);
908 //qglDisable(GL_POLYGON_OFFSET_FILL);
909 GL_Color(1, 1, 1, 1);
910 qglColorMask(1, 1, 1, 1);
911 qglDepthFunc(GL_EQUAL);
912 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
913 qglDisable(GL_STENCIL_TEST);
914 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
915 qglStencilFunc(GL_EQUAL, 128, 0xFF);
916 r_shadowstage = SHADOWSTAGE_LIGHT;
920 void R_Shadow_Stage_LightWithShadows(void)
923 memset(&m, 0, sizeof(m));
924 R_Mesh_State_Texture(&m);
925 GL_BlendFunc(GL_ONE, GL_ONE);
928 qglPolygonOffset(0, 0);
929 //qglDisable(GL_POLYGON_OFFSET_FILL);
930 GL_Color(1, 1, 1, 1);
931 qglColorMask(1, 1, 1, 1);
932 qglDepthFunc(GL_EQUAL);
933 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
934 qglEnable(GL_STENCIL_TEST);
935 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
936 // only draw light where this geometry was already rendered AND the
937 // stencil is 128 (values other than this mean shadow)
938 qglStencilFunc(GL_EQUAL, 128, 0xFF);
939 r_shadowstage = SHADOWSTAGE_LIGHT;
943 void R_Shadow_Stage_End(void)
946 memset(&m, 0, sizeof(m));
947 R_Mesh_State_Texture(&m);
948 GL_BlendFunc(GL_ONE, GL_ZERO);
951 qglPolygonOffset(0, 0);
952 //qglDisable(GL_POLYGON_OFFSET_FILL);
953 GL_Color(1, 1, 1, 1);
954 qglColorMask(1, 1, 1, 1);
955 qglDisable(GL_SCISSOR_TEST);
956 qglDepthFunc(GL_LEQUAL);
957 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
958 qglDisable(GL_STENCIL_TEST);
959 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
960 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
961 r_shadowstage = SHADOWSTAGE_NONE;
964 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
966 int i, ix1, iy1, ix2, iy2;
967 float x1, y1, x2, y2, x, y, f;
970 if (!r_shadow_scissor.integer)
972 // if view is inside the box, just say yes it's visible
973 // LordHavoc: for some odd reason scissor seems broken without stencil
974 // (?!? seems like a driver bug) so abort if gl_stencil is false
975 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
977 qglDisable(GL_SCISSOR_TEST);
980 for (i = 0;i < 3;i++)
982 if (r_viewforward[i] >= 0)
993 f = DotProduct(r_viewforward, r_vieworigin) + 1;
994 if (DotProduct(r_viewforward, v2) <= f)
996 // entirely behind nearclip plane
999 if (DotProduct(r_viewforward, v) >= f)
1001 // entirely infront of nearclip plane
1002 x1 = y1 = x2 = y2 = 0;
1003 for (i = 0;i < 8;i++)
1005 v[0] = (i & 1) ? mins[0] : maxs[0];
1006 v[1] = (i & 2) ? mins[1] : maxs[1];
1007 v[2] = (i & 4) ? mins[2] : maxs[2];
1009 GL_TransformToScreen(v, v2);
1010 //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]);
1029 // clipped by nearclip plane
1030 // this is nasty and crude...
1031 // create viewspace bbox
1032 for (i = 0;i < 8;i++)
1034 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1035 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1036 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1037 v2[0] = -DotProduct(v, r_viewleft);
1038 v2[1] = DotProduct(v, r_viewup);
1039 v2[2] = DotProduct(v, r_viewforward);
1042 if (smins[0] > v2[0]) smins[0] = v2[0];
1043 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1044 if (smins[1] > v2[1]) smins[1] = v2[1];
1045 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1046 if (smins[2] > v2[2]) smins[2] = v2[2];
1047 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1051 smins[0] = smaxs[0] = v2[0];
1052 smins[1] = smaxs[1] = v2[1];
1053 smins[2] = smaxs[2] = v2[2];
1056 // now we have a bbox in viewspace
1057 // clip it to the view plane
1060 // return true if that culled the box
1061 if (smins[2] >= smaxs[2])
1063 // ok some of it is infront of the view, transform each corner back to
1064 // worldspace and then to screenspace and make screen rect
1065 // initialize these variables just to avoid compiler warnings
1066 x1 = y1 = x2 = y2 = 0;
1067 for (i = 0;i < 8;i++)
1069 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1070 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1071 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1072 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1073 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1074 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1076 GL_TransformToScreen(v, v2);
1077 //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]);
1094 // this code doesn't handle boxes with any points behind view properly
1095 x1 = 1000;x2 = -1000;
1096 y1 = 1000;y2 = -1000;
1097 for (i = 0;i < 8;i++)
1099 v[0] = (i & 1) ? mins[0] : maxs[0];
1100 v[1] = (i & 2) ? mins[1] : maxs[1];
1101 v[2] = (i & 4) ? mins[2] : maxs[2];
1103 GL_TransformToScreen(v, v2);
1104 //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]);
1122 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1123 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1124 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1125 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1126 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1127 if (ix2 <= ix1 || iy2 <= iy1)
1129 // set up the scissor rectangle
1130 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1131 qglEnable(GL_SCISSOR_TEST);
1136 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1138 float *color4f = varray_color4f;
1139 float dist, dot, intensity, v[3], n[3];
1140 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1142 Matrix4x4_Transform(m, vertex3f, v);
1143 if ((dist = DotProduct(v, v)) < 1)
1145 Matrix4x4_Transform3x3(m, normal3f, n);
1146 if ((dot = DotProduct(n, v)) > 0)
1149 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1150 VectorScale(lightcolor, intensity, color4f);
1155 VectorClear(color4f);
1161 VectorClear(color4f);
1167 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1169 float *color4f = varray_color4f;
1170 float dist, dot, intensity, v[3], n[3];
1171 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1173 Matrix4x4_Transform(m, vertex3f, v);
1174 if ((dist = fabs(v[2])) < 1)
1176 Matrix4x4_Transform3x3(m, normal3f, n);
1177 if ((dot = DotProduct(n, v)) > 0)
1179 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1180 VectorScale(lightcolor, intensity, color4f);
1185 VectorClear(color4f);
1191 VectorClear(color4f);
1197 // FIXME: this should be done in a vertex program when possible
1198 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1199 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1203 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1204 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1205 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1212 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1216 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1217 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1224 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)
1228 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1230 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1231 // the cubemap normalizes this for us
1232 out3f[0] = DotProduct(svector3f, lightdir);
1233 out3f[1] = DotProduct(tvector3f, lightdir);
1234 out3f[2] = DotProduct(normal3f, lightdir);
1238 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)
1241 float lightdir[3], eyedir[3], halfdir[3];
1242 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1244 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1245 VectorNormalizeFast(lightdir);
1246 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1247 VectorNormalizeFast(eyedir);
1248 VectorAdd(lightdir, eyedir, halfdir);
1249 // the cubemap normalizes this for us
1250 out3f[0] = DotProduct(svector3f, halfdir);
1251 out3f[1] = DotProduct(tvector3f, halfdir);
1252 out3f[2] = DotProduct(normal3f, halfdir);
1256 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)
1259 float color[3], color2[3];
1261 GL_VertexPointer(vertex3f);
1262 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1265 bumptexture = r_shadow_blankbumptexture;
1267 // colorscale accounts for how much we multiply the brightness during combine
1268 // mult is how many times the final pass of the lighting will be
1269 // performed to get more brightness than otherwise possible
1270 // limit mult to 64 for sanity sake
1271 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1273 // 3/2 3D combine path (Geforce3, Radeon 8500)
1274 memset(&m, 0, sizeof(m));
1275 m.tex[0] = R_GetTexture(bumptexture);
1276 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1277 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1278 m.texcombinergb[0] = GL_REPLACE;
1279 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1280 m.pointer_texcoord[0] = texcoord2f;
1281 m.pointer_texcoord[1] = varray_texcoord3f[1];
1282 m.pointer_texcoord[2] = varray_texcoord3f[2];
1283 R_Mesh_State_Texture(&m);
1284 qglColorMask(0,0,0,1);
1285 GL_BlendFunc(GL_ONE, GL_ZERO);
1286 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1287 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1288 R_Mesh_Draw(numverts, numtriangles, elements);
1290 c_rt_lighttris += numtriangles;
1292 memset(&m, 0, sizeof(m));
1293 m.tex[0] = R_GetTexture(basetexture);
1294 m.texcubemap[1] = R_GetTexture(lightcubemap);
1295 m.pointer_texcoord[0] = texcoord2f;
1296 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1297 R_Mesh_State_Texture(&m);
1298 qglColorMask(1,1,1,0);
1299 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1301 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1302 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1303 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1305 color[0] = bound(0, color2[0], 1);
1306 color[1] = bound(0, color2[1], 1);
1307 color[2] = bound(0, color2[2], 1);
1308 GL_Color(color[0], color[1], color[2], 1);
1309 R_Mesh_Draw(numverts, numtriangles, elements);
1311 c_rt_lighttris += numtriangles;
1314 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1316 // 1/2/2 3D combine path (original Radeon)
1317 memset(&m, 0, sizeof(m));
1318 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1319 m.pointer_texcoord[0] = varray_texcoord3f[0];
1320 R_Mesh_State_Texture(&m);
1321 qglColorMask(0,0,0,1);
1322 GL_BlendFunc(GL_ONE, GL_ZERO);
1323 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1324 R_Mesh_Draw(numverts, numtriangles, elements);
1326 c_rt_lighttris += numtriangles;
1328 memset(&m, 0, sizeof(m));
1329 m.tex[0] = R_GetTexture(bumptexture);
1330 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1331 m.texcombinergb[0] = GL_REPLACE;
1332 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1333 m.pointer_texcoord[0] = texcoord2f;
1334 m.pointer_texcoord[1] = varray_texcoord3f[1];
1335 R_Mesh_State_Texture(&m);
1336 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1337 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1338 R_Mesh_Draw(numverts, numtriangles, elements);
1340 c_rt_lighttris += numtriangles;
1342 memset(&m, 0, sizeof(m));
1343 m.tex[0] = R_GetTexture(basetexture);
1344 m.texcubemap[1] = R_GetTexture(lightcubemap);
1345 m.pointer_texcoord[0] = texcoord2f;
1346 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1347 R_Mesh_State_Texture(&m);
1348 qglColorMask(1,1,1,0);
1349 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1351 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1352 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1353 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1355 color[0] = bound(0, color2[0], 1);
1356 color[1] = bound(0, color2[1], 1);
1357 color[2] = bound(0, color2[2], 1);
1358 GL_Color(color[0], color[1], color[2], 1);
1359 R_Mesh_Draw(numverts, numtriangles, elements);
1361 c_rt_lighttris += numtriangles;
1364 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1366 // 2/2 3D combine path (original Radeon)
1367 memset(&m, 0, sizeof(m));
1368 m.tex[0] = R_GetTexture(bumptexture);
1369 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1370 m.texcombinergb[0] = GL_REPLACE;
1371 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1372 m.pointer_texcoord[0] = texcoord2f;
1373 m.pointer_texcoord[1] = varray_texcoord3f[1];
1374 R_Mesh_State_Texture(&m);
1375 qglColorMask(0,0,0,1);
1376 GL_BlendFunc(GL_ONE, GL_ZERO);
1377 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1378 R_Mesh_Draw(numverts, numtriangles, elements);
1380 c_rt_lighttris += numtriangles;
1382 memset(&m, 0, sizeof(m));
1383 m.tex[0] = R_GetTexture(basetexture);
1384 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1385 m.pointer_texcoord[0] = texcoord2f;
1386 m.pointer_texcoord[1] = varray_texcoord3f[1];
1387 R_Mesh_State_Texture(&m);
1388 qglColorMask(1,1,1,0);
1389 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1390 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1391 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1392 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1394 color[0] = bound(0, color2[0], 1);
1395 color[1] = bound(0, color2[1], 1);
1396 color[2] = bound(0, color2[2], 1);
1397 GL_Color(color[0], color[1], color[2], 1);
1398 R_Mesh_Draw(numverts, numtriangles, elements);
1400 c_rt_lighttris += numtriangles;
1403 else if (r_textureunits.integer >= 4)
1405 // 4/2 2D combine path (Geforce3, Radeon 8500)
1406 memset(&m, 0, sizeof(m));
1407 m.tex[0] = R_GetTexture(bumptexture);
1408 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1409 m.texcombinergb[0] = GL_REPLACE;
1410 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1411 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1412 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1413 m.pointer_texcoord[0] = texcoord2f;
1414 m.pointer_texcoord[1] = varray_texcoord3f[1];
1415 m.pointer_texcoord[2] = varray_texcoord2f[2];
1416 m.pointer_texcoord[3] = varray_texcoord2f[3];
1417 R_Mesh_State_Texture(&m);
1418 qglColorMask(0,0,0,1);
1419 GL_BlendFunc(GL_ONE, GL_ZERO);
1420 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1421 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1422 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1423 R_Mesh_Draw(numverts, numtriangles, elements);
1425 c_rt_lighttris += numtriangles;
1427 memset(&m, 0, sizeof(m));
1428 m.tex[0] = R_GetTexture(basetexture);
1429 m.texcubemap[1] = R_GetTexture(lightcubemap);
1430 m.pointer_texcoord[0] = texcoord2f;
1431 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1432 R_Mesh_State_Texture(&m);
1433 qglColorMask(1,1,1,0);
1434 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1436 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1437 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1438 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1440 color[0] = bound(0, color2[0], 1);
1441 color[1] = bound(0, color2[1], 1);
1442 color[2] = bound(0, color2[2], 1);
1443 GL_Color(color[0], color[1], color[2], 1);
1444 R_Mesh_Draw(numverts, numtriangles, elements);
1446 c_rt_lighttris += numtriangles;
1451 // 2/2/2 2D combine path (any dot3 card)
1452 memset(&m, 0, sizeof(m));
1453 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1454 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1455 m.pointer_texcoord[0] = varray_texcoord2f[0];
1456 m.pointer_texcoord[1] = varray_texcoord2f[1];
1457 R_Mesh_State_Texture(&m);
1458 qglColorMask(0,0,0,1);
1459 GL_BlendFunc(GL_ONE, GL_ZERO);
1460 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1461 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1462 R_Mesh_Draw(numverts, numtriangles, elements);
1464 c_rt_lighttris += numtriangles;
1466 memset(&m, 0, sizeof(m));
1467 m.tex[0] = R_GetTexture(bumptexture);
1468 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1469 m.texcombinergb[0] = GL_REPLACE;
1470 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1471 m.pointer_texcoord[0] = texcoord2f;
1472 m.pointer_texcoord[1] = varray_texcoord3f[1];
1473 R_Mesh_State_Texture(&m);
1474 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1475 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1476 R_Mesh_Draw(numverts, numtriangles, elements);
1478 c_rt_lighttris += numtriangles;
1480 memset(&m, 0, sizeof(m));
1481 m.tex[0] = R_GetTexture(basetexture);
1482 m.texcubemap[1] = R_GetTexture(lightcubemap);
1483 m.pointer_texcoord[0] = texcoord2f;
1484 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1485 R_Mesh_State_Texture(&m);
1486 qglColorMask(1,1,1,0);
1487 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1489 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1490 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1491 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1493 color[0] = bound(0, color2[0], 1);
1494 color[1] = bound(0, color2[1], 1);
1495 color[2] = bound(0, color2[2], 1);
1496 GL_Color(color[0], color[1], color[2], 1);
1497 R_Mesh_Draw(numverts, numtriangles, elements);
1499 c_rt_lighttris += numtriangles;
1505 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1506 GL_DepthMask(false);
1508 GL_ColorPointer(varray_color4f);
1509 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1510 memset(&m, 0, sizeof(m));
1511 m.tex[0] = R_GetTexture(basetexture);
1512 m.pointer_texcoord[0] = texcoord2f;
1513 if (r_textureunits.integer >= 2)
1516 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1517 m.pointer_texcoord[1] = varray_texcoord2f[1];
1518 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1520 R_Mesh_State_Texture(&m);
1521 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1523 color[0] = bound(0, color2[0], 1);
1524 color[1] = bound(0, color2[1], 1);
1525 color[2] = bound(0, color2[2], 1);
1526 if (r_textureunits.integer >= 2)
1527 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1529 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1530 R_Mesh_Draw(numverts, numtriangles, elements);
1532 c_rt_lighttris += numtriangles;
1537 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)
1540 float color[3], color2[3], colorscale;
1542 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1545 glosstexture = r_shadow_blankglosstexture;
1546 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1548 colorscale = r_shadow_glossintensity.value;
1550 bumptexture = r_shadow_blankbumptexture;
1551 if (glosstexture == r_shadow_blankglosstexture)
1552 colorscale *= r_shadow_gloss2intensity.value;
1553 GL_VertexPointer(vertex3f);
1555 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1557 // 2/0/0/1/2 3D combine blendsquare path
1558 memset(&m, 0, sizeof(m));
1559 m.tex[0] = R_GetTexture(bumptexture);
1560 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1561 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1562 m.pointer_texcoord[0] = texcoord2f;
1563 m.pointer_texcoord[1] = varray_texcoord3f[1];
1564 R_Mesh_State_Texture(&m);
1565 qglColorMask(0,0,0,1);
1566 // this squares the result
1567 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1568 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1569 R_Mesh_Draw(numverts, numtriangles, elements);
1571 c_rt_lighttris += numtriangles;
1573 memset(&m, 0, sizeof(m));
1574 R_Mesh_State_Texture(&m);
1575 // square alpha in framebuffer a few times to make it shiny
1576 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1577 // these comments are a test run through this math for intensity 0.5
1578 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1579 // 0.25 * 0.25 = 0.0625 (this is another pass)
1580 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1581 R_Mesh_Draw(numverts, numtriangles, elements);
1583 c_rt_lighttris += numtriangles;
1584 R_Mesh_Draw(numverts, numtriangles, elements);
1586 c_rt_lighttris += numtriangles;
1588 memset(&m, 0, sizeof(m));
1589 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1590 m.pointer_texcoord[0] = varray_texcoord3f[0];
1591 R_Mesh_State_Texture(&m);
1592 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1593 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1594 R_Mesh_Draw(numverts, numtriangles, elements);
1596 c_rt_lighttris += numtriangles;
1598 memset(&m, 0, sizeof(m));
1599 m.tex[0] = R_GetTexture(glosstexture);
1600 m.texcubemap[1] = R_GetTexture(lightcubemap);
1601 m.pointer_texcoord[0] = texcoord2f;
1602 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1603 R_Mesh_State_Texture(&m);
1604 qglColorMask(1,1,1,0);
1605 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1607 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1608 VectorScale(lightcolor, colorscale, color2);
1609 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1611 color[0] = bound(0, color2[0], 1);
1612 color[1] = bound(0, color2[1], 1);
1613 color[2] = bound(0, color2[2], 1);
1614 GL_Color(color[0], color[1], color[2], 1);
1615 R_Mesh_Draw(numverts, numtriangles, elements);
1617 c_rt_lighttris += numtriangles;
1620 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1622 // 2/0/0/2 3D combine blendsquare path
1623 memset(&m, 0, sizeof(m));
1624 m.tex[0] = R_GetTexture(bumptexture);
1625 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1626 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1627 m.pointer_texcoord[0] = texcoord2f;
1628 m.pointer_texcoord[1] = varray_texcoord3f[1];
1629 R_Mesh_State_Texture(&m);
1630 qglColorMask(0,0,0,1);
1631 // this squares the result
1632 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1633 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1634 R_Mesh_Draw(numverts, numtriangles, elements);
1636 c_rt_lighttris += numtriangles;
1638 memset(&m, 0, sizeof(m));
1639 R_Mesh_State_Texture(&m);
1640 // square alpha in framebuffer a few times to make it shiny
1641 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1642 // these comments are a test run through this math for intensity 0.5
1643 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1644 // 0.25 * 0.25 = 0.0625 (this is another pass)
1645 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1646 R_Mesh_Draw(numverts, numtriangles, elements);
1648 c_rt_lighttris += numtriangles;
1649 R_Mesh_Draw(numverts, numtriangles, elements);
1651 c_rt_lighttris += numtriangles;
1653 memset(&m, 0, sizeof(m));
1654 m.tex[0] = R_GetTexture(glosstexture);
1655 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1656 m.pointer_texcoord[0] = texcoord2f;
1657 m.pointer_texcoord[1] = varray_texcoord3f[1];
1658 R_Mesh_State_Texture(&m);
1659 qglColorMask(1,1,1,0);
1660 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1661 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1662 VectorScale(lightcolor, colorscale, color2);
1663 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1665 color[0] = bound(0, color2[0], 1);
1666 color[1] = bound(0, color2[1], 1);
1667 color[2] = bound(0, color2[2], 1);
1668 GL_Color(color[0], color[1], color[2], 1);
1669 R_Mesh_Draw(numverts, numtriangles, elements);
1671 c_rt_lighttris += numtriangles;
1674 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1676 // 2/0/0/2/2 2D combine blendsquare path
1677 memset(&m, 0, sizeof(m));
1678 m.tex[0] = R_GetTexture(bumptexture);
1679 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1680 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1681 m.pointer_texcoord[0] = texcoord2f;
1682 m.pointer_texcoord[1] = varray_texcoord3f[1];
1683 R_Mesh_State_Texture(&m);
1684 qglColorMask(0,0,0,1);
1685 // this squares the result
1686 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1687 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1688 R_Mesh_Draw(numverts, numtriangles, elements);
1690 c_rt_lighttris += numtriangles;
1692 memset(&m, 0, sizeof(m));
1693 R_Mesh_State_Texture(&m);
1694 // square alpha in framebuffer a few times to make it shiny
1695 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1696 // these comments are a test run through this math for intensity 0.5
1697 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1698 // 0.25 * 0.25 = 0.0625 (this is another pass)
1699 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1700 R_Mesh_Draw(numverts, numtriangles, elements);
1702 c_rt_lighttris += numtriangles;
1703 R_Mesh_Draw(numverts, numtriangles, elements);
1705 c_rt_lighttris += numtriangles;
1707 memset(&m, 0, sizeof(m));
1708 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1709 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1710 m.pointer_texcoord[0] = varray_texcoord2f[0];
1711 m.pointer_texcoord[1] = varray_texcoord2f[1];
1712 R_Mesh_State_Texture(&m);
1713 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1714 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1715 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1716 R_Mesh_Draw(numverts, numtriangles, elements);
1718 c_rt_lighttris += numtriangles;
1720 memset(&m, 0, sizeof(m));
1721 m.tex[0] = R_GetTexture(glosstexture);
1722 m.texcubemap[1] = R_GetTexture(lightcubemap);
1723 m.pointer_texcoord[0] = texcoord2f;
1724 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1725 R_Mesh_State_Texture(&m);
1726 qglColorMask(1,1,1,0);
1727 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1729 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1730 VectorScale(lightcolor, colorscale, color2);
1731 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1733 color[0] = bound(0, color2[0], 1);
1734 color[1] = bound(0, color2[1], 1);
1735 color[2] = bound(0, color2[2], 1);
1736 GL_Color(color[0], color[1], color[2], 1);
1737 R_Mesh_Draw(numverts, numtriangles, elements);
1739 c_rt_lighttris += numtriangles;
1745 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1747 R_Mesh_Matrix(matrix);
1748 if (r_shadow_showtris.integer)
1752 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1753 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1754 qglDisable(GL_DEPTH_TEST);
1755 qglDisable(GL_STENCIL_TEST);
1756 //qglDisable(GL_CULL_FACE);
1757 qglColorMask(1,1,1,1);
1758 memset(&m, 0, sizeof(m));
1759 R_Mesh_State_Texture(&m);
1760 GL_Color(0,0.1,0,1);
1761 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1762 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1764 GL_VertexPointer(mesh->vertex3f);
1765 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1767 //qglEnable(GL_CULL_FACE);
1769 qglEnable(GL_DEPTH_TEST);
1772 qglEnable(GL_STENCIL_TEST);
1773 qglColorMask(0,0,0,0);
1776 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1779 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)
1782 R_Mesh_Matrix(matrix);
1783 if (r_shadow_showtris.integer)
1786 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1787 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1788 qglDisable(GL_DEPTH_TEST);
1789 qglDisable(GL_STENCIL_TEST);
1790 //qglDisable(GL_CULL_FACE);
1791 memset(&m, 0, sizeof(m));
1792 R_Mesh_State_Texture(&m);
1793 GL_Color(0.2,0,0,1);
1794 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1795 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1797 GL_VertexPointer(mesh->vertex3f);
1798 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1800 //qglEnable(GL_CULL_FACE);
1802 qglEnable(GL_DEPTH_TEST);
1804 qglEnable(GL_STENCIL_TEST);
1806 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1808 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);
1809 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);
1813 cvar_t r_editlights = {0, "r_editlights", "0"};
1814 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1815 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1816 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1817 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1818 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1819 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1820 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1821 worldlight_t *r_shadow_worldlightchain;
1822 worldlight_t *r_shadow_selectedlight;
1823 vec3_t r_editlights_cursorlocation;
1825 static int lightpvsbytes;
1826 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1828 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1830 int i, j, k, l, maxverts = 256, tris;
1831 float *vertex3f = NULL, mins[3], maxs[3];
1833 shadowmesh_t *mesh, *castmesh = NULL;
1835 if (radius < 15 || DotProduct(color, color) < 0.03)
1837 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1841 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1842 VectorCopy(origin, e->origin);
1843 VectorCopy(color, e->light);
1844 e->lightradius = radius;
1846 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1848 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1851 e->castshadows = castshadow;
1853 e->cullradius = e->lightradius;
1854 for (k = 0;k < 3;k++)
1856 mins[k] = e->origin[k] - e->lightradius;
1857 maxs[k] = e->origin[k] + e->lightradius;
1860 e->next = r_shadow_worldlightchain;
1861 r_shadow_worldlightchain = e;
1862 if (cubemapname && cubemapname[0])
1864 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1865 strcpy(e->cubemapname, cubemapname);
1866 // FIXME: add cubemap loading (and don't load a cubemap twice)
1868 // FIXME: rewrite this to store ALL geometry into a cache in the light
1870 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1871 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1874 if (cl.worldmodel->brushq3.num_leafs)
1878 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1879 VectorCopy(e->origin, e->mins);
1880 VectorCopy(e->origin, e->maxs);
1881 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1882 face->lighttemp_castshadow = false;
1883 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1885 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1887 for (k = 0;k < 3;k++)
1889 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1890 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1892 for (j = 0;j < leaf->numleaffaces;j++)
1894 face = leaf->firstleafface[j];
1895 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1896 face->lighttemp_castshadow = true;
1901 // add surfaces to shadow casting mesh and light mesh
1902 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1904 if (face->lighttemp_castshadow)
1906 face->lighttemp_castshadow = false;
1907 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1910 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1911 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1912 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1913 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);
1918 else if (cl.worldmodel->brushq1.numleafs)
1922 VectorCopy(e->origin, e->mins);
1923 VectorCopy(e->origin, e->maxs);
1924 i = CL_PointQ1Contents(e->origin);
1926 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1927 surf->lighttemp_castshadow = false;
1929 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1932 qbyte *bytesurfacepvs;
1934 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1935 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1937 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1939 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1941 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1943 for (k = 0;k < 3;k++)
1945 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1946 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1951 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1952 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1953 surf->lighttemp_castshadow = true;
1955 Mem_Free(byteleafpvs);
1956 Mem_Free(bytesurfacepvs);
1960 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1961 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1963 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1965 for (k = 0;k < 3;k++)
1967 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1968 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1970 for (j = 0;j < leaf->nummarksurfaces;j++)
1972 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1973 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1974 surf->lighttemp_castshadow = true;
1980 // add surfaces to shadow casting mesh and light mesh
1981 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1983 if (surf->lighttemp_castshadow)
1985 surf->lighttemp_castshadow = false;
1986 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1987 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);
1988 if (!(surf->flags & SURF_DRAWSKY))
1989 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);
1995 // limit box to light bounds (in case it grew larger)
1996 for (k = 0;k < 3;k++)
1998 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1999 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
2001 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2003 // cast shadow volume from castmesh
2004 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2008 for (mesh = castmesh;mesh;mesh = mesh->next)
2010 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2011 maxverts = max(maxverts, mesh->numverts * 2);
2016 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2017 // now that we have the buffers big enough, construct and add
2018 // the shadow volume mesh
2020 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2021 for (mesh = castmesh;mesh;mesh = mesh->next)
2023 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2024 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)))
2025 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2030 // we're done with castmesh now
2031 Mod_ShadowMesh_Free(castmesh);
2034 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2035 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2038 if (e->meshchain_shadow)
2039 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2040 k += mesh->numtriangles;
2042 if (e->meshchain_light)
2043 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2044 l += mesh->numtriangles;
2045 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);
2048 void R_Shadow_FreeWorldLight(worldlight_t *light)
2050 worldlight_t **lightpointer;
2051 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2052 if (*lightpointer != light)
2053 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2054 *lightpointer = light->next;
2055 if (light->cubemapname)
2056 Mem_Free(light->cubemapname);
2057 if (light->meshchain_shadow)
2058 Mod_ShadowMesh_Free(light->meshchain_shadow);
2059 if (light->meshchain_light)
2060 Mod_ShadowMesh_Free(light->meshchain_light);
2064 void R_Shadow_ClearWorldLights(void)
2066 while (r_shadow_worldlightchain)
2067 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2068 r_shadow_selectedlight = NULL;
2071 void R_Shadow_SelectLight(worldlight_t *light)
2073 if (r_shadow_selectedlight)
2074 r_shadow_selectedlight->selected = false;
2075 r_shadow_selectedlight = light;
2076 if (r_shadow_selectedlight)
2077 r_shadow_selectedlight->selected = true;
2080 rtexture_t *lighttextures[5];
2082 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2084 float scale = r_editlights_cursorgrid.value * 0.5f;
2085 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);
2088 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2091 const worldlight_t *light;
2094 if (light->selected)
2095 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2096 if (!light->meshchain_shadow)
2098 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);
2101 void R_Shadow_DrawLightSprites(void)
2105 worldlight_t *light;
2107 for (i = 0;i < 5;i++)
2109 lighttextures[i] = NULL;
2110 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2111 lighttextures[i] = pic->tex;
2114 for (light = r_shadow_worldlightchain;light;light = light->next)
2115 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2116 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2119 void R_Shadow_SelectLightInView(void)
2121 float bestrating, rating, temp[3];
2122 worldlight_t *best, *light;
2125 for (light = r_shadow_worldlightchain;light;light = light->next)
2127 VectorSubtract(light->origin, r_vieworigin, temp);
2128 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2131 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2132 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2134 bestrating = rating;
2139 R_Shadow_SelectLight(best);
2142 void R_Shadow_LoadWorldLights(void)
2144 int n, a, style, shadow;
2145 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2146 float origin[3], radius, color[3];
2147 if (cl.worldmodel == NULL)
2149 Con_Printf("No map loaded.\n");
2152 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2153 strlcat (name, ".rtlights", sizeof (name));
2154 lightsstring = FS_LoadFile(name, false);
2162 while (*s && *s != '\n')
2168 // check for modifier flags
2174 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);
2180 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);
2183 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2184 radius *= r_editlights_rtlightssizescale.value;
2185 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2190 Con_Printf("invalid rtlights file \"%s\"\n", name);
2191 Mem_Free(lightsstring);
2195 void R_Shadow_SaveWorldLights(void)
2197 worldlight_t *light;
2198 int bufchars, bufmaxchars;
2200 char name[MAX_QPATH];
2202 if (!r_shadow_worldlightchain)
2204 if (cl.worldmodel == NULL)
2206 Con_Printf("No map loaded.\n");
2209 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2210 strlcat (name, ".rtlights", sizeof (name));
2211 bufchars = bufmaxchars = 0;
2213 for (light = r_shadow_worldlightchain;light;light = light->next)
2215 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 : "");
2216 if (bufchars + (int) strlen(line) > bufmaxchars)
2218 bufmaxchars = bufchars + strlen(line) + 2048;
2220 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2224 memcpy(buf, oldbuf, bufchars);
2230 memcpy(buf + bufchars, line, strlen(line));
2231 bufchars += strlen(line);
2235 FS_WriteFile(name, buf, bufchars);
2240 void R_Shadow_LoadLightsFile(void)
2243 char name[MAX_QPATH], *lightsstring, *s, *t;
2244 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2245 if (cl.worldmodel == NULL)
2247 Con_Printf("No map loaded.\n");
2250 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2251 strlcat (name, ".lights", sizeof (name));
2252 lightsstring = FS_LoadFile(name, false);
2260 while (*s && *s != '\n')
2265 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);
2269 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);
2272 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2273 radius = bound(15, radius, 4096);
2274 VectorScale(color, (2.0f / (8388608.0f)), color);
2275 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2280 Con_Printf("invalid lights file \"%s\"\n", name);
2281 Mem_Free(lightsstring);
2285 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2287 int entnum, style, islight;
2288 char key[256], value[1024];
2289 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2292 if (cl.worldmodel == NULL)
2294 Con_Printf("No map loaded.\n");
2297 data = cl.worldmodel->brush.entities;
2300 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2303 origin[0] = origin[1] = origin[2] = 0;
2304 originhack[0] = originhack[1] = originhack[2] = 0;
2305 color[0] = color[1] = color[2] = 1;
2306 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2313 if (!COM_ParseToken(&data, false))
2315 if (com_token[0] == '}')
2316 break; // end of entity
2317 if (com_token[0] == '_')
2318 strcpy(key, com_token + 1);
2320 strcpy(key, com_token);
2321 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2322 key[strlen(key)-1] = 0;
2323 if (!COM_ParseToken(&data, false))
2325 strcpy(value, com_token);
2327 // now that we have the key pair worked out...
2328 if (!strcmp("light", key))
2329 light = atof(value);
2330 else if (!strcmp("origin", key))
2331 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2332 else if (!strcmp("color", key))
2333 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2334 else if (!strcmp("wait", key))
2335 fadescale = atof(value);
2336 else if (!strcmp("classname", key))
2338 if (!strncmp(value, "light", 5))
2341 if (!strcmp(value, "light_fluoro"))
2346 overridecolor[0] = 1;
2347 overridecolor[1] = 1;
2348 overridecolor[2] = 1;
2350 if (!strcmp(value, "light_fluorospark"))
2355 overridecolor[0] = 1;
2356 overridecolor[1] = 1;
2357 overridecolor[2] = 1;
2359 if (!strcmp(value, "light_globe"))
2364 overridecolor[0] = 1;
2365 overridecolor[1] = 0.8;
2366 overridecolor[2] = 0.4;
2368 if (!strcmp(value, "light_flame_large_yellow"))
2373 overridecolor[0] = 1;
2374 overridecolor[1] = 0.5;
2375 overridecolor[2] = 0.1;
2377 if (!strcmp(value, "light_flame_small_yellow"))
2382 overridecolor[0] = 1;
2383 overridecolor[1] = 0.5;
2384 overridecolor[2] = 0.1;
2386 if (!strcmp(value, "light_torch_small_white"))
2391 overridecolor[0] = 1;
2392 overridecolor[1] = 0.5;
2393 overridecolor[2] = 0.1;
2395 if (!strcmp(value, "light_torch_small_walltorch"))
2400 overridecolor[0] = 1;
2401 overridecolor[1] = 0.5;
2402 overridecolor[2] = 0.1;
2406 else if (!strcmp("style", key))
2407 style = atoi(value);
2408 else if (cl.worldmodel->type == mod_brushq3)
2410 if (!strcmp("scale", key))
2411 lightscale = atof(value);
2412 if (!strcmp("fade", key))
2413 fadescale = atof(value);
2416 if (light <= 0 && islight)
2418 if (lightscale <= 0)
2422 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2423 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2424 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2425 VectorCopy(overridecolor, color);
2426 VectorScale(color, light, color);
2427 VectorAdd(origin, originhack, origin);
2429 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2434 void R_Shadow_SetCursorLocationForView(void)
2436 vec_t dist, push, frac;
2437 vec3_t dest, endpos, normal;
2438 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2439 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2442 dist = frac * r_editlights_cursordistance.value;
2443 push = r_editlights_cursorpushback.value;
2447 VectorMA(endpos, push, r_viewforward, endpos);
2448 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2450 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2451 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2452 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2455 void R_Shadow_UpdateWorldLightSelection(void)
2457 if (r_editlights.integer)
2459 R_Shadow_SetCursorLocationForView();
2460 R_Shadow_SelectLightInView();
2461 R_Shadow_DrawLightSprites();
2464 R_Shadow_SelectLight(NULL);
2467 void R_Shadow_EditLights_Clear_f(void)
2469 R_Shadow_ClearWorldLights();
2472 void R_Shadow_EditLights_Reload_f(void)
2474 r_shadow_reloadlights = true;
2477 void R_Shadow_EditLights_Save_f(void)
2480 R_Shadow_SaveWorldLights();
2483 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2485 R_Shadow_ClearWorldLights();
2486 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2489 void R_Shadow_EditLights_ImportLightsFile_f(void)
2491 R_Shadow_ClearWorldLights();
2492 R_Shadow_LoadLightsFile();
2495 void R_Shadow_EditLights_Spawn_f(void)
2498 if (!r_editlights.integer)
2500 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2503 if (Cmd_Argc() != 1)
2505 Con_Printf("r_editlights_spawn does not take parameters\n");
2508 color[0] = color[1] = color[2] = 1;
2509 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2512 void R_Shadow_EditLights_Edit_f(void)
2514 vec3_t origin, color;
2517 char cubemapname[1024];
2518 if (!r_editlights.integer)
2520 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2523 if (!r_shadow_selectedlight)
2525 Con_Printf("No selected light.\n");
2528 VectorCopy(r_shadow_selectedlight->origin, origin);
2529 radius = r_shadow_selectedlight->lightradius;
2530 VectorCopy(r_shadow_selectedlight->light, color);
2531 style = r_shadow_selectedlight->style;
2532 if (r_shadow_selectedlight->cubemapname)
2533 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2536 shadows = r_shadow_selectedlight->castshadows;
2537 if (!strcmp(Cmd_Argv(1), "origin"))
2539 if (Cmd_Argc() != 5)
2541 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2544 origin[0] = atof(Cmd_Argv(2));
2545 origin[1] = atof(Cmd_Argv(3));
2546 origin[2] = atof(Cmd_Argv(4));
2548 else if (!strcmp(Cmd_Argv(1), "originx"))
2550 if (Cmd_Argc() != 3)
2552 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2555 origin[0] = atof(Cmd_Argv(2));
2557 else if (!strcmp(Cmd_Argv(1), "originy"))
2559 if (Cmd_Argc() != 3)
2561 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2564 origin[1] = atof(Cmd_Argv(2));
2566 else if (!strcmp(Cmd_Argv(1), "originz"))
2568 if (Cmd_Argc() != 3)
2570 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2573 origin[2] = atof(Cmd_Argv(2));
2575 else if (!strcmp(Cmd_Argv(1), "move"))
2577 if (Cmd_Argc() != 5)
2579 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2582 origin[0] += atof(Cmd_Argv(2));
2583 origin[1] += atof(Cmd_Argv(3));
2584 origin[2] += atof(Cmd_Argv(4));
2586 else if (!strcmp(Cmd_Argv(1), "movex"))
2588 if (Cmd_Argc() != 3)
2590 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2593 origin[0] += atof(Cmd_Argv(2));
2595 else if (!strcmp(Cmd_Argv(1), "movey"))
2597 if (Cmd_Argc() != 3)
2599 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2602 origin[1] += atof(Cmd_Argv(2));
2604 else if (!strcmp(Cmd_Argv(1), "movez"))
2606 if (Cmd_Argc() != 3)
2608 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2611 origin[2] += atof(Cmd_Argv(2));
2613 else if (!strcmp(Cmd_Argv(1), "color"))
2615 if (Cmd_Argc() != 5)
2617 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2620 color[0] = atof(Cmd_Argv(2));
2621 color[1] = atof(Cmd_Argv(3));
2622 color[2] = atof(Cmd_Argv(4));
2624 else if (!strcmp(Cmd_Argv(1), "radius"))
2626 if (Cmd_Argc() != 3)
2628 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2631 radius = atof(Cmd_Argv(2));
2633 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2635 if (Cmd_Argc() != 3)
2637 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2640 style = atoi(Cmd_Argv(2));
2642 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2646 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2649 if (Cmd_Argc() == 3)
2650 strcpy(cubemapname, Cmd_Argv(2));
2654 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2656 if (Cmd_Argc() != 3)
2658 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2661 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2665 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2666 Con_Printf("Selected light's properties:\n");
2667 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2668 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2669 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2670 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2671 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2672 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2675 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2676 r_shadow_selectedlight = NULL;
2677 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2680 extern int con_vislines;
2681 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2685 if (r_shadow_selectedlight == NULL)
2689 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2690 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;
2691 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2692 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;
2693 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2694 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2695 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;
2698 void R_Shadow_EditLights_ToggleShadow_f(void)
2700 if (!r_editlights.integer)
2702 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2705 if (!r_shadow_selectedlight)
2707 Con_Printf("No selected light.\n");
2710 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);
2711 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2712 r_shadow_selectedlight = NULL;
2715 void R_Shadow_EditLights_Remove_f(void)
2717 if (!r_editlights.integer)
2719 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2722 if (!r_shadow_selectedlight)
2724 Con_Printf("No selected light.\n");
2727 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2728 r_shadow_selectedlight = NULL;
2731 void R_Shadow_EditLights_Help_f(void)
2734 "Documentation on r_editlights system:\n"
2736 "r_editlights : enable/disable editing mode\n"
2737 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2738 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2739 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2740 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2741 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2742 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2743 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2745 "r_editlights_help : this help\n"
2746 "r_editlights_clear : remove all lights\n"
2747 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2748 "r_editlights_save : save to .rtlights file\n"
2749 "r_editlights_spawn : create a light with default settings\n"
2750 "r_editlights_edit command : edit selected light - more documentation below\n"
2751 "r_editlights_remove : remove selected light\n"
2752 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2753 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2754 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2756 "origin x y z : set light location\n"
2757 "originx x: set x component of light location\n"
2758 "originy y: set y component of light location\n"
2759 "originz z: set z component of light location\n"
2760 "move x y z : adjust light location\n"
2761 "movex x: adjust x component of light location\n"
2762 "movey y: adjust y component of light location\n"
2763 "movez z: adjust z component of light location\n"
2764 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2765 "radius radius : set radius (size) of light\n"
2766 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2767 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2768 "shadows 1/0 : turn on/off shadows\n"
2769 "<nothing> : print light properties to console\n"
2773 void R_Shadow_EditLights_Init(void)
2775 Cvar_RegisterVariable(&r_editlights);
2776 Cvar_RegisterVariable(&r_editlights_cursordistance);
2777 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2778 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2779 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2780 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2781 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2782 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2783 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2784 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2785 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2786 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2787 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2788 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2789 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2790 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2791 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2792 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);