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_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
151 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
152 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
153 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
154 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
155 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
156 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
157 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
158 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
159 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
160 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
161 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
162 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
163 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
164 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
165 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
166 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
167 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
168 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
169 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
170 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
171 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
173 int c_rt_lights, c_rt_clears, c_rt_scissored;
174 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
175 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
177 void R_Shadow_ClearWorldLights(void);
178 void R_Shadow_SaveWorldLights(void);
179 void R_Shadow_LoadWorldLights(void);
180 void R_Shadow_LoadLightsFile(void);
181 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
183 void r_shadow_start(void)
185 // allocate vertex processing arrays
186 r_shadow_mempool = Mem_AllocPool("R_Shadow");
187 maxshadowelements = 0;
188 shadowelements = NULL;
193 maxtrianglefacinglight = 0;
194 trianglefacinglight = NULL;
195 trianglefacinglightlist = NULL;
196 r_shadow_normalcubetexture = NULL;
197 r_shadow_attenuation2dtexture = NULL;
198 r_shadow_attenuation3dtexture = NULL;
199 r_shadow_blankbumptexture = NULL;
200 r_shadow_blankglosstexture = NULL;
201 r_shadow_blankwhitetexture = NULL;
202 r_shadow_texturepool = NULL;
203 R_Shadow_ClearWorldLights();
204 r_shadow_reloadlights = true;
207 void r_shadow_shutdown(void)
209 R_Shadow_ClearWorldLights();
210 r_shadow_reloadlights = true;
211 r_shadow_normalcubetexture = NULL;
212 r_shadow_attenuation2dtexture = NULL;
213 r_shadow_attenuation3dtexture = NULL;
214 r_shadow_blankbumptexture = NULL;
215 r_shadow_blankglosstexture = NULL;
216 r_shadow_blankwhitetexture = NULL;
217 R_FreeTexturePool(&r_shadow_texturepool);
218 maxshadowelements = 0;
219 shadowelements = NULL;
224 maxtrianglefacinglight = 0;
225 trianglefacinglight = NULL;
226 trianglefacinglightlist = NULL;
227 Mem_FreePool(&r_shadow_mempool);
230 void r_shadow_newmap(void)
232 R_Shadow_ClearWorldLights();
233 r_shadow_reloadlights = true;
236 void R_Shadow_Help_f(void)
239 "Documentation on r_shadow system:\n"
241 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
242 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
243 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
244 "r_shadow_realtime_world : use realtime world light rendering\n"
245 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
246 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
247 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
248 "r_shadow_glossintensity : brightness of textured gloss\n"
249 "r_shadow_gloss2intensity : brightness of forced gloss\n"
250 "r_shadow_debuglight : render only this light number (-1 = all)\n"
251 "r_shadow_scissor : use scissor optimization\n"
252 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
253 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
254 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
255 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
256 "r_shadow_portallight : use portal visibility for static light precomputation\n"
257 "r_shadow_projectdistance : shadow volume projection distance\n"
258 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
259 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
260 "r_shadow_worldshadows : enable world shadows\n"
261 "r_shadow_dlightshadows : enable dlight shadows\n"
263 "r_shadow_help : this help\n"
267 void R_Shadow_Init(void)
269 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
270 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
271 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
272 Cvar_RegisterVariable(&r_shadow_realtime_world);
273 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
274 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
275 Cvar_RegisterVariable(&r_shadow_gloss);
276 Cvar_RegisterVariable(&r_shadow_glossintensity);
277 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
278 Cvar_RegisterVariable(&r_shadow_debuglight);
279 Cvar_RegisterVariable(&r_shadow_scissor);
280 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
281 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
282 Cvar_RegisterVariable(&r_shadow_polygonfactor);
283 Cvar_RegisterVariable(&r_shadow_polygonoffset);
284 Cvar_RegisterVariable(&r_shadow_portallight);
285 Cvar_RegisterVariable(&r_shadow_projectdistance);
286 Cvar_RegisterVariable(&r_shadow_texture3d);
287 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
288 Cvar_RegisterVariable(&r_shadow_worldshadows);
289 Cvar_RegisterVariable(&r_shadow_dlightshadows);
290 Cvar_RegisterVariable(&r_shadow_showtris);
291 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
292 R_Shadow_EditLights_Init();
293 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
296 void R_Shadow_ResizeTriangleFacingLight(int numtris)
298 // make sure trianglefacinglight is big enough for this volume
299 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
300 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
301 if (maxtrianglefacinglight < numtris)
303 maxtrianglefacinglight = numtris;
304 if (trianglefacinglight)
305 Mem_Free(trianglefacinglight);
306 if (trianglefacinglightlist)
307 Mem_Free(trianglefacinglightlist);
308 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
309 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
313 int *R_Shadow_ResizeShadowElements(int numtris)
315 // make sure shadowelements is big enough for this volume
316 if (maxshadowelements < numtris * 24)
318 maxshadowelements = numtris * 24;
320 Mem_Free(shadowelements);
321 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
323 return shadowelements;
327 // readable version of some code found below
328 //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]))))
329 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
331 float dir0[3], dir1[3], normal[3];
333 // calculate two mostly perpendicular edge directions
334 VectorSubtract(a, b, dir0);
335 VectorSubtract(c, b, dir1);
337 // we have two edge directions, we can calculate a third vector from
338 // them, which is the direction of the surface normal (it's magnitude
340 CrossProduct(dir0, dir1, normal);
342 // compare distance of light along normal, with distance of any point
343 // of the triangle along the same normal (the triangle is planar,
344 // I.E. flat, so all points give the same answer)
345 return DotProduct(p, normal) > DotProduct(a, normal);
347 int checkcastshadowfromedge(int t, int i)
351 if (t >= trianglerange_start && t < trianglerange_end)
353 if (t < i && !trianglefacinglight[t])
364 te = inelement3i + t * 3;
365 v[0] = invertex3f + te[0] * 3;
366 v[1] = invertex3f + te[1] * 3;
367 v[2] = invertex3f + te[2] * 3;
368 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
377 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)
379 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
381 const int *e, *n, *te;
384 // make sure trianglefacinglight is big enough for this volume
385 if (maxtrianglefacinglight < trianglerange_end)
386 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
388 if (maxvertexupdate < innumvertices)
390 maxvertexupdate = innumvertices;
392 Mem_Free(vertexupdate);
394 Mem_Free(vertexremap);
395 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
396 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
400 if (r_shadow_singlepassvolumegeneration.integer)
402 // one pass approach (identify lit/dark faces and generate sides while doing so)
403 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
405 // calculate triangle facing flag
406 v[0] = invertex3f + e[0] * 3;
407 v[1] = invertex3f + e[1] * 3;
408 v[2] = invertex3f + e[2] * 3;
409 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
411 // make sure the vertices are created
412 for (j = 0;j < 3;j++)
414 if (vertexupdate[e[j]] != vertexupdatenum)
416 vertexupdate[e[j]] = vertexupdatenum;
417 vertexremap[e[j]] = outvertices;
418 VectorCopy(v[j], outvertex3f);
419 VectorSubtract(v[j], relativelightorigin, temp);
420 f = projectdistance / VectorLength(temp);
421 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
426 // output the front and back triangles
427 vr[0] = vertexremap[e[0]];
428 vr[1] = vertexremap[e[1]];
429 vr[2] = vertexremap[e[2]];
430 outelement3i[0] = vr[0];
431 outelement3i[1] = vr[1];
432 outelement3i[2] = vr[2];
433 outelement3i[3] = vr[2] + 1;
434 outelement3i[4] = vr[1] + 1;
435 outelement3i[5] = vr[0] + 1;
438 // output the sides (facing outward from this triangle)
440 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]))))
442 outelement3i[0] = vr[1];
443 outelement3i[1] = vr[0];
444 outelement3i[2] = vr[0] + 1;
445 outelement3i[3] = vr[1];
446 outelement3i[4] = vr[0] + 1;
447 outelement3i[5] = vr[1] + 1;
452 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]))))
454 outelement3i[0] = vr[2];
455 outelement3i[1] = vr[1];
456 outelement3i[2] = vr[1] + 1;
457 outelement3i[3] = vr[2];
458 outelement3i[4] = vr[1] + 1;
459 outelement3i[5] = vr[2] + 1;
464 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]))))
466 outelement3i[0] = vr[0];
467 outelement3i[1] = vr[2];
468 outelement3i[2] = vr[2] + 1;
469 outelement3i[3] = vr[0];
470 outelement3i[4] = vr[2] + 1;
471 outelement3i[5] = vr[0] + 1;
478 // this triangle is not facing the light
479 // output the sides (facing inward to this triangle)
481 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
483 vr[0] = vertexremap[e[0]];
484 vr[1] = vertexremap[e[1]];
485 outelement3i[0] = vr[1];
486 outelement3i[1] = vr[0] + 1;
487 outelement3i[2] = vr[0];
488 outelement3i[3] = vr[1];
489 outelement3i[4] = vr[1] + 1;
490 outelement3i[5] = vr[0] + 1;
495 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
497 vr[1] = vertexremap[e[1]];
498 vr[2] = vertexremap[e[2]];
499 outelement3i[0] = vr[2];
500 outelement3i[1] = vr[1] + 1;
501 outelement3i[2] = vr[1];
502 outelement3i[3] = vr[2];
503 outelement3i[4] = vr[2] + 1;
504 outelement3i[5] = vr[1] + 1;
509 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
511 vr[0] = vertexremap[e[0]];
512 vr[2] = vertexremap[e[2]];
513 outelement3i[0] = vr[0];
514 outelement3i[1] = vr[2] + 1;
515 outelement3i[2] = vr[2];
516 outelement3i[3] = vr[0];
517 outelement3i[4] = vr[0] + 1;
518 outelement3i[5] = vr[2] + 1;
527 // two pass approach (identify lit/dark faces and then generate sides)
528 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
530 // calculate triangle facing flag
531 v[0] = invertex3f + e[0] * 3;
532 v[1] = invertex3f + e[1] * 3;
533 v[2] = invertex3f + e[2] * 3;
534 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
536 trianglefacinglightlist[numfacing++] = i;
537 // make sure the vertices are created
538 for (j = 0;j < 3;j++)
540 if (vertexupdate[e[j]] != vertexupdatenum)
542 vertexupdate[e[j]] = vertexupdatenum;
543 vertexremap[e[j]] = outvertices;
544 VectorSubtract(v[j], relativelightorigin, temp);
545 f = projectdistance / VectorLength(temp);
546 VectorCopy(v[j], outvertex3f);
547 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
552 // output the front and back triangles
553 outelement3i[0] = vertexremap[e[0]];
554 outelement3i[1] = vertexremap[e[1]];
555 outelement3i[2] = vertexremap[e[2]];
556 outelement3i[3] = vertexremap[e[2]] + 1;
557 outelement3i[4] = vertexremap[e[1]] + 1;
558 outelement3i[5] = vertexremap[e[0]] + 1;
563 for (i = 0;i < numfacing;i++)
565 t = trianglefacinglightlist[i];
566 e = inelement3i + t * 3;
567 n = inneighbor3i + t * 3;
568 // output the sides (facing outward from this triangle)
570 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]))))
572 vr[0] = vertexremap[e[0]];
573 vr[1] = vertexremap[e[1]];
574 outelement3i[0] = vr[1];
575 outelement3i[1] = vr[0];
576 outelement3i[2] = vr[0] + 1;
577 outelement3i[3] = vr[1];
578 outelement3i[4] = vr[0] + 1;
579 outelement3i[5] = vr[1] + 1;
584 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]))))
586 vr[1] = vertexremap[e[1]];
587 vr[2] = vertexremap[e[2]];
588 outelement3i[0] = vr[2];
589 outelement3i[1] = vr[1];
590 outelement3i[2] = vr[1] + 1;
591 outelement3i[3] = vr[2];
592 outelement3i[4] = vr[1] + 1;
593 outelement3i[5] = vr[2] + 1;
598 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
600 vr[0] = vertexremap[e[0]];
601 vr[2] = vertexremap[e[2]];
602 outelement3i[0] = vr[0];
603 outelement3i[1] = vr[2];
604 outelement3i[2] = vr[2] + 1;
605 outelement3i[3] = vr[0];
606 outelement3i[4] = vr[2] + 1;
607 outelement3i[5] = vr[0] + 1;
614 *outnumvertices = outvertices;
618 float varray_vertex3f2[65536*3];
620 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
623 if (projectdistance < 0.1)
625 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
631 // make sure shadowelements is big enough for this volume
632 if (maxshadowelements < numtris * 24)
633 R_Shadow_ResizeShadowElements(numtris);
635 // check which triangles are facing the light, and then output
636 // triangle elements and vertices... by clever use of elements we
637 // can construct the whole shadow from the unprojected vertices and
638 // the projected vertices
639 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
641 GL_VertexPointer(varray_vertex3f2);
642 if (r_shadowstage == SHADOWSTAGE_STENCIL)
644 // decrement stencil if frontface is behind depthbuffer
645 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
646 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
647 R_Mesh_Draw(outverts, tris, shadowelements);
649 c_rt_shadowtris += numtris;
650 // increment stencil if backface is behind depthbuffer
651 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
652 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
654 R_Mesh_Draw(outverts, tris, shadowelements);
656 c_rt_shadowtris += numtris;
660 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
663 if (r_shadowstage == SHADOWSTAGE_STENCIL)
665 // decrement stencil if frontface is behind depthbuffer
666 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
667 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
668 for (mesh = firstmesh;mesh;mesh = mesh->next)
670 GL_VertexPointer(mesh->vertex3f);
671 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
672 c_rtcached_shadowmeshes++;
673 c_rtcached_shadowtris += mesh->numtriangles;
675 // increment stencil if backface is behind depthbuffer
676 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
677 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
679 for (mesh = firstmesh;mesh;mesh = mesh->next)
681 GL_VertexPointer(mesh->vertex3f);
682 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
683 c_rtcached_shadowmeshes++;
684 c_rtcached_shadowtris += mesh->numtriangles;
688 float r_shadow_attenpower, r_shadow_attenscale;
689 static void R_Shadow_MakeTextures(void)
691 int x, y, z, d, side;
692 float v[3], s, t, intensity;
694 R_FreeTexturePool(&r_shadow_texturepool);
695 r_shadow_texturepool = R_AllocTexturePool();
696 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
697 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
699 #define ATTEN2DSIZE 64
700 #define ATTEN3DSIZE 32
701 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
706 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
711 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
716 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
717 if (gl_texturecubemap)
719 for (side = 0;side < 6;side++)
721 for (y = 0;y < NORMSIZE;y++)
723 for (x = 0;x < NORMSIZE;x++)
725 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
726 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
760 intensity = 127.0f / sqrt(DotProduct(v, v));
761 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
762 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
763 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
764 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
768 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
771 r_shadow_normalcubetexture = NULL;
772 for (y = 0;y < ATTEN2DSIZE;y++)
774 for (x = 0;x < ATTEN2DSIZE;x++)
776 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
777 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
779 intensity = 1.0f - sqrt(DotProduct(v, v));
781 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
782 d = bound(0, intensity, 255);
783 data[(y*ATTEN2DSIZE+x)*4+0] = d;
784 data[(y*ATTEN2DSIZE+x)*4+1] = d;
785 data[(y*ATTEN2DSIZE+x)*4+2] = d;
786 data[(y*ATTEN2DSIZE+x)*4+3] = d;
789 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
790 if (r_shadow_texture3d.integer)
792 for (z = 0;z < ATTEN3DSIZE;z++)
794 for (y = 0;y < ATTEN3DSIZE;y++)
796 for (x = 0;x < ATTEN3DSIZE;x++)
798 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
799 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
800 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
801 intensity = 1.0f - sqrt(DotProduct(v, v));
803 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
804 d = bound(0, intensity, 255);
805 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
806 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
807 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
808 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
812 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
817 void R_Shadow_Stage_Begin(void)
821 if (r_shadow_texture3d.integer && !gl_texture3d)
822 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
824 if (!r_shadow_attenuation2dtexture
825 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
826 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
827 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
828 R_Shadow_MakeTextures();
830 memset(&m, 0, sizeof(m));
831 GL_BlendFunc(GL_ONE, GL_ZERO);
834 R_Mesh_State_Texture(&m);
835 GL_Color(0, 0, 0, 1);
836 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
837 qglDisable(GL_SCISSOR_TEST);
838 r_shadowstage = SHADOWSTAGE_NONE;
840 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
841 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
842 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
845 void R_Shadow_LoadWorldLightsIfNeeded(void)
847 if (r_shadow_reloadlights && cl.worldmodel)
849 R_Shadow_ClearWorldLights();
850 r_shadow_reloadlights = false;
851 R_Shadow_LoadWorldLights();
852 if (r_shadow_worldlightchain == NULL)
854 R_Shadow_LoadLightsFile();
855 if (r_shadow_worldlightchain == NULL)
856 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
861 void R_Shadow_Stage_ShadowVolumes(void)
864 memset(&m, 0, sizeof(m));
865 R_Mesh_State_Texture(&m);
866 GL_Color(1, 1, 1, 1);
867 qglColorMask(0, 0, 0, 0);
868 GL_BlendFunc(GL_ONE, GL_ZERO);
871 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
872 //if (r_shadow_polygonoffset.value != 0)
874 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
875 // qglEnable(GL_POLYGON_OFFSET_FILL);
878 // qglDisable(GL_POLYGON_OFFSET_FILL);
879 qglDepthFunc(GL_LESS);
880 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
881 qglEnable(GL_STENCIL_TEST);
882 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
883 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
884 r_shadowstage = SHADOWSTAGE_STENCIL;
885 qglClear(GL_STENCIL_BUFFER_BIT);
887 // LordHavoc note: many shadow volumes reside entirely inside the world
888 // (that is to say they are entirely bounded by their lit surfaces),
889 // which can be optimized by handling things as an inverted light volume,
890 // with the shadow boundaries of the world being simulated by an altered
891 // (129) bias to stencil clearing on such lights
892 // FIXME: generate inverted light volumes for use as shadow volumes and
893 // optimize for them as noted above
896 void R_Shadow_Stage_LightWithoutShadows(void)
899 memset(&m, 0, sizeof(m));
900 R_Mesh_State_Texture(&m);
901 GL_BlendFunc(GL_ONE, GL_ONE);
904 qglPolygonOffset(0, 0);
905 //qglDisable(GL_POLYGON_OFFSET_FILL);
906 GL_Color(1, 1, 1, 1);
907 qglColorMask(1, 1, 1, 1);
908 qglDepthFunc(GL_EQUAL);
909 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
910 qglDisable(GL_STENCIL_TEST);
911 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
912 qglStencilFunc(GL_EQUAL, 128, 0xFF);
913 r_shadowstage = SHADOWSTAGE_LIGHT;
917 void R_Shadow_Stage_LightWithShadows(void)
920 memset(&m, 0, sizeof(m));
921 R_Mesh_State_Texture(&m);
922 GL_BlendFunc(GL_ONE, GL_ONE);
925 qglPolygonOffset(0, 0);
926 //qglDisable(GL_POLYGON_OFFSET_FILL);
927 GL_Color(1, 1, 1, 1);
928 qglColorMask(1, 1, 1, 1);
929 qglDepthFunc(GL_EQUAL);
930 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
931 qglEnable(GL_STENCIL_TEST);
932 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
933 // only draw light where this geometry was already rendered AND the
934 // stencil is 128 (values other than this mean shadow)
935 qglStencilFunc(GL_EQUAL, 128, 0xFF);
936 r_shadowstage = SHADOWSTAGE_LIGHT;
940 void R_Shadow_Stage_End(void)
943 memset(&m, 0, sizeof(m));
944 R_Mesh_State_Texture(&m);
945 GL_BlendFunc(GL_ONE, GL_ZERO);
948 qglPolygonOffset(0, 0);
949 //qglDisable(GL_POLYGON_OFFSET_FILL);
950 GL_Color(1, 1, 1, 1);
951 qglColorMask(1, 1, 1, 1);
952 qglDisable(GL_SCISSOR_TEST);
953 qglDepthFunc(GL_LEQUAL);
954 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
955 qglDisable(GL_STENCIL_TEST);
956 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
957 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
958 r_shadowstage = SHADOWSTAGE_NONE;
961 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
963 int i, ix1, iy1, ix2, iy2;
964 float x1, y1, x2, y2, x, y, f;
967 if (!r_shadow_scissor.integer)
969 // if view is inside the box, just say yes it's visible
970 // LordHavoc: for some odd reason scissor seems broken without stencil
971 // (?!? seems like a driver bug) so abort if gl_stencil is false
972 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
974 qglDisable(GL_SCISSOR_TEST);
977 for (i = 0;i < 3;i++)
979 if (r_viewforward[i] >= 0)
990 f = DotProduct(r_viewforward, r_vieworigin) + 1;
991 if (DotProduct(r_viewforward, v2) <= f)
993 // entirely behind nearclip plane
996 if (DotProduct(r_viewforward, v) >= f)
998 // entirely infront of nearclip plane
999 x1 = y1 = x2 = y2 = 0;
1000 for (i = 0;i < 8;i++)
1002 v[0] = (i & 1) ? mins[0] : maxs[0];
1003 v[1] = (i & 2) ? mins[1] : maxs[1];
1004 v[2] = (i & 4) ? mins[2] : maxs[2];
1006 GL_TransformToScreen(v, v2);
1007 //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]);
1026 // clipped by nearclip plane
1027 // this is nasty and crude...
1028 // create viewspace bbox
1029 for (i = 0;i < 8;i++)
1031 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1032 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1033 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1034 v2[0] = -DotProduct(v, r_viewleft);
1035 v2[1] = DotProduct(v, r_viewup);
1036 v2[2] = DotProduct(v, r_viewforward);
1039 if (smins[0] > v2[0]) smins[0] = v2[0];
1040 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1041 if (smins[1] > v2[1]) smins[1] = v2[1];
1042 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1043 if (smins[2] > v2[2]) smins[2] = v2[2];
1044 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1048 smins[0] = smaxs[0] = v2[0];
1049 smins[1] = smaxs[1] = v2[1];
1050 smins[2] = smaxs[2] = v2[2];
1053 // now we have a bbox in viewspace
1054 // clip it to the view plane
1057 // return true if that culled the box
1058 if (smins[2] >= smaxs[2])
1060 // ok some of it is infront of the view, transform each corner back to
1061 // worldspace and then to screenspace and make screen rect
1062 // initialize these variables just to avoid compiler warnings
1063 x1 = y1 = x2 = y2 = 0;
1064 for (i = 0;i < 8;i++)
1066 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1067 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1068 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1069 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1070 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1071 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1073 GL_TransformToScreen(v, v2);
1074 //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]);
1091 // this code doesn't handle boxes with any points behind view properly
1092 x1 = 1000;x2 = -1000;
1093 y1 = 1000;y2 = -1000;
1094 for (i = 0;i < 8;i++)
1096 v[0] = (i & 1) ? mins[0] : maxs[0];
1097 v[1] = (i & 2) ? mins[1] : maxs[1];
1098 v[2] = (i & 4) ? mins[2] : maxs[2];
1100 GL_TransformToScreen(v, v2);
1101 //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]);
1119 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1120 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1121 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1122 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1123 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1124 if (ix2 <= ix1 || iy2 <= iy1)
1126 // set up the scissor rectangle
1127 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1128 qglEnable(GL_SCISSOR_TEST);
1133 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1135 float *color4f = varray_color4f;
1136 float dist, dot, intensity, v[3], n[3];
1137 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1139 Matrix4x4_Transform(m, vertex3f, v);
1140 if ((dist = DotProduct(v, v)) < 1)
1142 Matrix4x4_Transform3x3(m, normal3f, n);
1143 if ((dot = DotProduct(n, v)) > 0)
1146 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1147 VectorScale(lightcolor, intensity, color4f);
1152 VectorClear(color4f);
1158 VectorClear(color4f);
1164 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1166 float *color4f = varray_color4f;
1167 float dist, dot, intensity, v[3], n[3];
1168 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1170 Matrix4x4_Transform(m, vertex3f, v);
1171 if ((dist = fabs(v[2])) < 1)
1173 Matrix4x4_Transform3x3(m, normal3f, n);
1174 if ((dot = DotProduct(n, v)) > 0)
1176 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1177 VectorScale(lightcolor, intensity, color4f);
1182 VectorClear(color4f);
1188 VectorClear(color4f);
1194 // FIXME: this should be done in a vertex program when possible
1195 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1196 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1200 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1201 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1202 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1209 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1213 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1214 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1221 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)
1225 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1227 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1228 // the cubemap normalizes this for us
1229 out3f[0] = DotProduct(svector3f, lightdir);
1230 out3f[1] = DotProduct(tvector3f, lightdir);
1231 out3f[2] = DotProduct(normal3f, lightdir);
1235 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)
1238 float lightdir[3], eyedir[3], halfdir[3];
1239 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1241 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1242 VectorNormalizeFast(lightdir);
1243 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1244 VectorNormalizeFast(eyedir);
1245 VectorAdd(lightdir, eyedir, halfdir);
1246 // the cubemap normalizes this for us
1247 out3f[0] = DotProduct(svector3f, halfdir);
1248 out3f[1] = DotProduct(tvector3f, halfdir);
1249 out3f[2] = DotProduct(normal3f, halfdir);
1253 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)
1256 float color[3], color2[3];
1258 GL_VertexPointer(vertex3f);
1259 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1262 bumptexture = r_shadow_blankbumptexture;
1264 // colorscale accounts for how much we multiply the brightness during combine
1265 // mult is how many times the final pass of the lighting will be
1266 // performed to get more brightness than otherwise possible
1267 // limit mult to 64 for sanity sake
1268 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1270 // 3/2 3D combine path (Geforce3, Radeon 8500)
1271 memset(&m, 0, sizeof(m));
1272 m.tex[0] = R_GetTexture(bumptexture);
1273 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1274 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1275 m.texcombinergb[0] = GL_REPLACE;
1276 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1277 m.pointer_texcoord[0] = texcoord2f;
1278 m.pointer_texcoord[1] = varray_texcoord3f[1];
1279 m.pointer_texcoord[2] = varray_texcoord3f[2];
1280 R_Mesh_State_Texture(&m);
1281 qglColorMask(0,0,0,1);
1282 GL_BlendFunc(GL_ONE, GL_ZERO);
1283 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1284 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1285 R_Mesh_Draw(numverts, numtriangles, elements);
1287 c_rt_lighttris += numtriangles;
1289 memset(&m, 0, sizeof(m));
1290 m.tex[0] = R_GetTexture(basetexture);
1291 m.texcubemap[1] = R_GetTexture(lightcubemap);
1292 m.pointer_texcoord[0] = texcoord2f;
1293 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1294 R_Mesh_State_Texture(&m);
1295 qglColorMask(1,1,1,0);
1296 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1298 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1299 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1300 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1302 color[0] = bound(0, color2[0], 1);
1303 color[1] = bound(0, color2[1], 1);
1304 color[2] = bound(0, color2[2], 1);
1305 GL_Color(color[0], color[1], color[2], 1);
1306 R_Mesh_Draw(numverts, numtriangles, elements);
1308 c_rt_lighttris += numtriangles;
1311 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1313 // 1/2/2 3D combine path (original Radeon)
1314 memset(&m, 0, sizeof(m));
1315 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1316 m.pointer_texcoord[0] = varray_texcoord3f[0];
1317 R_Mesh_State_Texture(&m);
1318 qglColorMask(0,0,0,1);
1319 GL_BlendFunc(GL_ONE, GL_ZERO);
1320 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1321 R_Mesh_Draw(numverts, numtriangles, elements);
1323 c_rt_lighttris += numtriangles;
1325 memset(&m, 0, sizeof(m));
1326 m.tex[0] = R_GetTexture(bumptexture);
1327 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1328 m.texcombinergb[0] = GL_REPLACE;
1329 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1330 m.pointer_texcoord[0] = texcoord2f;
1331 m.pointer_texcoord[1] = varray_texcoord3f[1];
1332 R_Mesh_State_Texture(&m);
1333 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1334 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1335 R_Mesh_Draw(numverts, numtriangles, elements);
1337 c_rt_lighttris += numtriangles;
1339 memset(&m, 0, sizeof(m));
1340 m.tex[0] = R_GetTexture(basetexture);
1341 m.texcubemap[1] = R_GetTexture(lightcubemap);
1342 m.pointer_texcoord[0] = texcoord2f;
1343 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1344 R_Mesh_State_Texture(&m);
1345 qglColorMask(1,1,1,0);
1346 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1348 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1349 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1350 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1352 color[0] = bound(0, color2[0], 1);
1353 color[1] = bound(0, color2[1], 1);
1354 color[2] = bound(0, color2[2], 1);
1355 GL_Color(color[0], color[1], color[2], 1);
1356 R_Mesh_Draw(numverts, numtriangles, elements);
1358 c_rt_lighttris += numtriangles;
1361 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1363 // 2/2 3D combine path (original Radeon)
1364 memset(&m, 0, sizeof(m));
1365 m.tex[0] = R_GetTexture(bumptexture);
1366 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1367 m.texcombinergb[0] = GL_REPLACE;
1368 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1369 m.pointer_texcoord[0] = texcoord2f;
1370 m.pointer_texcoord[1] = varray_texcoord3f[1];
1371 R_Mesh_State_Texture(&m);
1372 qglColorMask(0,0,0,1);
1373 GL_BlendFunc(GL_ONE, GL_ZERO);
1374 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1375 R_Mesh_Draw(numverts, numtriangles, elements);
1377 c_rt_lighttris += numtriangles;
1379 memset(&m, 0, sizeof(m));
1380 m.tex[0] = R_GetTexture(basetexture);
1381 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1382 m.pointer_texcoord[0] = texcoord2f;
1383 m.pointer_texcoord[1] = varray_texcoord3f[1];
1384 R_Mesh_State_Texture(&m);
1385 qglColorMask(1,1,1,0);
1386 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1387 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1388 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1389 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1391 color[0] = bound(0, color2[0], 1);
1392 color[1] = bound(0, color2[1], 1);
1393 color[2] = bound(0, color2[2], 1);
1394 GL_Color(color[0], color[1], color[2], 1);
1395 R_Mesh_Draw(numverts, numtriangles, elements);
1397 c_rt_lighttris += numtriangles;
1400 else if (r_textureunits.integer >= 4)
1402 // 4/2 2D combine path (Geforce3, Radeon 8500)
1403 memset(&m, 0, sizeof(m));
1404 m.tex[0] = R_GetTexture(bumptexture);
1405 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1406 m.texcombinergb[0] = GL_REPLACE;
1407 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1408 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1409 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1410 m.pointer_texcoord[0] = texcoord2f;
1411 m.pointer_texcoord[1] = varray_texcoord3f[1];
1412 m.pointer_texcoord[2] = varray_texcoord2f[2];
1413 m.pointer_texcoord[3] = varray_texcoord2f[3];
1414 R_Mesh_State_Texture(&m);
1415 qglColorMask(0,0,0,1);
1416 GL_BlendFunc(GL_ONE, GL_ZERO);
1417 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1418 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1419 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1420 R_Mesh_Draw(numverts, numtriangles, elements);
1422 c_rt_lighttris += numtriangles;
1424 memset(&m, 0, sizeof(m));
1425 m.tex[0] = R_GetTexture(basetexture);
1426 m.texcubemap[1] = R_GetTexture(lightcubemap);
1427 m.pointer_texcoord[0] = texcoord2f;
1428 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1429 R_Mesh_State_Texture(&m);
1430 qglColorMask(1,1,1,0);
1431 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1433 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1434 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1435 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1437 color[0] = bound(0, color2[0], 1);
1438 color[1] = bound(0, color2[1], 1);
1439 color[2] = bound(0, color2[2], 1);
1440 GL_Color(color[0], color[1], color[2], 1);
1441 R_Mesh_Draw(numverts, numtriangles, elements);
1443 c_rt_lighttris += numtriangles;
1448 // 2/2/2 2D combine path (any dot3 card)
1449 memset(&m, 0, sizeof(m));
1450 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1451 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1452 m.pointer_texcoord[0] = varray_texcoord2f[0];
1453 m.pointer_texcoord[1] = varray_texcoord2f[1];
1454 R_Mesh_State_Texture(&m);
1455 qglColorMask(0,0,0,1);
1456 GL_BlendFunc(GL_ONE, GL_ZERO);
1457 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1458 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1459 R_Mesh_Draw(numverts, numtriangles, elements);
1461 c_rt_lighttris += numtriangles;
1463 memset(&m, 0, sizeof(m));
1464 m.tex[0] = R_GetTexture(bumptexture);
1465 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1466 m.texcombinergb[0] = GL_REPLACE;
1467 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1468 m.pointer_texcoord[0] = texcoord2f;
1469 m.pointer_texcoord[1] = varray_texcoord3f[1];
1470 R_Mesh_State_Texture(&m);
1471 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1472 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1473 R_Mesh_Draw(numverts, numtriangles, elements);
1475 c_rt_lighttris += numtriangles;
1477 memset(&m, 0, sizeof(m));
1478 m.tex[0] = R_GetTexture(basetexture);
1479 m.texcubemap[1] = R_GetTexture(lightcubemap);
1480 m.pointer_texcoord[0] = texcoord2f;
1481 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1482 R_Mesh_State_Texture(&m);
1483 qglColorMask(1,1,1,0);
1484 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1486 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1487 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1488 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1490 color[0] = bound(0, color2[0], 1);
1491 color[1] = bound(0, color2[1], 1);
1492 color[2] = bound(0, color2[2], 1);
1493 GL_Color(color[0], color[1], color[2], 1);
1494 R_Mesh_Draw(numverts, numtriangles, elements);
1496 c_rt_lighttris += numtriangles;
1502 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1503 GL_DepthMask(false);
1505 GL_ColorPointer(varray_color4f);
1506 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1507 memset(&m, 0, sizeof(m));
1508 m.tex[0] = R_GetTexture(basetexture);
1509 m.pointer_texcoord[0] = texcoord2f;
1510 if (r_textureunits.integer >= 2)
1513 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1514 m.pointer_texcoord[1] = varray_texcoord2f[1];
1515 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1517 R_Mesh_State_Texture(&m);
1518 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1520 color[0] = bound(0, color2[0], 1);
1521 color[1] = bound(0, color2[1], 1);
1522 color[2] = bound(0, color2[2], 1);
1523 if (r_textureunits.integer >= 2)
1524 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1526 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1527 R_Mesh_Draw(numverts, numtriangles, elements);
1529 c_rt_lighttris += numtriangles;
1534 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)
1537 float color[3], color2[3], colorscale;
1539 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1542 glosstexture = r_shadow_blankglosstexture;
1543 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1545 colorscale = r_shadow_glossintensity.value;
1547 bumptexture = r_shadow_blankbumptexture;
1548 if (glosstexture == r_shadow_blankglosstexture)
1549 colorscale *= r_shadow_gloss2intensity.value;
1550 GL_VertexPointer(vertex3f);
1552 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1554 // 2/0/0/1/2 3D combine blendsquare path
1555 memset(&m, 0, sizeof(m));
1556 m.tex[0] = R_GetTexture(bumptexture);
1557 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1558 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1559 m.pointer_texcoord[0] = texcoord2f;
1560 m.pointer_texcoord[1] = varray_texcoord3f[1];
1561 R_Mesh_State_Texture(&m);
1562 qglColorMask(0,0,0,1);
1563 // this squares the result
1564 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1565 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1566 R_Mesh_Draw(numverts, numtriangles, elements);
1568 c_rt_lighttris += numtriangles;
1570 memset(&m, 0, sizeof(m));
1571 R_Mesh_State_Texture(&m);
1572 // square alpha in framebuffer a few times to make it shiny
1573 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1574 // these comments are a test run through this math for intensity 0.5
1575 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1576 // 0.25 * 0.25 = 0.0625 (this is another pass)
1577 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1578 R_Mesh_Draw(numverts, numtriangles, elements);
1580 c_rt_lighttris += numtriangles;
1581 R_Mesh_Draw(numverts, numtriangles, elements);
1583 c_rt_lighttris += numtriangles;
1585 memset(&m, 0, sizeof(m));
1586 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1587 m.pointer_texcoord[0] = varray_texcoord3f[0];
1588 R_Mesh_State_Texture(&m);
1589 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1590 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1591 R_Mesh_Draw(numverts, numtriangles, elements);
1593 c_rt_lighttris += numtriangles;
1595 memset(&m, 0, sizeof(m));
1596 m.tex[0] = R_GetTexture(glosstexture);
1597 m.texcubemap[1] = R_GetTexture(lightcubemap);
1598 m.pointer_texcoord[0] = texcoord2f;
1599 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1600 R_Mesh_State_Texture(&m);
1601 qglColorMask(1,1,1,0);
1602 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1604 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1605 VectorScale(lightcolor, colorscale, color2);
1606 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1608 color[0] = bound(0, color2[0], 1);
1609 color[1] = bound(0, color2[1], 1);
1610 color[2] = bound(0, color2[2], 1);
1611 GL_Color(color[0], color[1], color[2], 1);
1612 R_Mesh_Draw(numverts, numtriangles, elements);
1614 c_rt_lighttris += numtriangles;
1617 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1619 // 2/0/0/2 3D combine blendsquare path
1620 memset(&m, 0, sizeof(m));
1621 m.tex[0] = R_GetTexture(bumptexture);
1622 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1623 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1624 m.pointer_texcoord[0] = texcoord2f;
1625 m.pointer_texcoord[1] = varray_texcoord3f[1];
1626 R_Mesh_State_Texture(&m);
1627 qglColorMask(0,0,0,1);
1628 // this squares the result
1629 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1630 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1631 R_Mesh_Draw(numverts, numtriangles, elements);
1633 c_rt_lighttris += numtriangles;
1635 memset(&m, 0, sizeof(m));
1636 R_Mesh_State_Texture(&m);
1637 // square alpha in framebuffer a few times to make it shiny
1638 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1639 // these comments are a test run through this math for intensity 0.5
1640 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1641 // 0.25 * 0.25 = 0.0625 (this is another pass)
1642 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1643 R_Mesh_Draw(numverts, numtriangles, elements);
1645 c_rt_lighttris += numtriangles;
1646 R_Mesh_Draw(numverts, numtriangles, elements);
1648 c_rt_lighttris += numtriangles;
1650 memset(&m, 0, sizeof(m));
1651 m.tex[0] = R_GetTexture(glosstexture);
1652 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1653 m.pointer_texcoord[0] = texcoord2f;
1654 m.pointer_texcoord[1] = varray_texcoord3f[1];
1655 R_Mesh_State_Texture(&m);
1656 qglColorMask(1,1,1,0);
1657 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1658 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1659 VectorScale(lightcolor, colorscale, color2);
1660 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1662 color[0] = bound(0, color2[0], 1);
1663 color[1] = bound(0, color2[1], 1);
1664 color[2] = bound(0, color2[2], 1);
1665 GL_Color(color[0], color[1], color[2], 1);
1666 R_Mesh_Draw(numverts, numtriangles, elements);
1668 c_rt_lighttris += numtriangles;
1671 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1673 // 2/0/0/2/2 2D combine blendsquare path
1674 memset(&m, 0, sizeof(m));
1675 m.tex[0] = R_GetTexture(bumptexture);
1676 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1677 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1678 m.pointer_texcoord[0] = texcoord2f;
1679 m.pointer_texcoord[1] = varray_texcoord3f[1];
1680 R_Mesh_State_Texture(&m);
1681 qglColorMask(0,0,0,1);
1682 // this squares the result
1683 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1684 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1685 R_Mesh_Draw(numverts, numtriangles, elements);
1687 c_rt_lighttris += numtriangles;
1689 memset(&m, 0, sizeof(m));
1690 R_Mesh_State_Texture(&m);
1691 // square alpha in framebuffer a few times to make it shiny
1692 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1693 // these comments are a test run through this math for intensity 0.5
1694 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1695 // 0.25 * 0.25 = 0.0625 (this is another pass)
1696 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1697 R_Mesh_Draw(numverts, numtriangles, elements);
1699 c_rt_lighttris += numtriangles;
1700 R_Mesh_Draw(numverts, numtriangles, elements);
1702 c_rt_lighttris += numtriangles;
1704 memset(&m, 0, sizeof(m));
1705 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1706 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1707 m.pointer_texcoord[0] = varray_texcoord2f[0];
1708 m.pointer_texcoord[1] = varray_texcoord2f[1];
1709 R_Mesh_State_Texture(&m);
1710 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1711 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1712 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1713 R_Mesh_Draw(numverts, numtriangles, elements);
1715 c_rt_lighttris += numtriangles;
1717 memset(&m, 0, sizeof(m));
1718 m.tex[0] = R_GetTexture(glosstexture);
1719 m.texcubemap[1] = R_GetTexture(lightcubemap);
1720 m.pointer_texcoord[0] = texcoord2f;
1721 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1722 R_Mesh_State_Texture(&m);
1723 qglColorMask(1,1,1,0);
1724 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1726 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1727 VectorScale(lightcolor, colorscale, color2);
1728 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1730 color[0] = bound(0, color2[0], 1);
1731 color[1] = bound(0, color2[1], 1);
1732 color[2] = bound(0, color2[2], 1);
1733 GL_Color(color[0], color[1], color[2], 1);
1734 R_Mesh_Draw(numverts, numtriangles, elements);
1736 c_rt_lighttris += numtriangles;
1742 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1744 R_Mesh_Matrix(matrix);
1745 if (r_shadow_showtris.integer)
1749 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1750 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1751 qglDisable(GL_DEPTH_TEST);
1752 qglDisable(GL_STENCIL_TEST);
1753 //qglDisable(GL_CULL_FACE);
1754 qglColorMask(1,1,1,1);
1755 memset(&m, 0, sizeof(m));
1756 R_Mesh_State_Texture(&m);
1757 GL_Color(0,0.1,0,1);
1758 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1759 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1761 GL_VertexPointer(mesh->vertex3f);
1762 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1764 //qglEnable(GL_CULL_FACE);
1766 qglEnable(GL_DEPTH_TEST);
1769 qglEnable(GL_STENCIL_TEST);
1770 qglColorMask(0,0,0,0);
1773 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1776 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)
1779 R_Mesh_Matrix(matrix);
1780 if (r_shadow_showtris.integer)
1783 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1784 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1785 qglDisable(GL_DEPTH_TEST);
1786 qglDisable(GL_STENCIL_TEST);
1787 //qglDisable(GL_CULL_FACE);
1788 memset(&m, 0, sizeof(m));
1789 R_Mesh_State_Texture(&m);
1790 GL_Color(0.2,0,0,1);
1791 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1792 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1794 GL_VertexPointer(mesh->vertex3f);
1795 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1797 //qglEnable(GL_CULL_FACE);
1799 qglEnable(GL_DEPTH_TEST);
1801 qglEnable(GL_STENCIL_TEST);
1803 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1805 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);
1806 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);
1810 cvar_t r_editlights = {0, "r_editlights", "0"};
1811 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1812 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1813 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1814 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1815 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1816 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1817 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1818 worldlight_t *r_shadow_worldlightchain;
1819 worldlight_t *r_shadow_selectedlight;
1820 vec3_t r_editlights_cursorlocation;
1822 static int lightpvsbytes;
1823 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1825 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1827 int i, j, k, l, maxverts = 256, tris;
1828 float *vertex3f = NULL, mins[3], maxs[3];
1830 shadowmesh_t *mesh, *castmesh = NULL;
1832 if (radius < 15 || DotProduct(color, color) < 0.03)
1834 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1838 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1839 VectorCopy(origin, e->origin);
1840 VectorCopy(color, e->light);
1841 e->lightradius = radius;
1843 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1845 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1848 e->castshadows = castshadow;
1850 e->cullradius = e->lightradius;
1851 for (k = 0;k < 3;k++)
1853 mins[k] = e->origin[k] - e->lightradius;
1854 maxs[k] = e->origin[k] + e->lightradius;
1857 e->next = r_shadow_worldlightchain;
1858 r_shadow_worldlightchain = e;
1859 if (cubemapname && cubemapname[0])
1861 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1862 strcpy(e->cubemapname, cubemapname);
1863 // FIXME: add cubemap loading (and don't load a cubemap twice)
1865 // FIXME: rewrite this to store ALL geometry into a cache in the light
1867 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1868 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1871 if (cl.worldmodel->brushq3.num_leafs)
1875 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1876 VectorCopy(e->origin, e->mins);
1877 VectorCopy(e->origin, e->maxs);
1878 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1879 face->lighttemp_castshadow = false;
1880 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1882 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1884 for (k = 0;k < 3;k++)
1886 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1887 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1889 for (j = 0;j < leaf->numleaffaces;j++)
1891 face = leaf->firstleafface[j];
1892 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1893 face->lighttemp_castshadow = true;
1898 // add surfaces to shadow casting mesh and light mesh
1899 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1901 if (face->lighttemp_castshadow)
1903 face->lighttemp_castshadow = false;
1904 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1907 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1908 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1909 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1910 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);
1915 else if (cl.worldmodel->brushq1.numleafs)
1919 VectorCopy(e->origin, e->mins);
1920 VectorCopy(e->origin, e->maxs);
1921 i = CL_PointQ1Contents(e->origin);
1923 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1924 surf->lighttemp_castshadow = false;
1926 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1929 qbyte *bytesurfacepvs;
1931 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1932 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1934 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1936 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1938 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1940 for (k = 0;k < 3;k++)
1942 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1943 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1948 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1949 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1950 surf->lighttemp_castshadow = true;
1952 Mem_Free(byteleafpvs);
1953 Mem_Free(bytesurfacepvs);
1957 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1958 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1960 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1962 for (k = 0;k < 3;k++)
1964 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1965 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1967 for (j = 0;j < leaf->nummarksurfaces;j++)
1969 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1970 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1971 surf->lighttemp_castshadow = true;
1977 // add surfaces to shadow casting mesh and light mesh
1978 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1980 if (surf->lighttemp_castshadow)
1982 surf->lighttemp_castshadow = false;
1983 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1984 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);
1985 if (!(surf->flags & SURF_DRAWSKY))
1986 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);
1992 // limit box to light bounds (in case it grew larger)
1993 for (k = 0;k < 3;k++)
1995 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1996 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1998 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2000 // cast shadow volume from castmesh
2001 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2005 for (mesh = castmesh;mesh;mesh = mesh->next)
2007 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2008 maxverts = max(maxverts, mesh->numverts * 2);
2013 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2014 // now that we have the buffers big enough, construct and add
2015 // the shadow volume mesh
2017 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2018 for (mesh = castmesh;mesh;mesh = mesh->next)
2020 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2021 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)))
2022 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2027 // we're done with castmesh now
2028 Mod_ShadowMesh_Free(castmesh);
2031 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2032 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2035 if (e->meshchain_shadow)
2036 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2037 k += mesh->numtriangles;
2039 if (e->meshchain_light)
2040 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2041 l += mesh->numtriangles;
2042 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);
2045 void R_Shadow_FreeWorldLight(worldlight_t *light)
2047 worldlight_t **lightpointer;
2048 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2049 if (*lightpointer != light)
2050 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2051 *lightpointer = light->next;
2052 if (light->cubemapname)
2053 Mem_Free(light->cubemapname);
2054 if (light->meshchain_shadow)
2055 Mod_ShadowMesh_Free(light->meshchain_shadow);
2056 if (light->meshchain_light)
2057 Mod_ShadowMesh_Free(light->meshchain_light);
2061 void R_Shadow_ClearWorldLights(void)
2063 while (r_shadow_worldlightchain)
2064 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2065 r_shadow_selectedlight = NULL;
2068 void R_Shadow_SelectLight(worldlight_t *light)
2070 if (r_shadow_selectedlight)
2071 r_shadow_selectedlight->selected = false;
2072 r_shadow_selectedlight = light;
2073 if (r_shadow_selectedlight)
2074 r_shadow_selectedlight->selected = true;
2077 rtexture_t *lighttextures[5];
2079 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2081 float scale = r_editlights_cursorgrid.value * 0.5f;
2082 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);
2085 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2088 const worldlight_t *light;
2091 if (light->selected)
2092 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2093 if (!light->meshchain_shadow)
2095 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);
2098 void R_Shadow_DrawLightSprites(void)
2102 worldlight_t *light;
2104 for (i = 0;i < 5;i++)
2106 lighttextures[i] = NULL;
2107 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2108 lighttextures[i] = pic->tex;
2111 for (light = r_shadow_worldlightchain;light;light = light->next)
2112 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2113 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2116 void R_Shadow_SelectLightInView(void)
2118 float bestrating, rating, temp[3];
2119 worldlight_t *best, *light;
2122 for (light = r_shadow_worldlightchain;light;light = light->next)
2124 VectorSubtract(light->origin, r_vieworigin, temp);
2125 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2128 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2129 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2131 bestrating = rating;
2136 R_Shadow_SelectLight(best);
2139 void R_Shadow_LoadWorldLights(void)
2141 int n, a, style, shadow;
2142 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2143 float origin[3], radius, color[3];
2144 if (cl.worldmodel == NULL)
2146 Con_Printf("No map loaded.\n");
2149 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2150 strlcat (name, ".rtlights", sizeof (name));
2151 lightsstring = FS_LoadFile(name, false);
2159 while (*s && *s != '\n')
2165 // check for modifier flags
2171 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);
2177 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);
2180 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2181 radius *= r_editlights_rtlightssizescale.value;
2182 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2187 Con_Printf("invalid rtlights file \"%s\"\n", name);
2188 Mem_Free(lightsstring);
2192 void R_Shadow_SaveWorldLights(void)
2194 worldlight_t *light;
2195 int bufchars, bufmaxchars;
2197 char name[MAX_QPATH];
2199 if (!r_shadow_worldlightchain)
2201 if (cl.worldmodel == NULL)
2203 Con_Printf("No map loaded.\n");
2206 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2207 strlcat (name, ".rtlights", sizeof (name));
2208 bufchars = bufmaxchars = 0;
2210 for (light = r_shadow_worldlightchain;light;light = light->next)
2212 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 : "");
2213 if (bufchars + (int) strlen(line) > bufmaxchars)
2215 bufmaxchars = bufchars + strlen(line) + 2048;
2217 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2221 memcpy(buf, oldbuf, bufchars);
2227 memcpy(buf + bufchars, line, strlen(line));
2228 bufchars += strlen(line);
2232 FS_WriteFile(name, buf, bufchars);
2237 void R_Shadow_LoadLightsFile(void)
2240 char name[MAX_QPATH], *lightsstring, *s, *t;
2241 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2242 if (cl.worldmodel == NULL)
2244 Con_Printf("No map loaded.\n");
2247 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2248 strlcat (name, ".lights", sizeof (name));
2249 lightsstring = FS_LoadFile(name, false);
2257 while (*s && *s != '\n')
2262 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);
2266 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);
2269 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2270 radius = bound(15, radius, 4096);
2271 VectorScale(color, (2.0f / (8388608.0f)), color);
2272 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2277 Con_Printf("invalid lights file \"%s\"\n", name);
2278 Mem_Free(lightsstring);
2282 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2284 int entnum, style, islight;
2285 char key[256], value[1024];
2286 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2289 if (cl.worldmodel == NULL)
2291 Con_Printf("No map loaded.\n");
2294 data = cl.worldmodel->brush.entities;
2297 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2300 origin[0] = origin[1] = origin[2] = 0;
2301 originhack[0] = originhack[1] = originhack[2] = 0;
2302 color[0] = color[1] = color[2] = 1;
2303 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2310 if (!COM_ParseToken(&data, false))
2312 if (com_token[0] == '}')
2313 break; // end of entity
2314 if (com_token[0] == '_')
2315 strcpy(key, com_token + 1);
2317 strcpy(key, com_token);
2318 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2319 key[strlen(key)-1] = 0;
2320 if (!COM_ParseToken(&data, false))
2322 strcpy(value, com_token);
2324 // now that we have the key pair worked out...
2325 if (!strcmp("light", key))
2326 light = atof(value);
2327 else if (!strcmp("origin", key))
2328 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2329 else if (!strcmp("color", key))
2330 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2331 else if (!strcmp("wait", key))
2332 fadescale = atof(value);
2333 else if (!strcmp("classname", key))
2335 if (!strncmp(value, "light", 5))
2338 if (!strcmp(value, "light_fluoro"))
2343 overridecolor[0] = 1;
2344 overridecolor[1] = 1;
2345 overridecolor[2] = 1;
2347 if (!strcmp(value, "light_fluorospark"))
2352 overridecolor[0] = 1;
2353 overridecolor[1] = 1;
2354 overridecolor[2] = 1;
2356 if (!strcmp(value, "light_globe"))
2361 overridecolor[0] = 1;
2362 overridecolor[1] = 0.8;
2363 overridecolor[2] = 0.4;
2365 if (!strcmp(value, "light_flame_large_yellow"))
2370 overridecolor[0] = 1;
2371 overridecolor[1] = 0.5;
2372 overridecolor[2] = 0.1;
2374 if (!strcmp(value, "light_flame_small_yellow"))
2379 overridecolor[0] = 1;
2380 overridecolor[1] = 0.5;
2381 overridecolor[2] = 0.1;
2383 if (!strcmp(value, "light_torch_small_white"))
2388 overridecolor[0] = 1;
2389 overridecolor[1] = 0.5;
2390 overridecolor[2] = 0.1;
2392 if (!strcmp(value, "light_torch_small_walltorch"))
2397 overridecolor[0] = 1;
2398 overridecolor[1] = 0.5;
2399 overridecolor[2] = 0.1;
2403 else if (!strcmp("style", key))
2404 style = atoi(value);
2405 else if (cl.worldmodel->type == mod_brushq3)
2407 if (!strcmp("scale", key))
2408 lightscale = atof(value);
2409 if (!strcmp("fade", key))
2410 fadescale = atof(value);
2413 if (light <= 0 && islight)
2415 if (lightscale <= 0)
2419 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2420 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2421 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2422 VectorCopy(overridecolor, color);
2423 VectorScale(color, light, color);
2424 VectorAdd(origin, originhack, origin);
2426 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2431 void R_Shadow_SetCursorLocationForView(void)
2433 vec_t dist, push, frac;
2434 vec3_t dest, endpos, normal;
2435 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2436 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2439 dist = frac * r_editlights_cursordistance.value;
2440 push = r_editlights_cursorpushback.value;
2444 VectorMA(endpos, push, r_viewforward, endpos);
2445 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2447 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2448 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2449 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2452 void R_Shadow_UpdateWorldLightSelection(void)
2454 if (r_editlights.integer)
2456 R_Shadow_SetCursorLocationForView();
2457 R_Shadow_SelectLightInView();
2458 R_Shadow_DrawLightSprites();
2461 R_Shadow_SelectLight(NULL);
2464 void R_Shadow_EditLights_Clear_f(void)
2466 R_Shadow_ClearWorldLights();
2469 void R_Shadow_EditLights_Reload_f(void)
2471 r_shadow_reloadlights = true;
2474 void R_Shadow_EditLights_Save_f(void)
2477 R_Shadow_SaveWorldLights();
2480 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2482 R_Shadow_ClearWorldLights();
2483 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2486 void R_Shadow_EditLights_ImportLightsFile_f(void)
2488 R_Shadow_ClearWorldLights();
2489 R_Shadow_LoadLightsFile();
2492 void R_Shadow_EditLights_Spawn_f(void)
2495 if (!r_editlights.integer)
2497 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2500 if (Cmd_Argc() != 1)
2502 Con_Printf("r_editlights_spawn does not take parameters\n");
2505 color[0] = color[1] = color[2] = 1;
2506 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2509 void R_Shadow_EditLights_Edit_f(void)
2511 vec3_t origin, color;
2514 char cubemapname[1024];
2515 if (!r_editlights.integer)
2517 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2520 if (!r_shadow_selectedlight)
2522 Con_Printf("No selected light.\n");
2525 VectorCopy(r_shadow_selectedlight->origin, origin);
2526 radius = r_shadow_selectedlight->lightradius;
2527 VectorCopy(r_shadow_selectedlight->light, color);
2528 style = r_shadow_selectedlight->style;
2529 if (r_shadow_selectedlight->cubemapname)
2530 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2533 shadows = r_shadow_selectedlight->castshadows;
2534 if (!strcmp(Cmd_Argv(1), "origin"))
2536 if (Cmd_Argc() != 5)
2538 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2541 origin[0] = atof(Cmd_Argv(2));
2542 origin[1] = atof(Cmd_Argv(3));
2543 origin[2] = atof(Cmd_Argv(4));
2545 else if (!strcmp(Cmd_Argv(1), "originx"))
2547 if (Cmd_Argc() != 3)
2549 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2552 origin[0] = atof(Cmd_Argv(2));
2554 else if (!strcmp(Cmd_Argv(1), "originy"))
2556 if (Cmd_Argc() != 3)
2558 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2561 origin[1] = atof(Cmd_Argv(2));
2563 else if (!strcmp(Cmd_Argv(1), "originz"))
2565 if (Cmd_Argc() != 3)
2567 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2570 origin[2] = atof(Cmd_Argv(2));
2572 else if (!strcmp(Cmd_Argv(1), "move"))
2574 if (Cmd_Argc() != 5)
2576 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2579 origin[0] += atof(Cmd_Argv(2));
2580 origin[1] += atof(Cmd_Argv(3));
2581 origin[2] += atof(Cmd_Argv(4));
2583 else if (!strcmp(Cmd_Argv(1), "movex"))
2585 if (Cmd_Argc() != 3)
2587 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2590 origin[0] += atof(Cmd_Argv(2));
2592 else if (!strcmp(Cmd_Argv(1), "movey"))
2594 if (Cmd_Argc() != 3)
2596 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2599 origin[1] += atof(Cmd_Argv(2));
2601 else if (!strcmp(Cmd_Argv(1), "movez"))
2603 if (Cmd_Argc() != 3)
2605 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2608 origin[2] += atof(Cmd_Argv(2));
2610 else if (!strcmp(Cmd_Argv(1), "color"))
2612 if (Cmd_Argc() != 5)
2614 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2617 color[0] = atof(Cmd_Argv(2));
2618 color[1] = atof(Cmd_Argv(3));
2619 color[2] = atof(Cmd_Argv(4));
2621 else if (!strcmp(Cmd_Argv(1), "radius"))
2623 if (Cmd_Argc() != 3)
2625 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2628 radius = atof(Cmd_Argv(2));
2630 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2632 if (Cmd_Argc() != 3)
2634 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2637 style = atoi(Cmd_Argv(2));
2639 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2643 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2646 if (Cmd_Argc() == 3)
2647 strcpy(cubemapname, Cmd_Argv(2));
2651 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2653 if (Cmd_Argc() != 3)
2655 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2658 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2662 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2663 Con_Printf("Selected light's properties:\n");
2664 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2665 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2666 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2667 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2668 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2669 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2672 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2673 r_shadow_selectedlight = NULL;
2674 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2677 extern int con_vislines;
2678 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2682 if (r_shadow_selectedlight == NULL)
2686 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2687 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;
2688 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2689 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;
2690 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2691 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2692 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;
2695 void R_Shadow_EditLights_ToggleShadow_f(void)
2697 if (!r_editlights.integer)
2699 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2702 if (!r_shadow_selectedlight)
2704 Con_Printf("No selected light.\n");
2707 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);
2708 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2709 r_shadow_selectedlight = NULL;
2712 void R_Shadow_EditLights_Remove_f(void)
2714 if (!r_editlights.integer)
2716 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2719 if (!r_shadow_selectedlight)
2721 Con_Printf("No selected light.\n");
2724 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2725 r_shadow_selectedlight = NULL;
2728 void R_Shadow_EditLights_Help_f(void)
2731 "Documentation on r_editlights system:\n"
2733 "r_editlights : enable/disable editing mode\n"
2734 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2735 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2736 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2737 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2738 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2739 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2740 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2742 "r_editlights_help : this help\n"
2743 "r_editlights_clear : remove all lights\n"
2744 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2745 "r_editlights_save : save to .rtlights file\n"
2746 "r_editlights_spawn : create a light with default settings\n"
2747 "r_editlights_edit command : edit selected light - more documentation below\n"
2748 "r_editlights_remove : remove selected light\n"
2749 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2750 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2751 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2753 "origin x y z : set light location\n"
2754 "originx x: set x component of light location\n"
2755 "originy y: set y component of light location\n"
2756 "originz z: set z component of light location\n"
2757 "move x y z : adjust light location\n"
2758 "movex x: adjust x component of light location\n"
2759 "movey y: adjust y component of light location\n"
2760 "movez z: adjust z component of light location\n"
2761 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2762 "radius radius : set radius (size) of light\n"
2763 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2764 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2765 "shadows 1/0 : turn on/off shadows\n"
2766 "<nothing> : print light properties to console\n"
2770 void R_Shadow_EditLights_Init(void)
2772 Cvar_RegisterVariable(&r_editlights);
2773 Cvar_RegisterVariable(&r_editlights_cursordistance);
2774 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2775 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2776 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2777 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2778 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2779 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2780 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2781 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2782 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2783 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2784 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2785 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2786 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2787 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2788 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2789 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);