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"
120 extern void R_Shadow_EditLights_Init(void);
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_ERASESTENCIL 3
127 int r_shadowstage = SHADOWSTAGE_NONE;
128 int r_shadow_reloadlights = false;
130 mempool_t *r_shadow_mempool;
132 int maxshadowelements;
134 int maxtrianglefacinglight;
135 qbyte *trianglefacinglight;
136 int *trianglefacinglightlist;
143 rtexturepool_t *r_shadow_texturepool;
144 rtexture_t *r_shadow_normalcubetexture;
145 rtexture_t *r_shadow_attenuation2dtexture;
146 rtexture_t *r_shadow_attenuation3dtexture;
147 rtexture_t *r_shadow_blankbumptexture;
148 rtexture_t *r_shadow_blankglosstexture;
149 rtexture_t *r_shadow_blankwhitetexture;
151 // used only for light filters (cubemaps)
152 rtexturepool_t *r_shadow_filters_texturepool;
154 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
155 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
156 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
157 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
158 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
159 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
160 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
161 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
162 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
163 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
164 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
165 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
166 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
167 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
168 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
169 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
170 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
171 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
172 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
173 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
174 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
175 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
176 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
177 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
178 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
180 int c_rt_lights, c_rt_clears, c_rt_scissored;
181 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
182 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
184 void R_Shadow_ClearWorldLights(void);
185 void R_Shadow_SaveWorldLights(void);
186 void R_Shadow_LoadWorldLights(void);
187 void R_Shadow_LoadLightsFile(void);
188 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
190 void r_shadow_start(void)
192 // allocate vertex processing arrays
193 r_shadow_mempool = Mem_AllocPool("R_Shadow");
194 maxshadowelements = 0;
195 shadowelements = NULL;
200 maxtrianglefacinglight = 0;
201 trianglefacinglight = NULL;
202 trianglefacinglightlist = NULL;
203 r_shadow_normalcubetexture = NULL;
204 r_shadow_attenuation2dtexture = NULL;
205 r_shadow_attenuation3dtexture = NULL;
206 r_shadow_blankbumptexture = NULL;
207 r_shadow_blankglosstexture = NULL;
208 r_shadow_blankwhitetexture = NULL;
209 r_shadow_texturepool = NULL;
210 r_shadow_filters_texturepool = NULL;
211 R_Shadow_ClearWorldLights();
212 r_shadow_reloadlights = true;
215 void r_shadow_shutdown(void)
217 R_Shadow_ClearWorldLights();
218 r_shadow_reloadlights = true;
219 r_shadow_normalcubetexture = NULL;
220 r_shadow_attenuation2dtexture = NULL;
221 r_shadow_attenuation3dtexture = NULL;
222 r_shadow_blankbumptexture = NULL;
223 r_shadow_blankglosstexture = NULL;
224 r_shadow_blankwhitetexture = NULL;
225 R_FreeTexturePool(&r_shadow_texturepool);
226 R_FreeTexturePool(&r_shadow_filters_texturepool);
227 maxshadowelements = 0;
228 shadowelements = NULL;
233 maxtrianglefacinglight = 0;
234 trianglefacinglight = NULL;
235 trianglefacinglightlist = NULL;
236 Mem_FreePool(&r_shadow_mempool);
239 void r_shadow_newmap(void)
241 R_Shadow_ClearWorldLights();
242 r_shadow_reloadlights = true;
245 void R_Shadow_Help_f(void)
248 "Documentation on r_shadow system:\n"
250 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
251 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
252 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
253 "r_shadow_realtime_world : use realtime world light rendering\n"
254 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
255 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
256 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
257 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
258 "r_shadow_glossintensity : brightness of textured gloss\n"
259 "r_shadow_gloss2intensity : brightness of forced gloss\n"
260 "r_shadow_debuglight : render only this light number (-1 = all)\n"
261 "r_shadow_scissor : use scissor optimization\n"
262 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
263 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
264 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
265 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
266 "r_shadow_portallight : use portal visibility for static light precomputation\n"
267 "r_shadow_projectdistance : shadow volume projection distance\n"
268 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
269 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
270 "r_shadow_worldshadows : enable world shadows\n"
271 "r_shadow_dlightshadows : enable dlight shadows\n"
273 "r_shadow_help : this help\n"
277 void R_Shadow_Init(void)
279 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
280 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
281 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
282 Cvar_RegisterVariable(&r_shadow_realtime_world);
283 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
284 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
285 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
286 Cvar_RegisterVariable(&r_shadow_gloss);
287 Cvar_RegisterVariable(&r_shadow_glossintensity);
288 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
289 Cvar_RegisterVariable(&r_shadow_debuglight);
290 Cvar_RegisterVariable(&r_shadow_scissor);
291 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
292 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
293 Cvar_RegisterVariable(&r_shadow_polygonfactor);
294 Cvar_RegisterVariable(&r_shadow_polygonoffset);
295 Cvar_RegisterVariable(&r_shadow_portallight);
296 Cvar_RegisterVariable(&r_shadow_projectdistance);
297 Cvar_RegisterVariable(&r_shadow_texture3d);
298 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
299 Cvar_RegisterVariable(&r_shadow_worldshadows);
300 Cvar_RegisterVariable(&r_shadow_dlightshadows);
301 Cvar_RegisterVariable(&r_shadow_showtris);
302 Cvar_RegisterVariable(&r_shadow_staticworldlights);
303 Cvar_RegisterVariable(&r_shadow_cull);
304 if (gamemode == GAME_TENEBRAE)
306 Cvar_SetValue("r_shadow_gloss", 2);
307 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
309 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
310 R_Shadow_EditLights_Init();
311 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
314 matrix4x4_t matrix_attenuationxyz =
317 {0.5, 0.0, 0.0, 0.5},
318 {0.0, 0.5, 0.0, 0.5},
319 {0.0, 0.0, 0.5, 0.5},
324 matrix4x4_t matrix_attenuationz =
327 {0.0, 0.0, 0.5, 0.5},
328 {0.0, 0.0, 0.0, 0.0},
329 {0.0, 0.0, 0.0, 0.0},
334 void R_Shadow_ResizeTriangleFacingLight(int numtris)
336 // make sure trianglefacinglight is big enough for this volume
337 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
338 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
339 if (maxtrianglefacinglight < numtris)
341 maxtrianglefacinglight = numtris;
342 if (trianglefacinglight)
343 Mem_Free(trianglefacinglight);
344 if (trianglefacinglightlist)
345 Mem_Free(trianglefacinglightlist);
346 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
347 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
351 int *R_Shadow_ResizeShadowElements(int numtris)
353 // make sure shadowelements is big enough for this volume
354 if (maxshadowelements < numtris * 24)
356 maxshadowelements = numtris * 24;
358 Mem_Free(shadowelements);
359 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
361 return shadowelements;
365 // readable version of some code found below
366 //if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
367 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
369 float dir0[3], dir1[3], normal[3];
371 // calculate two mostly perpendicular edge directions
372 VectorSubtract(a, b, dir0);
373 VectorSubtract(c, b, dir1);
375 // we have two edge directions, we can calculate a third vector from
376 // them, which is the direction of the surface normal (it's magnitude
378 CrossProduct(dir0, dir1, normal);
380 // compare distance of light along normal, with distance of any point
381 // of the triangle along the same normal (the triangle is planar,
382 // I.E. flat, so all points give the same answer)
383 return DotProduct(p, normal) > DotProduct(a, normal);
385 int checkcastshadowfromedge(int t, int i)
389 if (t >= trianglerange_start && t < trianglerange_end)
391 if (t < i && !trianglefacinglight[t])
402 te = inelement3i + t * 3;
403 v[0] = invertex3f + te[0] * 3;
404 v[1] = invertex3f + te[1] * 3;
405 v[2] = invertex3f + te[2] * 3;
406 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
415 int R_Shadow_ConstructShadowVolume(int innumvertices, int trianglerange_start, int trianglerange_end, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *relativelightorigin, float projectdistance)
417 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
419 const int *e, *n, *te;
422 // make sure trianglefacinglight is big enough for this volume
423 if (maxtrianglefacinglight < trianglerange_end)
424 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
426 if (maxvertexupdate < innumvertices)
428 maxvertexupdate = innumvertices;
430 Mem_Free(vertexupdate);
432 Mem_Free(vertexremap);
433 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
434 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
438 if (r_shadow_singlepassvolumegeneration.integer)
440 // one pass approach (identify lit/dark faces and generate sides while doing so)
441 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
443 // calculate triangle facing flag
444 v[0] = invertex3f + e[0] * 3;
445 v[1] = invertex3f + e[1] * 3;
446 v[2] = invertex3f + e[2] * 3;
447 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
449 // make sure the vertices are created
450 for (j = 0;j < 3;j++)
452 if (vertexupdate[e[j]] != vertexupdatenum)
454 vertexupdate[e[j]] = vertexupdatenum;
455 vertexremap[e[j]] = outvertices;
456 VectorCopy(v[j], outvertex3f);
457 VectorSubtract(v[j], relativelightorigin, temp);
458 f = projectdistance / VectorLength(temp);
459 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
464 // output the front and back triangles
465 vr[0] = vertexremap[e[0]];
466 vr[1] = vertexremap[e[1]];
467 vr[2] = vertexremap[e[2]];
468 outelement3i[0] = vr[0];
469 outelement3i[1] = vr[1];
470 outelement3i[2] = vr[2];
471 outelement3i[3] = vr[2] + 1;
472 outelement3i[4] = vr[1] + 1;
473 outelement3i[5] = vr[0] + 1;
476 // output the sides (facing outward from this triangle)
478 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
480 outelement3i[0] = vr[1];
481 outelement3i[1] = vr[0];
482 outelement3i[2] = vr[0] + 1;
483 outelement3i[3] = vr[1];
484 outelement3i[4] = vr[0] + 1;
485 outelement3i[5] = vr[1] + 1;
490 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
492 outelement3i[0] = vr[2];
493 outelement3i[1] = vr[1];
494 outelement3i[2] = vr[1] + 1;
495 outelement3i[3] = vr[2];
496 outelement3i[4] = vr[1] + 1;
497 outelement3i[5] = vr[2] + 1;
502 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
504 outelement3i[0] = vr[0];
505 outelement3i[1] = vr[2];
506 outelement3i[2] = vr[2] + 1;
507 outelement3i[3] = vr[0];
508 outelement3i[4] = vr[2] + 1;
509 outelement3i[5] = vr[0] + 1;
516 // this triangle is not facing the light
517 // output the sides (facing inward to this triangle)
519 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
521 vr[0] = vertexremap[e[0]];
522 vr[1] = vertexremap[e[1]];
523 outelement3i[0] = vr[1];
524 outelement3i[1] = vr[0] + 1;
525 outelement3i[2] = vr[0];
526 outelement3i[3] = vr[1];
527 outelement3i[4] = vr[1] + 1;
528 outelement3i[5] = vr[0] + 1;
533 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
535 vr[1] = vertexremap[e[1]];
536 vr[2] = vertexremap[e[2]];
537 outelement3i[0] = vr[2];
538 outelement3i[1] = vr[1] + 1;
539 outelement3i[2] = vr[1];
540 outelement3i[3] = vr[2];
541 outelement3i[4] = vr[2] + 1;
542 outelement3i[5] = vr[1] + 1;
547 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
549 vr[0] = vertexremap[e[0]];
550 vr[2] = vertexremap[e[2]];
551 outelement3i[0] = vr[0];
552 outelement3i[1] = vr[2] + 1;
553 outelement3i[2] = vr[2];
554 outelement3i[3] = vr[0];
555 outelement3i[4] = vr[0] + 1;
556 outelement3i[5] = vr[2] + 1;
565 // two pass approach (identify lit/dark faces and then generate sides)
566 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
568 // calculate triangle facing flag
569 v[0] = invertex3f + e[0] * 3;
570 v[1] = invertex3f + e[1] * 3;
571 v[2] = invertex3f + e[2] * 3;
572 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
574 trianglefacinglightlist[numfacing++] = i;
575 // make sure the vertices are created
576 for (j = 0;j < 3;j++)
578 if (vertexupdate[e[j]] != vertexupdatenum)
580 vertexupdate[e[j]] = vertexupdatenum;
581 vertexremap[e[j]] = outvertices;
582 VectorSubtract(v[j], relativelightorigin, temp);
583 f = projectdistance / VectorLength(temp);
584 VectorCopy(v[j], outvertex3f);
585 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
590 // output the front and back triangles
591 outelement3i[0] = vertexremap[e[0]];
592 outelement3i[1] = vertexremap[e[1]];
593 outelement3i[2] = vertexremap[e[2]];
594 outelement3i[3] = vertexremap[e[2]] + 1;
595 outelement3i[4] = vertexremap[e[1]] + 1;
596 outelement3i[5] = vertexremap[e[0]] + 1;
601 for (i = 0;i < numfacing;i++)
603 t = trianglefacinglightlist[i];
604 e = inelement3i + t * 3;
605 n = inneighbor3i + t * 3;
606 // output the sides (facing outward from this triangle)
608 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
610 vr[0] = vertexremap[e[0]];
611 vr[1] = vertexremap[e[1]];
612 outelement3i[0] = vr[1];
613 outelement3i[1] = vr[0];
614 outelement3i[2] = vr[0] + 1;
615 outelement3i[3] = vr[1];
616 outelement3i[4] = vr[0] + 1;
617 outelement3i[5] = vr[1] + 1;
622 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
624 vr[1] = vertexremap[e[1]];
625 vr[2] = vertexremap[e[2]];
626 outelement3i[0] = vr[2];
627 outelement3i[1] = vr[1];
628 outelement3i[2] = vr[1] + 1;
629 outelement3i[3] = vr[2];
630 outelement3i[4] = vr[1] + 1;
631 outelement3i[5] = vr[2] + 1;
636 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
638 vr[0] = vertexremap[e[0]];
639 vr[2] = vertexremap[e[2]];
640 outelement3i[0] = vr[0];
641 outelement3i[1] = vr[2];
642 outelement3i[2] = vr[2] + 1;
643 outelement3i[3] = vr[0];
644 outelement3i[4] = vr[2] + 1;
645 outelement3i[5] = vr[0] + 1;
652 *outnumvertices = outvertices;
656 float varray_vertex3f2[65536*3];
658 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
661 if (projectdistance < 0.1)
663 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
669 // make sure shadowelements is big enough for this volume
670 if (maxshadowelements < numtris * 24)
671 R_Shadow_ResizeShadowElements(numtris);
673 // check which triangles are facing the light, and then output
674 // triangle elements and vertices... by clever use of elements we
675 // can construct the whole shadow from the unprojected vertices and
676 // the projected vertices
677 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
679 GL_VertexPointer(varray_vertex3f2);
680 if (r_shadowstage == SHADOWSTAGE_STENCIL)
682 // decrement stencil if frontface is behind depthbuffer
683 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
684 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
685 R_Mesh_Draw(outverts, tris, shadowelements);
687 c_rt_shadowtris += numtris;
688 // increment stencil if backface is behind depthbuffer
689 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
690 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
692 R_Mesh_Draw(outverts, tris, shadowelements);
694 c_rt_shadowtris += numtris;
698 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
701 if (r_shadowstage == SHADOWSTAGE_STENCIL)
703 // decrement stencil if frontface is behind depthbuffer
704 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
705 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
706 for (mesh = firstmesh;mesh;mesh = mesh->next)
708 GL_VertexPointer(mesh->vertex3f);
709 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
710 c_rtcached_shadowmeshes++;
711 c_rtcached_shadowtris += mesh->numtriangles;
713 // increment stencil if backface is behind depthbuffer
714 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
715 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
717 for (mesh = firstmesh;mesh;mesh = mesh->next)
719 GL_VertexPointer(mesh->vertex3f);
720 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
721 c_rtcached_shadowmeshes++;
722 c_rtcached_shadowtris += mesh->numtriangles;
726 float r_shadow_attenpower, r_shadow_attenscale;
727 static void R_Shadow_MakeTextures(void)
729 int x, y, z, d, side;
730 float v[3], s, t, intensity;
732 R_FreeTexturePool(&r_shadow_texturepool);
733 r_shadow_texturepool = R_AllocTexturePool();
734 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
735 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
737 #define ATTEN2DSIZE 64
738 #define ATTEN3DSIZE 32
739 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
744 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
749 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
754 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
755 if (gl_texturecubemap)
757 for (side = 0;side < 6;side++)
759 for (y = 0;y < NORMSIZE;y++)
761 for (x = 0;x < NORMSIZE;x++)
763 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
764 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
798 intensity = 127.0f / sqrt(DotProduct(v, v));
799 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
800 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
801 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
802 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
806 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
809 r_shadow_normalcubetexture = NULL;
810 for (y = 0;y < ATTEN2DSIZE;y++)
812 for (x = 0;x < ATTEN2DSIZE;x++)
814 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
815 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
817 intensity = 1.0f - sqrt(DotProduct(v, v));
819 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
820 d = bound(0, intensity, 255);
821 data[(y*ATTEN2DSIZE+x)*4+0] = d;
822 data[(y*ATTEN2DSIZE+x)*4+1] = d;
823 data[(y*ATTEN2DSIZE+x)*4+2] = d;
824 data[(y*ATTEN2DSIZE+x)*4+3] = d;
827 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
828 if (r_shadow_texture3d.integer)
830 for (z = 0;z < ATTEN3DSIZE;z++)
832 for (y = 0;y < ATTEN3DSIZE;y++)
834 for (x = 0;x < ATTEN3DSIZE;x++)
836 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
837 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
838 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
839 intensity = 1.0f - sqrt(DotProduct(v, v));
841 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
842 d = bound(0, intensity, 255);
843 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
844 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
845 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
846 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
850 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
855 void R_Shadow_Stage_Begin(void)
859 if (r_shadow_texture3d.integer && !gl_texture3d)
860 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
862 if (!r_shadow_attenuation2dtexture
863 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
864 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
865 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
866 R_Shadow_MakeTextures();
868 memset(&m, 0, sizeof(m));
869 GL_BlendFunc(GL_ONE, GL_ZERO);
872 R_Mesh_State_Texture(&m);
873 GL_Color(0, 0, 0, 1);
874 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
875 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
876 r_shadowstage = SHADOWSTAGE_NONE;
878 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
879 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
880 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
883 void R_Shadow_LoadWorldLightsIfNeeded(void)
885 if (r_shadow_reloadlights && cl.worldmodel)
887 R_Shadow_ClearWorldLights();
888 r_shadow_reloadlights = false;
889 R_Shadow_LoadWorldLights();
890 if (r_shadow_worldlightchain == NULL)
892 R_Shadow_LoadLightsFile();
893 if (r_shadow_worldlightchain == NULL)
894 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
899 void R_Shadow_Stage_ShadowVolumes(void)
902 memset(&m, 0, sizeof(m));
903 R_Mesh_State_Texture(&m);
904 GL_Color(1, 1, 1, 1);
905 qglColorMask(0, 0, 0, 0);
906 GL_BlendFunc(GL_ONE, GL_ZERO);
909 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
910 //if (r_shadow_polygonoffset.value != 0)
912 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
913 // qglEnable(GL_POLYGON_OFFSET_FILL);
916 // qglDisable(GL_POLYGON_OFFSET_FILL);
917 qglDepthFunc(GL_LESS);
918 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
919 qglEnable(GL_STENCIL_TEST);
920 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
921 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
922 r_shadowstage = SHADOWSTAGE_STENCIL;
923 qglClear(GL_STENCIL_BUFFER_BIT);
925 // LordHavoc note: many shadow volumes reside entirely inside the world
926 // (that is to say they are entirely bounded by their lit surfaces),
927 // which can be optimized by handling things as an inverted light volume,
928 // with the shadow boundaries of the world being simulated by an altered
929 // (129) bias to stencil clearing on such lights
930 // FIXME: generate inverted light volumes for use as shadow volumes and
931 // optimize for them as noted above
934 void R_Shadow_Stage_LightWithoutShadows(void)
937 memset(&m, 0, sizeof(m));
938 R_Mesh_State_Texture(&m);
939 GL_BlendFunc(GL_ONE, GL_ONE);
942 qglPolygonOffset(0, 0);
943 //qglDisable(GL_POLYGON_OFFSET_FILL);
944 GL_Color(1, 1, 1, 1);
945 qglColorMask(1, 1, 1, 1);
946 qglDepthFunc(GL_EQUAL);
947 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
948 qglDisable(GL_STENCIL_TEST);
949 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
950 qglStencilFunc(GL_EQUAL, 128, 0xFF);
951 r_shadowstage = SHADOWSTAGE_LIGHT;
955 void R_Shadow_Stage_LightWithShadows(void)
958 memset(&m, 0, sizeof(m));
959 R_Mesh_State_Texture(&m);
960 GL_BlendFunc(GL_ONE, GL_ONE);
963 qglPolygonOffset(0, 0);
964 //qglDisable(GL_POLYGON_OFFSET_FILL);
965 GL_Color(1, 1, 1, 1);
966 qglColorMask(1, 1, 1, 1);
967 qglDepthFunc(GL_EQUAL);
968 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
969 qglEnable(GL_STENCIL_TEST);
970 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
971 // only draw light where this geometry was already rendered AND the
972 // stencil is 128 (values other than this mean shadow)
973 qglStencilFunc(GL_EQUAL, 128, 0xFF);
974 r_shadowstage = SHADOWSTAGE_LIGHT;
978 void R_Shadow_Stage_End(void)
981 memset(&m, 0, sizeof(m));
982 R_Mesh_State_Texture(&m);
983 GL_BlendFunc(GL_ONE, GL_ZERO);
986 qglPolygonOffset(0, 0);
987 //qglDisable(GL_POLYGON_OFFSET_FILL);
988 GL_Color(1, 1, 1, 1);
989 qglColorMask(1, 1, 1, 1);
990 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
991 qglDepthFunc(GL_LEQUAL);
992 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
993 qglDisable(GL_STENCIL_TEST);
994 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
995 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
996 r_shadowstage = SHADOWSTAGE_NONE;
999 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1001 int i, ix1, iy1, ix2, iy2;
1002 float x1, y1, x2, y2, x, y, f;
1003 vec3_t smins, smaxs;
1005 if (!r_shadow_scissor.integer)
1007 // if view is inside the box, just say yes it's visible
1008 // LordHavoc: for some odd reason scissor seems broken without stencil
1009 // (?!? seems like a driver bug) so abort if gl_stencil is false
1010 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1012 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1015 for (i = 0;i < 3;i++)
1017 if (r_viewforward[i] >= 0)
1028 f = DotProduct(r_viewforward, r_vieworigin) + 1;
1029 if (DotProduct(r_viewforward, v2) <= f)
1031 // entirely behind nearclip plane
1034 if (DotProduct(r_viewforward, v) >= f)
1036 // entirely infront of nearclip plane
1037 x1 = y1 = x2 = y2 = 0;
1038 for (i = 0;i < 8;i++)
1040 v[0] = (i & 1) ? mins[0] : maxs[0];
1041 v[1] = (i & 2) ? mins[1] : maxs[1];
1042 v[2] = (i & 4) ? mins[2] : maxs[2];
1044 GL_TransformToScreen(v, v2);
1045 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1064 // clipped by nearclip plane
1065 // this is nasty and crude...
1066 // create viewspace bbox
1067 for (i = 0;i < 8;i++)
1069 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1070 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1071 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1072 v2[0] = -DotProduct(v, r_viewleft);
1073 v2[1] = DotProduct(v, r_viewup);
1074 v2[2] = DotProduct(v, r_viewforward);
1077 if (smins[0] > v2[0]) smins[0] = v2[0];
1078 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1079 if (smins[1] > v2[1]) smins[1] = v2[1];
1080 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1081 if (smins[2] > v2[2]) smins[2] = v2[2];
1082 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1086 smins[0] = smaxs[0] = v2[0];
1087 smins[1] = smaxs[1] = v2[1];
1088 smins[2] = smaxs[2] = v2[2];
1091 // now we have a bbox in viewspace
1092 // clip it to the view plane
1095 // return true if that culled the box
1096 if (smins[2] >= smaxs[2])
1098 // ok some of it is infront of the view, transform each corner back to
1099 // worldspace and then to screenspace and make screen rect
1100 // initialize these variables just to avoid compiler warnings
1101 x1 = y1 = x2 = y2 = 0;
1102 for (i = 0;i < 8;i++)
1104 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1105 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1106 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1107 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1108 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1109 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1111 GL_TransformToScreen(v, v2);
1112 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1129 // this code doesn't handle boxes with any points behind view properly
1130 x1 = 1000;x2 = -1000;
1131 y1 = 1000;y2 = -1000;
1132 for (i = 0;i < 8;i++)
1134 v[0] = (i & 1) ? mins[0] : maxs[0];
1135 v[1] = (i & 2) ? mins[1] : maxs[1];
1136 v[2] = (i & 4) ? mins[2] : maxs[2];
1138 GL_TransformToScreen(v, v2);
1139 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1157 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1158 if (ix1 < r_view_x) ix1 = r_view_x;
1159 if (iy1 < r_view_y) iy1 = r_view_y;
1160 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1161 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1162 if (ix2 <= ix1 || iy2 <= iy1)
1164 // set up the scissor rectangle
1165 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1166 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1167 //qglEnable(GL_SCISSOR_TEST);
1172 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1174 float *color4f = varray_color4f;
1175 float dist, dot, intensity, v[3], n[3];
1176 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1178 Matrix4x4_Transform(m, vertex3f, v);
1179 if ((dist = DotProduct(v, v)) < 1)
1181 Matrix4x4_Transform3x3(m, normal3f, n);
1182 if ((dot = DotProduct(n, v)) > 0)
1185 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1186 VectorScale(lightcolor, intensity, color4f);
1191 VectorClear(color4f);
1197 VectorClear(color4f);
1203 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1205 float *color4f = varray_color4f;
1206 float dist, dot, intensity, v[3], n[3];
1207 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1209 Matrix4x4_Transform(m, vertex3f, v);
1210 if ((dist = fabs(v[2])) < 1)
1212 Matrix4x4_Transform3x3(m, normal3f, n);
1213 if ((dot = DotProduct(n, v)) > 0)
1215 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1216 VectorScale(lightcolor, intensity, color4f);
1221 VectorClear(color4f);
1227 VectorClear(color4f);
1233 // FIXME: this should be done in a vertex program when possible
1234 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1235 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1239 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1240 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1241 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1248 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1252 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1253 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1260 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1264 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1266 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1267 // the cubemap normalizes this for us
1268 out3f[0] = DotProduct(svector3f, lightdir);
1269 out3f[1] = DotProduct(tvector3f, lightdir);
1270 out3f[2] = DotProduct(normal3f, lightdir);
1274 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1277 float lightdir[3], eyedir[3], halfdir[3];
1278 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1280 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1281 VectorNormalizeFast(lightdir);
1282 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1283 VectorNormalizeFast(eyedir);
1284 VectorAdd(lightdir, eyedir, halfdir);
1285 // the cubemap normalizes this for us
1286 out3f[0] = DotProduct(svector3f, halfdir);
1287 out3f[1] = DotProduct(tvector3f, halfdir);
1288 out3f[2] = DotProduct(normal3f, halfdir);
1292 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1295 float color[3], color2[3];
1297 GL_VertexPointer(vertex3f);
1298 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1301 bumptexture = r_shadow_blankbumptexture;
1303 // colorscale accounts for how much we multiply the brightness during combine
1304 // mult is how many times the final pass of the lighting will be
1305 // performed to get more brightness than otherwise possible
1306 // limit mult to 64 for sanity sake
1307 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1309 // 3/2 3D combine path (Geforce3, Radeon 8500)
1310 memset(&m, 0, sizeof(m));
1311 m.tex[0] = R_GetTexture(bumptexture);
1312 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1313 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1314 m.texcombinergb[0] = GL_REPLACE;
1315 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1316 m.pointer_texcoord[0] = texcoord2f;
1317 m.pointer_texcoord[1] = varray_texcoord3f[1];
1318 m.pointer_texcoord[2] = varray_texcoord3f[2];
1319 R_Mesh_State_Texture(&m);
1320 qglColorMask(0,0,0,1);
1321 GL_BlendFunc(GL_ONE, GL_ZERO);
1322 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1323 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1324 R_Mesh_Draw(numverts, numtriangles, elements);
1326 c_rt_lighttris += numtriangles;
1328 memset(&m, 0, sizeof(m));
1329 m.tex[0] = R_GetTexture(basetexture);
1330 m.pointer_texcoord[0] = texcoord2f;
1333 m.texcubemap[1] = R_GetTexture(lightcubemap);
1334 m.pointer_texcoord[1] = varray_texcoord3f[1];
1335 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1337 R_Mesh_State_Texture(&m);
1338 qglColorMask(1,1,1,0);
1339 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1340 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1341 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1343 color[0] = bound(0, color2[0], 1);
1344 color[1] = bound(0, color2[1], 1);
1345 color[2] = bound(0, color2[2], 1);
1346 GL_Color(color[0], color[1], color[2], 1);
1347 R_Mesh_Draw(numverts, numtriangles, elements);
1349 c_rt_lighttris += numtriangles;
1352 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1354 // 1/2/2 3D combine path (original Radeon)
1355 memset(&m, 0, sizeof(m));
1356 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1357 m.pointer_texcoord[0] = varray_texcoord3f[0];
1358 R_Mesh_State_Texture(&m);
1359 qglColorMask(0,0,0,1);
1360 GL_BlendFunc(GL_ONE, GL_ZERO);
1361 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1362 R_Mesh_Draw(numverts, numtriangles, elements);
1364 c_rt_lighttris += numtriangles;
1366 memset(&m, 0, sizeof(m));
1367 m.tex[0] = R_GetTexture(bumptexture);
1368 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1369 m.texcombinergb[0] = GL_REPLACE;
1370 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1371 m.pointer_texcoord[0] = texcoord2f;
1372 m.pointer_texcoord[1] = varray_texcoord3f[1];
1373 R_Mesh_State_Texture(&m);
1374 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1375 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1376 R_Mesh_Draw(numverts, numtriangles, elements);
1378 c_rt_lighttris += numtriangles;
1380 memset(&m, 0, sizeof(m));
1381 m.tex[0] = R_GetTexture(basetexture);
1382 m.pointer_texcoord[0] = texcoord2f;
1385 m.texcubemap[1] = R_GetTexture(lightcubemap);
1386 m.pointer_texcoord[1] = varray_texcoord3f[1];
1387 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1389 R_Mesh_State_Texture(&m);
1390 qglColorMask(1,1,1,0);
1391 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1392 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1393 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1395 color[0] = bound(0, color2[0], 1);
1396 color[1] = bound(0, color2[1], 1);
1397 color[2] = bound(0, color2[2], 1);
1398 GL_Color(color[0], color[1], color[2], 1);
1399 R_Mesh_Draw(numverts, numtriangles, elements);
1401 c_rt_lighttris += numtriangles;
1404 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1406 // 2/2 3D combine path (original Radeon)
1407 memset(&m, 0, sizeof(m));
1408 m.tex[0] = R_GetTexture(bumptexture);
1409 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1410 m.texcombinergb[0] = GL_REPLACE;
1411 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1412 m.pointer_texcoord[0] = texcoord2f;
1413 m.pointer_texcoord[1] = varray_texcoord3f[1];
1414 R_Mesh_State_Texture(&m);
1415 qglColorMask(0,0,0,1);
1416 GL_BlendFunc(GL_ONE, GL_ZERO);
1417 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1418 R_Mesh_Draw(numverts, numtriangles, elements);
1420 c_rt_lighttris += numtriangles;
1422 memset(&m, 0, sizeof(m));
1423 m.tex[0] = R_GetTexture(basetexture);
1424 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1425 m.pointer_texcoord[0] = texcoord2f;
1426 m.pointer_texcoord[1] = varray_texcoord3f[1];
1427 R_Mesh_State_Texture(&m);
1428 qglColorMask(1,1,1,0);
1429 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1430 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1431 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1432 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1434 color[0] = bound(0, color2[0], 1);
1435 color[1] = bound(0, color2[1], 1);
1436 color[2] = bound(0, color2[2], 1);
1437 GL_Color(color[0], color[1], color[2], 1);
1438 R_Mesh_Draw(numverts, numtriangles, elements);
1440 c_rt_lighttris += numtriangles;
1443 else if (r_textureunits.integer >= 4)
1445 // 4/2 2D combine path (Geforce3, Radeon 8500)
1446 memset(&m, 0, sizeof(m));
1447 m.tex[0] = R_GetTexture(bumptexture);
1448 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1449 m.texcombinergb[0] = GL_REPLACE;
1450 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1451 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1452 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1453 m.pointer_texcoord[0] = texcoord2f;
1454 m.pointer_texcoord[1] = varray_texcoord3f[1];
1455 m.pointer_texcoord[2] = varray_texcoord2f[2];
1456 m.pointer_texcoord[3] = varray_texcoord2f[3];
1457 R_Mesh_State_Texture(&m);
1458 qglColorMask(0,0,0,1);
1459 GL_BlendFunc(GL_ONE, GL_ZERO);
1460 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1461 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1462 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1463 R_Mesh_Draw(numverts, numtriangles, elements);
1465 c_rt_lighttris += numtriangles;
1467 memset(&m, 0, sizeof(m));
1468 m.tex[0] = R_GetTexture(basetexture);
1469 m.pointer_texcoord[0] = texcoord2f;
1472 m.texcubemap[1] = R_GetTexture(lightcubemap);
1473 m.pointer_texcoord[1] = varray_texcoord3f[1];
1474 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1476 R_Mesh_State_Texture(&m);
1477 qglColorMask(1,1,1,0);
1478 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1479 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1480 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1482 color[0] = bound(0, color2[0], 1);
1483 color[1] = bound(0, color2[1], 1);
1484 color[2] = bound(0, color2[2], 1);
1485 GL_Color(color[0], color[1], color[2], 1);
1486 R_Mesh_Draw(numverts, numtriangles, elements);
1488 c_rt_lighttris += numtriangles;
1493 // 2/2/2 2D combine path (any dot3 card)
1494 memset(&m, 0, sizeof(m));
1495 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1496 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1497 m.pointer_texcoord[0] = varray_texcoord2f[0];
1498 m.pointer_texcoord[1] = varray_texcoord2f[1];
1499 R_Mesh_State_Texture(&m);
1500 qglColorMask(0,0,0,1);
1501 GL_BlendFunc(GL_ONE, GL_ZERO);
1502 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1503 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1504 R_Mesh_Draw(numverts, numtriangles, elements);
1506 c_rt_lighttris += numtriangles;
1508 memset(&m, 0, sizeof(m));
1509 m.tex[0] = R_GetTexture(bumptexture);
1510 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1511 m.texcombinergb[0] = GL_REPLACE;
1512 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1513 m.pointer_texcoord[0] = texcoord2f;
1514 m.pointer_texcoord[1] = varray_texcoord3f[1];
1515 R_Mesh_State_Texture(&m);
1516 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1517 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1518 R_Mesh_Draw(numverts, numtriangles, elements);
1520 c_rt_lighttris += numtriangles;
1522 memset(&m, 0, sizeof(m));
1523 m.tex[0] = R_GetTexture(basetexture);
1524 m.pointer_texcoord[0] = texcoord2f;
1527 m.texcubemap[1] = R_GetTexture(lightcubemap);
1528 m.pointer_texcoord[1] = varray_texcoord3f[1];
1529 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1531 R_Mesh_State_Texture(&m);
1532 qglColorMask(1,1,1,0);
1533 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1534 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1535 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1537 color[0] = bound(0, color2[0], 1);
1538 color[1] = bound(0, color2[1], 1);
1539 color[2] = bound(0, color2[2], 1);
1540 GL_Color(color[0], color[1], color[2], 1);
1541 R_Mesh_Draw(numverts, numtriangles, elements);
1543 c_rt_lighttris += numtriangles;
1549 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1550 GL_DepthMask(false);
1552 GL_ColorPointer(varray_color4f);
1553 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1554 memset(&m, 0, sizeof(m));
1555 m.tex[0] = R_GetTexture(basetexture);
1556 m.pointer_texcoord[0] = texcoord2f;
1557 if (r_textureunits.integer >= 2)
1560 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1561 m.pointer_texcoord[1] = varray_texcoord2f[1];
1562 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1564 R_Mesh_State_Texture(&m);
1565 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1567 color[0] = bound(0, color2[0], 1);
1568 color[1] = bound(0, color2[1], 1);
1569 color[2] = bound(0, color2[2], 1);
1570 if (r_textureunits.integer >= 2)
1571 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1573 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1574 R_Mesh_Draw(numverts, numtriangles, elements);
1576 c_rt_lighttris += numtriangles;
1581 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1584 float color[3], color2[3], colorscale;
1586 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1589 glosstexture = r_shadow_blankglosstexture;
1590 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1592 colorscale = r_shadow_glossintensity.value;
1594 bumptexture = r_shadow_blankbumptexture;
1595 if (glosstexture == r_shadow_blankglosstexture)
1596 colorscale *= r_shadow_gloss2intensity.value;
1597 GL_VertexPointer(vertex3f);
1599 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1601 // 2/0/0/1/2 3D combine blendsquare path
1602 memset(&m, 0, sizeof(m));
1603 m.tex[0] = R_GetTexture(bumptexture);
1604 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1605 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1606 m.pointer_texcoord[0] = texcoord2f;
1607 m.pointer_texcoord[1] = varray_texcoord3f[1];
1608 R_Mesh_State_Texture(&m);
1609 qglColorMask(0,0,0,1);
1610 // this squares the result
1611 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1612 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1613 R_Mesh_Draw(numverts, numtriangles, elements);
1615 c_rt_lighttris += numtriangles;
1617 memset(&m, 0, sizeof(m));
1618 R_Mesh_State_Texture(&m);
1619 // square alpha in framebuffer a few times to make it shiny
1620 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1621 // these comments are a test run through this math for intensity 0.5
1622 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1623 // 0.25 * 0.25 = 0.0625 (this is another pass)
1624 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1625 R_Mesh_Draw(numverts, numtriangles, elements);
1627 c_rt_lighttris += numtriangles;
1628 R_Mesh_Draw(numverts, numtriangles, elements);
1630 c_rt_lighttris += numtriangles;
1632 memset(&m, 0, sizeof(m));
1633 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1634 m.pointer_texcoord[0] = varray_texcoord3f[0];
1635 R_Mesh_State_Texture(&m);
1636 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1637 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1638 R_Mesh_Draw(numverts, numtriangles, elements);
1640 c_rt_lighttris += numtriangles;
1642 memset(&m, 0, sizeof(m));
1643 m.tex[0] = R_GetTexture(glosstexture);
1646 m.texcubemap[1] = R_GetTexture(lightcubemap);
1647 m.pointer_texcoord[1] = varray_texcoord3f[1];
1648 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1650 m.pointer_texcoord[0] = texcoord2f;
1651 R_Mesh_State_Texture(&m);
1652 qglColorMask(1,1,1,0);
1653 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1654 VectorScale(lightcolor, colorscale, color2);
1655 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1657 color[0] = bound(0, color2[0], 1);
1658 color[1] = bound(0, color2[1], 1);
1659 color[2] = bound(0, color2[2], 1);
1660 GL_Color(color[0], color[1], color[2], 1);
1661 R_Mesh_Draw(numverts, numtriangles, elements);
1663 c_rt_lighttris += numtriangles;
1666 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1668 // 2/0/0/2 3D combine blendsquare path
1669 memset(&m, 0, sizeof(m));
1670 m.tex[0] = R_GetTexture(bumptexture);
1671 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1672 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1673 m.pointer_texcoord[0] = texcoord2f;
1674 m.pointer_texcoord[1] = varray_texcoord3f[1];
1675 R_Mesh_State_Texture(&m);
1676 qglColorMask(0,0,0,1);
1677 // this squares the result
1678 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1679 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1680 R_Mesh_Draw(numverts, numtriangles, elements);
1682 c_rt_lighttris += numtriangles;
1684 memset(&m, 0, sizeof(m));
1685 R_Mesh_State_Texture(&m);
1686 // square alpha in framebuffer a few times to make it shiny
1687 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1688 // these comments are a test run through this math for intensity 0.5
1689 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1690 // 0.25 * 0.25 = 0.0625 (this is another pass)
1691 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1692 R_Mesh_Draw(numverts, numtriangles, elements);
1694 c_rt_lighttris += numtriangles;
1695 R_Mesh_Draw(numverts, numtriangles, elements);
1697 c_rt_lighttris += numtriangles;
1699 memset(&m, 0, sizeof(m));
1700 m.tex[0] = R_GetTexture(glosstexture);
1701 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1702 m.pointer_texcoord[0] = texcoord2f;
1703 m.pointer_texcoord[1] = varray_texcoord3f[1];
1704 R_Mesh_State_Texture(&m);
1705 qglColorMask(1,1,1,0);
1706 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1707 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1708 VectorScale(lightcolor, colorscale, color2);
1709 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1711 color[0] = bound(0, color2[0], 1);
1712 color[1] = bound(0, color2[1], 1);
1713 color[2] = bound(0, color2[2], 1);
1714 GL_Color(color[0], color[1], color[2], 1);
1715 R_Mesh_Draw(numverts, numtriangles, elements);
1717 c_rt_lighttris += numtriangles;
1720 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1722 // 2/0/0/2/2 2D combine blendsquare path
1723 memset(&m, 0, sizeof(m));
1724 m.tex[0] = R_GetTexture(bumptexture);
1725 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1726 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1727 m.pointer_texcoord[0] = texcoord2f;
1728 m.pointer_texcoord[1] = varray_texcoord3f[1];
1729 R_Mesh_State_Texture(&m);
1730 qglColorMask(0,0,0,1);
1731 // this squares the result
1732 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1733 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1734 R_Mesh_Draw(numverts, numtriangles, elements);
1736 c_rt_lighttris += numtriangles;
1738 memset(&m, 0, sizeof(m));
1739 R_Mesh_State_Texture(&m);
1740 // square alpha in framebuffer a few times to make it shiny
1741 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1742 // these comments are a test run through this math for intensity 0.5
1743 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1744 // 0.25 * 0.25 = 0.0625 (this is another pass)
1745 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1746 R_Mesh_Draw(numverts, numtriangles, elements);
1748 c_rt_lighttris += numtriangles;
1749 R_Mesh_Draw(numverts, numtriangles, elements);
1751 c_rt_lighttris += numtriangles;
1753 memset(&m, 0, sizeof(m));
1754 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1755 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1756 m.pointer_texcoord[0] = varray_texcoord2f[0];
1757 m.pointer_texcoord[1] = varray_texcoord2f[1];
1758 R_Mesh_State_Texture(&m);
1759 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1760 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1761 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1762 R_Mesh_Draw(numverts, numtriangles, elements);
1764 c_rt_lighttris += numtriangles;
1766 memset(&m, 0, sizeof(m));
1767 m.tex[0] = R_GetTexture(glosstexture);
1770 m.texcubemap[1] = R_GetTexture(lightcubemap);
1771 m.pointer_texcoord[1] = varray_texcoord3f[1];
1772 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1774 m.pointer_texcoord[0] = texcoord2f;
1775 R_Mesh_State_Texture(&m);
1776 qglColorMask(1,1,1,0);
1777 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1778 VectorScale(lightcolor, colorscale, color2);
1779 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1781 color[0] = bound(0, color2[0], 1);
1782 color[1] = bound(0, color2[1], 1);
1783 color[2] = bound(0, color2[2], 1);
1784 GL_Color(color[0], color[1], color[2], 1);
1785 R_Mesh_Draw(numverts, numtriangles, elements);
1787 c_rt_lighttris += numtriangles;
1793 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1797 R_RTLight_Uncompile(rtlight);
1798 memset(rtlight, 0, sizeof(*rtlight));
1800 VectorCopy(light->origin, rtlight->shadoworigin);
1801 VectorCopy(light->color, rtlight->color);
1802 rtlight->radius = light->radius;
1803 rtlight->cullradius = rtlight->radius;
1804 rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
1805 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
1806 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
1807 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
1808 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
1809 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
1810 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
1811 rtlight->cubemapname[0] = 0;
1812 if (light->cubemapname[0])
1813 strcpy(rtlight->cubemapname, light->cubemapname);
1814 else if (light->cubemapnum > 0)
1815 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1816 rtlight->shadow = light->shadow;
1817 rtlight->corona = light->corona;
1818 rtlight->style = light->style;
1819 rtlight->isstatic = isstatic;
1820 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1821 // ConcatScale won't work here because this needs to scale rotate and
1822 // translate, not just rotate
1823 scale = 1.0f / rtlight->radius;
1824 for (k = 0;k < 3;k++)
1825 for (j = 0;j < 4;j++)
1826 rtlight->matrix_worldtolight.m[k][j] *= scale;
1827 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1828 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1830 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1831 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1832 VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1833 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1836 // compiles rtlight geometry
1837 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1838 void R_RTLight_Compile(rtlight_t *rtlight)
1840 int i, j, k, l, maxverts = 256, tris;
1841 float *vertex3f = NULL, mins[3], maxs[3];
1842 shadowmesh_t *mesh, *castmesh = NULL;
1844 qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1845 qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1847 // compile the light
1848 rtlight->compiled = true;
1849 VectorCopy(rtlight->cullmins, mins);
1850 VectorCopy(rtlight->cullmaxs, maxs);
1851 if (rtlight->shadow)
1852 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1853 rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1856 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
1857 memset(lightpvs, 0, lightpvsbytes);
1858 if (cl.worldmodel->brushq3.num_leafs)
1863 // make a pvs that only includes things within the box
1864 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1865 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1866 SETPVSBIT(lightpvs, leaf->clusterindex);
1868 // make a cluster list for fast visibility checking during rendering
1869 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1870 if (CHECKPVSBIT(lightpvs, i))
1871 rtlight->static_numclusters++;
1872 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1873 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1874 if (CHECKPVSBIT(lightpvs, i))
1875 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1877 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1878 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1879 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1880 face->lighttemp_castshadow = false;
1881 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1883 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1885 for (k = 0;k < 3;k++)
1887 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1888 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1890 for (j = 0;j < leaf->numleaffaces;j++)
1892 face = leaf->firstleafface[j];
1893 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1894 face->lighttemp_castshadow = true;
1899 // add surfaces to shadow casting mesh and light mesh
1900 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1902 if (face->lighttemp_castshadow)
1904 face->lighttemp_castshadow = false;
1905 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1907 if (rtlight->shadow)
1908 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1909 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1910 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1911 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1916 else if (cl.worldmodel->brushq1.num_leafs)
1920 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1921 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1922 i = CL_PointQ1Contents(rtlight->shadoworigin);
1924 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1925 surf->lighttemp_castshadow = false;
1927 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1930 qbyte *bytesurfacepvs;
1932 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
1933 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1935 Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
1937 // make a pvs that only includes things within the box
1938 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1940 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1942 SETPVSBIT(lightpvs, leaf->clusterindex);
1943 for (k = 0;k < 3;k++)
1945 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1946 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1951 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1952 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1953 surf->lighttemp_castshadow = true;
1955 Mem_Free(byteleafpvs);
1956 Mem_Free(bytesurfacepvs);
1958 // make a cluster list for fast visibility checking during rendering
1959 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1960 if (CHECKPVSBIT(lightpvs, i))
1961 rtlight->static_numclusters++;
1962 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1963 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1964 if (CHECKPVSBIT(lightpvs, i))
1965 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1969 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1971 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1973 // make a pvs that only includes things within the box
1974 SETPVSBIT(lightpvs, leaf->clusterindex);
1975 for (k = 0;k < 3;k++)
1977 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1978 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1980 for (j = 0;j < leaf->nummarksurfaces;j++)
1982 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1983 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1984 surf->lighttemp_castshadow = true;
1989 // make a pvs that only includes things within the box
1990 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1991 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1992 SETPVSBIT(lightpvs, leaf->clusterindex);
1994 // make a cluster list for fast visibility checking during rendering
1995 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1996 if (CHECKPVSBIT(lightpvs, i))
1997 rtlight->static_numclusters++;
1998 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1999 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2000 if (CHECKPVSBIT(lightpvs, i))
2001 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
2004 // add surfaces to shadow casting mesh and light mesh
2005 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2007 if (surf->lighttemp_castshadow)
2009 surf->lighttemp_castshadow = false;
2010 if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
2011 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
2012 if (!(surf->flags & SURF_DRAWSKY))
2013 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
2019 // limit box to light bounds (in case it grew larger)
2020 for (k = 0;k < 3;k++)
2022 if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
2023 if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
2025 rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2026 rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2028 // cast shadow volume from castmesh
2029 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2033 for (mesh = castmesh;mesh;mesh = mesh->next)
2035 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2036 maxverts = max(maxverts, mesh->numverts * 2);
2041 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2042 // now that we have the buffers big enough, construct and add
2043 // the shadow volume mesh
2044 if (rtlight->shadow)
2045 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2046 for (mesh = castmesh;mesh;mesh = mesh->next)
2048 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2049 if ((tris = R_Shadow_ConstructShadowVolume(castmesh->numverts, 0, castmesh->numtriangles, castmesh->element3i, castmesh->neighbor3i, castmesh->vertex3f, NULL, shadowelements, vertex3f, rtlight->shadoworigin, r_shadow_projectdistance.value)))
2050 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2055 // we're done with castmesh now
2056 Mod_ShadowMesh_Free(castmesh);
2059 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
2060 rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
2063 if (rtlight->static_meshchain_shadow)
2064 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2065 k += mesh->numtriangles;
2067 if (rtlight->static_meshchain_light)
2068 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2069 l += mesh->numtriangles;
2070 Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], k, l);
2073 void R_RTLight_Uncompile(rtlight_t *rtlight)
2075 if (rtlight->compiled)
2077 if (rtlight->static_meshchain_shadow)
2078 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2079 rtlight->static_meshchain_shadow = NULL;
2080 if (rtlight->static_meshchain_light)
2081 Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2082 rtlight->static_meshchain_light = NULL;
2083 if (rtlight->static_clusterindices)
2084 Mem_Free(rtlight->static_clusterindices);
2085 rtlight->static_clusterindices = NULL;
2086 rtlight->static_numclusters = 0;
2087 rtlight->compiled = false;
2091 int shadowframecount = 0;
2093 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
2096 if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
2098 vec3_t relativeshadoworigin;
2099 Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
2100 ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
2104 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2106 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2109 entity_render_t *ent;
2111 vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2112 rtexture_t *cubemaptexture;
2113 matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2115 if (d_lightstylevalue[rtlight->style] <= 0)
2117 if (rtlight->compiled)
2119 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2121 for (i = 0;i < rtlight->static_numclusters;i++)
2122 if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
2124 if (i == rtlight->static_numclusters)
2127 else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2129 if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2132 if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2133 R_RTLight_Compile(rtlight);
2135 f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2136 VectorScale(rtlight->color, f, lightcolor);
2138 if (rtlight->selected)
2140 f = 2 + sin(realtime * M_PI * 4.0);
2141 VectorScale(lightcolor, f, lightcolor);
2145 if (rtlight->cubemapname[0])
2146 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2148 cubemaptexture = NULL;
2150 shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2151 if (shadow && (gl_stencil || visiblevolumes))
2153 if (!visiblevolumes)
2154 R_Shadow_Stage_ShadowVolumes();
2155 ent = &cl_entities[0].render;
2156 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2158 R_Mesh_Matrix(&ent->matrix);
2159 if (r_shadow_showtris.integer)
2163 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2164 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2165 qglDisable(GL_DEPTH_TEST);
2166 qglDisable(GL_STENCIL_TEST);
2167 //qglDisable(GL_CULL_FACE);
2168 qglColorMask(1,1,1,1);
2169 memset(&m, 0, sizeof(m));
2170 R_Mesh_State_Texture(&m);
2171 GL_Color(0,0.1,0,1);
2172 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2173 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2175 GL_VertexPointer(mesh->vertex3f);
2176 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2178 //qglEnable(GL_CULL_FACE);
2180 qglEnable(GL_DEPTH_TEST);
2183 qglEnable(GL_STENCIL_TEST);
2184 qglColorMask(0,0,0,0);
2187 R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
2190 R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2191 if (r_drawentities.integer)
2192 for (i = 0;i < r_refdef.numentities;i++)
2193 if (r_refdef.entities[i]->flags & RENDER_SHADOW)
2194 R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2197 if (!visiblevolumes)
2199 if (shadow && gl_stencil)
2200 R_Shadow_Stage_LightWithShadows();
2202 R_Shadow_Stage_LightWithoutShadows();
2204 ent = &cl_entities[0].render;
2205 if (ent->model && ent->model->DrawLight)
2207 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2208 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2209 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2210 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2211 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2212 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2214 //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2216 R_Mesh_Matrix(&ent->matrix);
2217 if (r_shadow_showtris.integer)
2220 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2221 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2222 qglDisable(GL_DEPTH_TEST);
2223 qglDisable(GL_STENCIL_TEST);
2224 //qglDisable(GL_CULL_FACE);
2225 memset(&m, 0, sizeof(m));
2226 R_Mesh_State_Texture(&m);
2227 GL_Color(0.2,0,0,1);
2228 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2229 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2231 GL_VertexPointer(mesh->vertex3f);
2232 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2234 //qglEnable(GL_CULL_FACE);
2236 qglEnable(GL_DEPTH_TEST);
2238 qglEnable(GL_STENCIL_TEST);
2240 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2242 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, cubemaptexture);
2243 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, cubemaptexture);
2247 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2249 if (r_drawentities.integer)
2251 for (i = 0;i < r_refdef.numentities;i++)
2253 ent = r_refdef.entities[i];
2254 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
2255 && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2256 && (ent->flags & RENDER_LIGHT))
2258 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2259 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2260 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2261 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2262 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2263 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2270 void R_ShadowVolumeLighting(int visiblevolumes)
2278 memset(&m, 0, sizeof(m));
2279 R_Mesh_State_Texture(&m);
2281 GL_BlendFunc(GL_ONE, GL_ONE);
2282 GL_DepthMask(false);
2283 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2284 qglDisable(GL_CULL_FACE);
2285 GL_Color(0.0, 0.0125, 0.1, 1);
2288 R_Shadow_Stage_Begin();
2290 if (r_shadow_realtime_world.integer)
2292 R_Shadow_LoadWorldLightsIfNeeded();
2293 if (r_shadow_debuglight.integer >= 0)
2295 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2296 if (lnum == r_shadow_debuglight.integer)
2297 R_DrawRTLight(&light->rtlight, visiblevolumes);
2300 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2301 R_DrawRTLight(&light->rtlight, visiblevolumes);
2303 if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2304 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2305 R_DrawRTLight(&light->rtlight, visiblevolumes);
2309 qglEnable(GL_CULL_FACE);
2310 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2313 R_Shadow_Stage_End();
2316 cvar_t r_editlights = {0, "r_editlights", "0"};
2317 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2318 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2319 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2320 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2321 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2322 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2323 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2324 dlight_t *r_shadow_worldlightchain;
2325 dlight_t *r_shadow_selectedlight;
2326 vec3_t r_editlights_cursorlocation;
2328 typedef struct cubemapinfo_s
2331 rtexture_t *texture;
2335 #define MAX_CUBEMAPS 128
2336 static int numcubemaps;
2337 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2339 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2340 typedef struct suffixinfo_s
2343 int flipx, flipy, flipdiagonal;
2346 static suffixinfo_t suffix[3][6] =
2349 {"posx", false, false, false},
2350 {"negx", false, false, false},
2351 {"posy", false, false, false},
2352 {"negy", false, false, false},
2353 {"posz", false, false, false},
2354 {"negz", false, false, false}
2357 {"px", false, false, false},
2358 {"nx", false, false, false},
2359 {"py", false, false, false},
2360 {"ny", false, false, false},
2361 {"pz", false, false, false},
2362 {"nz", false, false, false}
2365 {"ft", true, false, true},
2366 {"bk", false, true, true},
2367 {"lf", true, true, false},
2368 {"rt", false, false, false},
2369 {"up", false, false, false},
2370 {"dn", false, false, false}
2374 static int componentorder[4] = {0, 1, 2, 3};
2376 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2378 int i, j, cubemapsize;
2379 qbyte *cubemappixels, *image_rgba;
2380 rtexture_t *cubemaptexture;
2382 // must start 0 so the first loadimagepixels has no requested width/height
2384 cubemappixels = NULL;
2385 cubemaptexture = NULL;
2386 for (j = 0;j < 3 && !cubemappixels;j++)
2388 for (i = 0;i < 6;i++)
2390 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2391 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2393 if (image_width == image_height)
2395 if (!cubemappixels && image_width >= 1)
2397 cubemapsize = image_width;
2398 // note this clears to black, so unavailable sizes are black
2399 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2402 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
2405 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2406 Mem_Free(image_rgba);
2412 if (!r_shadow_filters_texturepool)
2413 r_shadow_filters_texturepool = R_AllocTexturePool();
2414 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2415 Mem_Free(cubemappixels);
2419 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2420 for (j = 0;j < 3;j++)
2421 for (i = 0;i < 6;i++)
2422 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2423 Con_Printf(" and was unable to find any of them.\n");
2425 return cubemaptexture;
2428 rtexture_t *R_Shadow_Cubemap(const char *basename)
2431 for (i = 0;i < numcubemaps;i++)
2432 if (!strcasecmp(cubemaps[i].basename, basename))
2433 return cubemaps[i].texture;
2434 if (i >= MAX_CUBEMAPS)
2437 strcpy(cubemaps[i].basename, basename);
2438 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2439 return cubemaps[i].texture;
2442 void R_Shadow_FreeCubemaps(void)
2445 R_FreeTexturePool(&r_shadow_filters_texturepool);
2448 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
2452 if (radius < 15 || DotProduct(color, color) < 0.03)
2454 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2458 light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2459 VectorCopy(origin, light->origin);
2460 VectorCopy(angles, light->angles);
2461 VectorCopy(color, light->color);
2462 light->radius = radius;
2463 light->style = style;
2464 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2466 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2469 light->shadow = shadowenable;
2470 light->corona = corona;
2471 if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2472 strcpy(light->cubemapname, cubemapname);
2473 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2474 light->next = r_shadow_worldlightchain;
2475 r_shadow_worldlightchain = light;
2477 R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2478 if (r_shadow_staticworldlights.integer)
2479 R_RTLight_Compile(&light->rtlight);
2482 void R_Shadow_FreeWorldLight(dlight_t *light)
2484 dlight_t **lightpointer;
2485 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2486 if (*lightpointer != light)
2487 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2488 *lightpointer = light->next;
2489 R_RTLight_Uncompile(&light->rtlight);
2493 void R_Shadow_ClearWorldLights(void)
2495 while (r_shadow_worldlightchain)
2496 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2497 r_shadow_selectedlight = NULL;
2498 R_Shadow_FreeCubemaps();
2501 void R_Shadow_SelectLight(dlight_t *light)
2503 if (r_shadow_selectedlight)
2504 r_shadow_selectedlight->selected = false;
2505 r_shadow_selectedlight = light;
2506 if (r_shadow_selectedlight)
2507 r_shadow_selectedlight->selected = true;
2510 rtexture_t *lighttextures[5];
2512 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2514 float scale = r_editlights_cursorgrid.value * 0.5f;
2515 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2518 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2521 const dlight_t *light;
2524 if (light->selected)
2525 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2528 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2531 void R_Shadow_DrawLightSprites(void)
2537 for (i = 0;i < 5;i++)
2539 lighttextures[i] = NULL;
2540 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2541 lighttextures[i] = pic->tex;
2544 for (light = r_shadow_worldlightchain;light;light = light->next)
2545 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2546 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2549 void R_Shadow_SelectLightInView(void)
2551 float bestrating, rating, temp[3];
2552 dlight_t *best, *light;
2555 for (light = r_shadow_worldlightchain;light;light = light->next)
2557 VectorSubtract(light->origin, r_vieworigin, temp);
2558 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2561 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2562 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2564 bestrating = rating;
2569 R_Shadow_SelectLight(best);
2572 void R_Shadow_LoadWorldLights(void)
2574 int n, a, style, shadow;
2575 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2576 float origin[3], radius, color[3], angles[3], corona;
2577 if (cl.worldmodel == NULL)
2579 Con_Printf("No map loaded.\n");
2582 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2583 strlcat (name, ".rtlights", sizeof (name));
2584 lightsstring = FS_LoadFile(name, false);
2592 while (*s && *s != '\n')
2598 // check for modifier flags
2604 a = sscanf(t, "%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2606 VectorClear(angles);
2614 Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2])\n", a, n + 1);
2617 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2618 radius *= r_editlights_rtlightssizescale.value;
2619 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2624 Con_Printf("invalid rtlights file \"%s\"\n", name);
2625 Mem_Free(lightsstring);
2629 void R_Shadow_SaveWorldLights(void)
2632 int bufchars, bufmaxchars;
2634 char name[MAX_QPATH];
2636 if (!r_shadow_worldlightchain)
2638 if (cl.worldmodel == NULL)
2640 Con_Printf("No map loaded.\n");
2643 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2644 strlcat (name, ".rtlights", sizeof (name));
2645 bufchars = bufmaxchars = 0;
2647 for (light = r_shadow_worldlightchain;light;light = light->next)
2649 sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "", light->corona, light->angles[0], light->angles[1], light->angles[2]);
2650 if (bufchars + (int) strlen(line) > bufmaxchars)
2652 bufmaxchars = bufchars + strlen(line) + 2048;
2654 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2658 memcpy(buf, oldbuf, bufchars);
2664 memcpy(buf + bufchars, line, strlen(line));
2665 bufchars += strlen(line);
2669 FS_WriteFile(name, buf, bufchars);
2674 void R_Shadow_LoadLightsFile(void)
2677 char name[MAX_QPATH], *lightsstring, *s, *t;
2678 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2679 if (cl.worldmodel == NULL)
2681 Con_Printf("No map loaded.\n");
2684 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2685 strlcat (name, ".lights", sizeof (name));
2686 lightsstring = FS_LoadFile(name, false);
2694 while (*s && *s != '\n')
2699 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &origin[0], &origin[1], &origin[2], &falloff, &color[0], &color[1], &color[2], &subtract, &spotdir[0], &spotdir[1], &spotdir[2], &spotcone, &distbias, &style);
2703 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
2706 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2707 radius = bound(15, radius, 4096);
2708 VectorScale(color, (2.0f / (8388608.0f)), color);
2709 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2714 Con_Printf("invalid lights file \"%s\"\n", name);
2715 Mem_Free(lightsstring);
2719 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2721 int entnum, style, islight, skin, pflags, effects;
2722 char key[256], value[1024];
2723 float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2726 if (cl.worldmodel == NULL)
2728 Con_Printf("No map loaded.\n");
2731 data = cl.worldmodel->brush.entities;
2734 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2737 origin[0] = origin[1] = origin[2] = 0;
2738 originhack[0] = originhack[1] = originhack[2] = 0;
2739 angles[0] = angles[1] = angles[2] = 0;
2740 color[0] = color[1] = color[2] = 1;
2741 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2751 if (!COM_ParseToken(&data, false))
2753 if (com_token[0] == '}')
2754 break; // end of entity
2755 if (com_token[0] == '_')
2756 strcpy(key, com_token + 1);
2758 strcpy(key, com_token);
2759 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2760 key[strlen(key)-1] = 0;
2761 if (!COM_ParseToken(&data, false))
2763 strcpy(value, com_token);
2765 // now that we have the key pair worked out...
2766 if (!strcmp("light", key))
2767 light = atof(value);
2768 else if (!strcmp("origin", key))
2769 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2770 else if (!strcmp("angle", key))
2771 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2772 else if (!strcmp("angles", key))
2773 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2774 else if (!strcmp("color", key))
2775 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2776 else if (!strcmp("wait", key))
2777 fadescale = atof(value);
2778 else if (!strcmp("classname", key))
2780 if (!strncmp(value, "light", 5))
2783 if (!strcmp(value, "light_fluoro"))
2788 overridecolor[0] = 1;
2789 overridecolor[1] = 1;
2790 overridecolor[2] = 1;
2792 if (!strcmp(value, "light_fluorospark"))
2797 overridecolor[0] = 1;
2798 overridecolor[1] = 1;
2799 overridecolor[2] = 1;
2801 if (!strcmp(value, "light_globe"))
2806 overridecolor[0] = 1;
2807 overridecolor[1] = 0.8;
2808 overridecolor[2] = 0.4;
2810 if (!strcmp(value, "light_flame_large_yellow"))
2815 overridecolor[0] = 1;
2816 overridecolor[1] = 0.5;
2817 overridecolor[2] = 0.1;
2819 if (!strcmp(value, "light_flame_small_yellow"))
2824 overridecolor[0] = 1;
2825 overridecolor[1] = 0.5;
2826 overridecolor[2] = 0.1;
2828 if (!strcmp(value, "light_torch_small_white"))
2833 overridecolor[0] = 1;
2834 overridecolor[1] = 0.5;
2835 overridecolor[2] = 0.1;
2837 if (!strcmp(value, "light_torch_small_walltorch"))
2842 overridecolor[0] = 1;
2843 overridecolor[1] = 0.5;
2844 overridecolor[2] = 0.1;
2848 else if (!strcmp("style", key))
2849 style = atoi(value);
2850 else if (cl.worldmodel->type == mod_brushq3)
2852 if (!strcmp("scale", key))
2853 lightscale = atof(value);
2854 if (!strcmp("fade", key))
2855 fadescale = atof(value);
2857 else if (!strcmp("skin", key))
2858 skin = (int)atof(value);
2859 else if (!strcmp("pflags", key))
2860 pflags = (int)atof(value);
2861 else if (!strcmp("effects", key))
2862 effects = (int)atof(value);
2864 if (light <= 0 && islight)
2866 if (lightscale <= 0)
2870 if (gamemode == GAME_TENEBRAE)
2872 if (effects & EF_NODRAW)
2874 pflags |= PFLAGS_FULLDYNAMIC;
2875 effects &= ~EF_NODRAW;
2878 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2879 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2880 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2881 VectorCopy(overridecolor, color);
2882 VectorScale(color, light, color);
2883 VectorAdd(origin, originhack, origin);
2884 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2885 R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2890 void R_Shadow_SetCursorLocationForView(void)
2892 vec_t dist, push, frac;
2893 vec3_t dest, endpos, normal;
2894 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2895 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2898 dist = frac * r_editlights_cursordistance.value;
2899 push = r_editlights_cursorpushback.value;
2903 VectorMA(endpos, push, r_viewforward, endpos);
2904 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2906 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2907 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2908 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2911 void R_Shadow_UpdateWorldLightSelection(void)
2913 if (r_editlights.integer)
2915 R_Shadow_SetCursorLocationForView();
2916 R_Shadow_SelectLightInView();
2917 R_Shadow_DrawLightSprites();
2920 R_Shadow_SelectLight(NULL);
2923 void R_Shadow_EditLights_Clear_f(void)
2925 R_Shadow_ClearWorldLights();
2928 void R_Shadow_EditLights_Reload_f(void)
2930 r_shadow_reloadlights = true;
2933 void R_Shadow_EditLights_Save_f(void)
2936 R_Shadow_SaveWorldLights();
2939 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2941 R_Shadow_ClearWorldLights();
2942 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2945 void R_Shadow_EditLights_ImportLightsFile_f(void)
2947 R_Shadow_ClearWorldLights();
2948 R_Shadow_LoadLightsFile();
2951 void R_Shadow_EditLights_Spawn_f(void)
2954 if (!r_editlights.integer)
2956 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2959 if (Cmd_Argc() != 1)
2961 Con_Printf("r_editlights_spawn does not take parameters\n");
2964 color[0] = color[1] = color[2] = 1;
2965 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2968 void R_Shadow_EditLights_Edit_f(void)
2970 vec3_t origin, angles, color;
2971 vec_t radius, corona;
2973 char cubemapname[1024];
2974 if (!r_editlights.integer)
2976 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2979 if (!r_shadow_selectedlight)
2981 Con_Printf("No selected light.\n");
2984 VectorCopy(r_shadow_selectedlight->origin, origin);
2985 VectorCopy(r_shadow_selectedlight->angles, angles);
2986 VectorCopy(r_shadow_selectedlight->color, color);
2987 radius = r_shadow_selectedlight->radius;
2988 style = r_shadow_selectedlight->style;
2989 if (r_shadow_selectedlight->cubemapname)
2990 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2993 shadows = r_shadow_selectedlight->shadow;
2994 corona = r_shadow_selectedlight->corona;
2995 if (!strcmp(Cmd_Argv(1), "origin"))
2997 if (Cmd_Argc() != 5)
2999 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3002 origin[0] = atof(Cmd_Argv(2));
3003 origin[1] = atof(Cmd_Argv(3));
3004 origin[2] = atof(Cmd_Argv(4));
3006 else if (!strcmp(Cmd_Argv(1), "originx"))
3008 if (Cmd_Argc() != 3)
3010 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3013 origin[0] = atof(Cmd_Argv(2));
3015 else if (!strcmp(Cmd_Argv(1), "originy"))
3017 if (Cmd_Argc() != 3)
3019 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3022 origin[1] = atof(Cmd_Argv(2));
3024 else if (!strcmp(Cmd_Argv(1), "originz"))
3026 if (Cmd_Argc() != 3)
3028 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3031 origin[2] = atof(Cmd_Argv(2));
3033 else if (!strcmp(Cmd_Argv(1), "move"))
3035 if (Cmd_Argc() != 5)
3037 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3040 origin[0] += atof(Cmd_Argv(2));
3041 origin[1] += atof(Cmd_Argv(3));
3042 origin[2] += atof(Cmd_Argv(4));
3044 else if (!strcmp(Cmd_Argv(1), "movex"))
3046 if (Cmd_Argc() != 3)
3048 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3051 origin[0] += atof(Cmd_Argv(2));
3053 else if (!strcmp(Cmd_Argv(1), "movey"))
3055 if (Cmd_Argc() != 3)
3057 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3060 origin[1] += atof(Cmd_Argv(2));
3062 else if (!strcmp(Cmd_Argv(1), "movez"))
3064 if (Cmd_Argc() != 3)
3066 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3069 origin[2] += atof(Cmd_Argv(2));
3071 if (!strcmp(Cmd_Argv(1), "angles"))
3073 if (Cmd_Argc() != 5)
3075 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3078 angles[0] = atof(Cmd_Argv(2));
3079 angles[1] = atof(Cmd_Argv(3));
3080 angles[2] = atof(Cmd_Argv(4));
3082 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3084 if (Cmd_Argc() != 3)
3086 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3089 angles[0] = atof(Cmd_Argv(2));
3091 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3093 if (Cmd_Argc() != 3)
3095 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3098 angles[1] = atof(Cmd_Argv(2));
3100 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3102 if (Cmd_Argc() != 3)
3104 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3107 angles[2] = atof(Cmd_Argv(2));
3109 else if (!strcmp(Cmd_Argv(1), "color"))
3111 if (Cmd_Argc() != 5)
3113 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3116 color[0] = atof(Cmd_Argv(2));
3117 color[1] = atof(Cmd_Argv(3));
3118 color[2] = atof(Cmd_Argv(4));
3120 else if (!strcmp(Cmd_Argv(1), "radius"))
3122 if (Cmd_Argc() != 3)
3124 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3127 radius = atof(Cmd_Argv(2));
3129 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
3131 if (Cmd_Argc() != 3)
3133 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3136 style = atoi(Cmd_Argv(2));
3138 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
3142 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3145 if (Cmd_Argc() == 3)
3146 strcpy(cubemapname, Cmd_Argv(2));
3150 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
3152 if (Cmd_Argc() != 3)
3154 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3157 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3159 else if (!strcmp(Cmd_Argv(1), "corona"))
3161 if (Cmd_Argc() != 3)
3163 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3166 corona = atof(Cmd_Argv(2));
3170 Con_Printf("usage: r_editlights_edit [property] [value]\n");
3171 Con_Printf("Selected light's properties:\n");
3172 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3173 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3174 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3175 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3176 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3177 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3178 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3179 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3182 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3183 r_shadow_selectedlight = NULL;
3184 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3187 extern int con_vislines;
3188 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3192 if (r_shadow_selectedlight == NULL)
3196 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3197 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;
3198 sprintf(temp, "Angles %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3199 sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3200 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3201 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3202 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3203 sprintf(temp, "Shadows %s", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3204 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3207 void R_Shadow_EditLights_ToggleShadow_f(void)
3209 if (!r_editlights.integer)
3211 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3214 if (!r_shadow_selectedlight)
3216 Con_Printf("No selected light.\n");
3219 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3220 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3221 r_shadow_selectedlight = NULL;
3224 void R_Shadow_EditLights_ToggleCorona_f(void)
3226 if (!r_editlights.integer)
3228 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3231 if (!r_shadow_selectedlight)
3233 Con_Printf("No selected light.\n");
3236 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3237 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3238 r_shadow_selectedlight = NULL;
3241 void R_Shadow_EditLights_Remove_f(void)
3243 if (!r_editlights.integer)
3245 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3248 if (!r_shadow_selectedlight)
3250 Con_Printf("No selected light.\n");
3253 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3254 r_shadow_selectedlight = NULL;
3257 void R_Shadow_EditLights_Help_f(void)
3260 "Documentation on r_editlights system:\n"
3262 "r_editlights : enable/disable editing mode\n"
3263 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3264 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3265 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3266 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3267 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3268 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3269 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3271 "r_editlights_help : this help\n"
3272 "r_editlights_clear : remove all lights\n"
3273 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3274 "r_editlights_save : save to .rtlights file\n"
3275 "r_editlights_spawn : create a light with default settings\n"
3276 "r_editlights_edit command : edit selected light - more documentation below\n"
3277 "r_editlights_remove : remove selected light\n"
3278 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3279 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3280 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3282 "origin x y z : set light location\n"
3283 "originx x: set x component of light location\n"
3284 "originy y: set y component of light location\n"
3285 "originz z: set z component of light location\n"
3286 "move x y z : adjust light location\n"
3287 "movex x: adjust x component of light location\n"
3288 "movey y: adjust y component of light location\n"
3289 "movez z: adjust z component of light location\n"
3290 "angles x y z : set light angles\n"
3291 "anglesx x: set x component of light angles\n"
3292 "anglesy y: set y component of light angles\n"
3293 "anglesz z: set z component of light angles\n"
3294 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3295 "radius radius : set radius (size) of light\n"
3296 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3297 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3298 "shadows 1/0 : turn on/off shadows\n"
3299 "corona n : set corona intensity\n"
3300 "<nothing> : print light properties to console\n"
3304 void R_Shadow_EditLights_Init(void)
3306 Cvar_RegisterVariable(&r_editlights);
3307 Cvar_RegisterVariable(&r_editlights_cursordistance);
3308 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3309 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3310 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3311 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3312 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3313 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3314 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3315 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3316 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3317 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3318 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3319 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3320 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3321 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3322 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3323 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3324 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);