3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however. Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
119 extern void R_Shadow_EditLights_Init(void);
121 #define SHADOWSTAGE_NONE 0
122 #define SHADOWSTAGE_STENCIL 1
123 #define SHADOWSTAGE_LIGHT 2
124 #define SHADOWSTAGE_ERASESTENCIL 3
126 int r_shadowstage = SHADOWSTAGE_NONE;
127 int r_shadow_reloadlights = false;
129 mempool_t *r_shadow_mempool;
131 int maxshadowelements;
133 int maxtrianglefacinglight;
134 qbyte *trianglefacinglight;
135 int *trianglefacinglightlist;
142 rtexturepool_t *r_shadow_texturepool;
143 rtexture_t *r_shadow_normalcubetexture;
144 rtexture_t *r_shadow_attenuation2dtexture;
145 rtexture_t *r_shadow_attenuation3dtexture;
146 rtexture_t *r_shadow_blankbumptexture;
147 rtexture_t *r_shadow_blankglosstexture;
148 rtexture_t *r_shadow_blankwhitetexture;
150 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
151 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
152 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
153 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
154 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
155 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
156 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
157 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
158 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
159 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
160 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
161 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
162 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
163 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
164 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
165 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
166 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
167 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
168 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
169 cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
170 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
172 int c_rt_lights, c_rt_clears, c_rt_scissored;
173 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
174 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
176 void R_Shadow_ClearWorldLights(void);
177 void R_Shadow_SaveWorldLights(void);
178 void R_Shadow_LoadWorldLights(void);
179 void R_Shadow_LoadLightsFile(void);
180 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
182 void r_shadow_start(void)
184 // allocate vertex processing arrays
185 r_shadow_mempool = Mem_AllocPool("R_Shadow");
186 maxshadowelements = 0;
187 shadowelements = NULL;
192 maxtrianglefacinglight = 0;
193 trianglefacinglight = NULL;
194 trianglefacinglightlist = NULL;
195 r_shadow_normalcubetexture = NULL;
196 r_shadow_attenuation2dtexture = NULL;
197 r_shadow_attenuation3dtexture = NULL;
198 r_shadow_blankbumptexture = NULL;
199 r_shadow_blankglosstexture = NULL;
200 r_shadow_blankwhitetexture = NULL;
201 r_shadow_texturepool = NULL;
202 R_Shadow_ClearWorldLights();
203 r_shadow_reloadlights = true;
206 void r_shadow_shutdown(void)
208 R_Shadow_ClearWorldLights();
209 r_shadow_reloadlights = true;
210 r_shadow_normalcubetexture = NULL;
211 r_shadow_attenuation2dtexture = NULL;
212 r_shadow_attenuation3dtexture = NULL;
213 r_shadow_blankbumptexture = NULL;
214 r_shadow_blankglosstexture = NULL;
215 r_shadow_blankwhitetexture = NULL;
216 R_FreeTexturePool(&r_shadow_texturepool);
217 maxshadowelements = 0;
218 shadowelements = NULL;
223 maxtrianglefacinglight = 0;
224 trianglefacinglight = NULL;
225 trianglefacinglightlist = NULL;
226 Mem_FreePool(&r_shadow_mempool);
229 void r_shadow_newmap(void)
231 R_Shadow_ClearWorldLights();
232 r_shadow_reloadlights = true;
235 void R_Shadow_Help_f(void)
238 "Documentation on r_shadow system:\n"
240 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
241 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
242 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
243 "r_shadow_realtime_world : use realtime world light rendering\n"
244 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
245 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
246 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
247 "r_shadow_glossintensity : brightness of textured gloss\n"
248 "r_shadow_gloss2intensity : brightness of forced gloss\n"
249 "r_shadow_debuglight : render only this light number (-1 = all)\n"
250 "r_shadow_scissor : use scissor optimization\n"
251 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
252 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
253 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
254 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
255 "r_shadow_portallight : use portal visibility for static light precomputation\n"
256 "r_shadow_projectdistance : shadow volume projection distance\n"
257 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
258 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
259 "r_shadow_shadows : dlight shadows (world always has shadows)\n"
261 "r_shadow_help : this help\n"
265 void R_Shadow_Init(void)
267 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
268 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
269 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
270 Cvar_RegisterVariable(&r_shadow_realtime_world);
271 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
272 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
273 Cvar_RegisterVariable(&r_shadow_gloss);
274 Cvar_RegisterVariable(&r_shadow_glossintensity);
275 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
276 Cvar_RegisterVariable(&r_shadow_debuglight);
277 Cvar_RegisterVariable(&r_shadow_scissor);
278 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
279 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
280 Cvar_RegisterVariable(&r_shadow_polygonfactor);
281 Cvar_RegisterVariable(&r_shadow_polygonoffset);
282 Cvar_RegisterVariable(&r_shadow_portallight);
283 Cvar_RegisterVariable(&r_shadow_projectdistance);
284 Cvar_RegisterVariable(&r_shadow_texture3d);
285 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
286 Cvar_RegisterVariable(&r_shadow_shadows);
287 Cvar_RegisterVariable(&r_shadow_showtris);
288 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
289 R_Shadow_EditLights_Init();
290 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
293 void R_Shadow_ResizeTriangleFacingLight(int numtris)
295 // make sure trianglefacinglight is big enough for this volume
296 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
297 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
298 if (maxtrianglefacinglight < numtris)
300 maxtrianglefacinglight = numtris;
301 if (trianglefacinglight)
302 Mem_Free(trianglefacinglight);
303 if (trianglefacinglightlist)
304 Mem_Free(trianglefacinglightlist);
305 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
306 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
310 int *R_Shadow_ResizeShadowElements(int numtris)
312 // make sure shadowelements is big enough for this volume
313 if (maxshadowelements < numtris * 24)
315 maxshadowelements = numtris * 24;
317 Mem_Free(shadowelements);
318 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
320 return shadowelements;
324 // readable version of some code found below
325 //if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
326 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
328 float dir0[3], dir1[3], normal[3];
330 // calculate two mostly perpendicular edge directions
331 VectorSubtract(a, b, dir0);
332 VectorSubtract(c, b, dir1);
334 // we have two edge directions, we can calculate a third vector from
335 // them, which is the direction of the surface normal (it's magnitude
337 CrossProduct(dir0, dir1, normal);
339 // compare distance of light along normal, with distance of any point
340 // of the triangle along the same normal (the triangle is planar,
341 // I.E. flat, so all points give the same answer)
342 return DotProduct(p, normal) > DotProduct(a, normal);
344 int checkcastshadowfromedge(int t, int i)
348 if (t >= trianglerange_start && t < trianglerange_end)
350 if (t < i && !trianglefacinglight[t])
361 te = inelement3i + t * 3;
362 v[0] = invertex3f + te[0] * 3;
363 v[1] = invertex3f + te[1] * 3;
364 v[2] = invertex3f + te[2] * 3;
365 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
374 int R_Shadow_ConstructShadowVolume(int innumvertices, int trianglerange_start, int trianglerange_end, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *relativelightorigin, float projectdistance)
376 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
378 const int *e, *n, *te;
381 // make sure trianglefacinglight is big enough for this volume
382 if (maxtrianglefacinglight < trianglerange_end)
383 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
385 if (maxvertexupdate < innumvertices)
387 maxvertexupdate = innumvertices;
389 Mem_Free(vertexupdate);
391 Mem_Free(vertexremap);
392 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
393 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
397 if (r_shadow_singlepassvolumegeneration.integer)
399 // one pass approach (identify lit/dark faces and generate sides while doing so)
400 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
402 // calculate triangle facing flag
403 v[0] = invertex3f + e[0] * 3;
404 v[1] = invertex3f + e[1] * 3;
405 v[2] = invertex3f + e[2] * 3;
406 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
408 // make sure the vertices are created
409 for (j = 0;j < 3;j++)
411 if (vertexupdate[e[j]] != vertexupdatenum)
413 vertexupdate[e[j]] = vertexupdatenum;
414 vertexremap[e[j]] = outvertices;
415 VectorCopy(v[j], outvertex3f);
416 VectorSubtract(v[j], relativelightorigin, temp);
417 f = projectdistance / VectorLength(temp);
418 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
423 // output the front and back triangles
424 vr[0] = vertexremap[e[0]];
425 vr[1] = vertexremap[e[1]];
426 vr[2] = vertexremap[e[2]];
427 outelement3i[0] = vr[0];
428 outelement3i[1] = vr[1];
429 outelement3i[2] = vr[2];
430 outelement3i[3] = vr[2] + 1;
431 outelement3i[4] = vr[1] + 1;
432 outelement3i[5] = vr[0] + 1;
435 // output the sides (facing outward from this triangle)
437 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
439 outelement3i[0] = vr[1];
440 outelement3i[1] = vr[0];
441 outelement3i[2] = vr[0] + 1;
442 outelement3i[3] = vr[1];
443 outelement3i[4] = vr[0] + 1;
444 outelement3i[5] = vr[1] + 1;
449 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
451 outelement3i[0] = vr[2];
452 outelement3i[1] = vr[1];
453 outelement3i[2] = vr[1] + 1;
454 outelement3i[3] = vr[2];
455 outelement3i[4] = vr[1] + 1;
456 outelement3i[5] = vr[2] + 1;
461 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
463 outelement3i[0] = vr[0];
464 outelement3i[1] = vr[2];
465 outelement3i[2] = vr[2] + 1;
466 outelement3i[3] = vr[0];
467 outelement3i[4] = vr[2] + 1;
468 outelement3i[5] = vr[0] + 1;
475 // this triangle is not facing the light
476 // output the sides (facing inward to this triangle)
478 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
480 vr[0] = vertexremap[e[0]];
481 vr[1] = vertexremap[e[1]];
482 outelement3i[0] = vr[1];
483 outelement3i[1] = vr[0] + 1;
484 outelement3i[2] = vr[0];
485 outelement3i[3] = vr[1];
486 outelement3i[4] = vr[1] + 1;
487 outelement3i[5] = vr[0] + 1;
492 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
494 vr[1] = vertexremap[e[1]];
495 vr[2] = vertexremap[e[2]];
496 outelement3i[0] = vr[2];
497 outelement3i[1] = vr[1] + 1;
498 outelement3i[2] = vr[1];
499 outelement3i[3] = vr[2];
500 outelement3i[4] = vr[2] + 1;
501 outelement3i[5] = vr[1] + 1;
506 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
508 vr[0] = vertexremap[e[0]];
509 vr[2] = vertexremap[e[2]];
510 outelement3i[0] = vr[0];
511 outelement3i[1] = vr[2] + 1;
512 outelement3i[2] = vr[2];
513 outelement3i[3] = vr[0];
514 outelement3i[4] = vr[0] + 1;
515 outelement3i[5] = vr[2] + 1;
524 // two pass approach (identify lit/dark faces and then generate sides)
525 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
527 // calculate triangle facing flag
528 v[0] = invertex3f + e[0] * 3;
529 v[1] = invertex3f + e[1] * 3;
530 v[2] = invertex3f + e[2] * 3;
531 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
533 trianglefacinglightlist[numfacing++] = i;
534 // make sure the vertices are created
535 for (j = 0;j < 3;j++)
537 if (vertexupdate[e[j]] != vertexupdatenum)
539 vertexupdate[e[j]] = vertexupdatenum;
540 vertexremap[e[j]] = outvertices;
541 VectorSubtract(v[j], relativelightorigin, temp);
542 f = projectdistance / VectorLength(temp);
543 VectorCopy(v[j], outvertex3f);
544 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
549 // output the front and back triangles
550 outelement3i[0] = vertexremap[e[0]];
551 outelement3i[1] = vertexremap[e[1]];
552 outelement3i[2] = vertexremap[e[2]];
553 outelement3i[3] = vertexremap[e[2]] + 1;
554 outelement3i[4] = vertexremap[e[1]] + 1;
555 outelement3i[5] = vertexremap[e[0]] + 1;
560 for (i = 0;i < numfacing;i++)
562 t = trianglefacinglightlist[i];
563 e = inelement3i + t * 3;
564 n = inneighbor3i + t * 3;
565 // output the sides (facing outward from this triangle)
567 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
569 vr[0] = vertexremap[e[0]];
570 vr[1] = vertexremap[e[1]];
571 outelement3i[0] = vr[1];
572 outelement3i[1] = vr[0];
573 outelement3i[2] = vr[0] + 1;
574 outelement3i[3] = vr[1];
575 outelement3i[4] = vr[0] + 1;
576 outelement3i[5] = vr[1] + 1;
581 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
583 vr[1] = vertexremap[e[1]];
584 vr[2] = vertexremap[e[2]];
585 outelement3i[0] = vr[2];
586 outelement3i[1] = vr[1];
587 outelement3i[2] = vr[1] + 1;
588 outelement3i[3] = vr[2];
589 outelement3i[4] = vr[1] + 1;
590 outelement3i[5] = vr[2] + 1;
595 if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
597 vr[0] = vertexremap[e[0]];
598 vr[2] = vertexremap[e[2]];
599 outelement3i[0] = vr[0];
600 outelement3i[1] = vr[2];
601 outelement3i[2] = vr[2] + 1;
602 outelement3i[3] = vr[0];
603 outelement3i[4] = vr[2] + 1;
604 outelement3i[5] = vr[0] + 1;
611 *outnumvertices = outvertices;
615 float varray_vertex3f2[65536*3];
617 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
620 if (projectdistance < 0.1)
622 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
628 // make sure shadowelements is big enough for this volume
629 if (maxshadowelements < numtris * 24)
630 R_Shadow_ResizeShadowElements(numtris);
632 // check which triangles are facing the light, and then output
633 // triangle elements and vertices... by clever use of elements we
634 // can construct the whole shadow from the unprojected vertices and
635 // the projected vertices
636 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
638 GL_VertexPointer(varray_vertex3f2);
639 if (r_shadowstage == SHADOWSTAGE_STENCIL)
641 // decrement stencil if frontface is behind depthbuffer
642 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
643 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
644 R_Mesh_Draw(outverts, tris, shadowelements);
646 c_rt_shadowtris += numtris;
647 // increment stencil if backface is behind depthbuffer
648 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
649 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
651 R_Mesh_Draw(outverts, tris, shadowelements);
653 c_rt_shadowtris += numtris;
657 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
660 if (r_shadowstage == SHADOWSTAGE_STENCIL)
662 // decrement stencil if frontface is behind depthbuffer
663 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
664 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
665 for (mesh = firstmesh;mesh;mesh = mesh->next)
667 GL_VertexPointer(mesh->vertex3f);
668 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
669 c_rtcached_shadowmeshes++;
670 c_rtcached_shadowtris += mesh->numtriangles;
672 // increment stencil if backface is behind depthbuffer
673 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
674 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
676 for (mesh = firstmesh;mesh;mesh = mesh->next)
678 GL_VertexPointer(mesh->vertex3f);
679 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
680 c_rtcached_shadowmeshes++;
681 c_rtcached_shadowtris += mesh->numtriangles;
685 float r_shadow_attenpower, r_shadow_attenscale;
686 static void R_Shadow_MakeTextures(void)
688 int x, y, z, d, side;
689 float v[3], s, t, intensity;
691 R_FreeTexturePool(&r_shadow_texturepool);
692 r_shadow_texturepool = R_AllocTexturePool();
693 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
694 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
696 #define ATTEN2DSIZE 64
697 #define ATTEN3DSIZE 32
698 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
703 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
708 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
713 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
714 if (gl_texturecubemap)
716 for (side = 0;side < 6;side++)
718 for (y = 0;y < NORMSIZE;y++)
720 for (x = 0;x < NORMSIZE;x++)
722 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
723 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
757 intensity = 127.0f / sqrt(DotProduct(v, v));
758 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
759 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
760 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
761 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
765 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
768 r_shadow_normalcubetexture = NULL;
769 for (y = 0;y < ATTEN2DSIZE;y++)
771 for (x = 0;x < ATTEN2DSIZE;x++)
773 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
774 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
776 intensity = 1.0f - sqrt(DotProduct(v, v));
778 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
779 d = bound(0, intensity, 255);
780 data[(y*ATTEN2DSIZE+x)*4+0] = d;
781 data[(y*ATTEN2DSIZE+x)*4+1] = d;
782 data[(y*ATTEN2DSIZE+x)*4+2] = d;
783 data[(y*ATTEN2DSIZE+x)*4+3] = d;
786 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
787 if (r_shadow_texture3d.integer)
789 for (z = 0;z < ATTEN3DSIZE;z++)
791 for (y = 0;y < ATTEN3DSIZE;y++)
793 for (x = 0;x < ATTEN3DSIZE;x++)
795 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
796 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
797 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
798 intensity = 1.0f - sqrt(DotProduct(v, v));
800 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
801 d = bound(0, intensity, 255);
802 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
803 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
804 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
805 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
809 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
814 void R_Shadow_Stage_Begin(void)
818 if (r_shadow_texture3d.integer && !gl_texture3d)
819 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
821 if (!r_shadow_attenuation2dtexture
822 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
823 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
824 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
825 R_Shadow_MakeTextures();
827 memset(&m, 0, sizeof(m));
828 GL_BlendFunc(GL_ONE, GL_ZERO);
831 R_Mesh_State_Texture(&m);
832 GL_Color(0, 0, 0, 1);
833 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
834 qglDisable(GL_SCISSOR_TEST);
835 r_shadowstage = SHADOWSTAGE_NONE;
837 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
838 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
839 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
842 void R_Shadow_LoadWorldLightsIfNeeded(void)
844 if (r_shadow_reloadlights && cl.worldmodel)
846 R_Shadow_ClearWorldLights();
847 r_shadow_reloadlights = false;
848 R_Shadow_LoadWorldLights();
849 if (r_shadow_worldlightchain == NULL)
851 R_Shadow_LoadLightsFile();
852 if (r_shadow_worldlightchain == NULL)
853 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
858 void R_Shadow_Stage_ShadowVolumes(void)
861 memset(&m, 0, sizeof(m));
862 R_Mesh_State_Texture(&m);
863 GL_Color(1, 1, 1, 1);
864 qglColorMask(0, 0, 0, 0);
865 GL_BlendFunc(GL_ONE, GL_ZERO);
868 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
869 //if (r_shadow_polygonoffset.value != 0)
871 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
872 // qglEnable(GL_POLYGON_OFFSET_FILL);
875 // qglDisable(GL_POLYGON_OFFSET_FILL);
876 qglDepthFunc(GL_LESS);
877 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
878 qglEnable(GL_STENCIL_TEST);
879 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
880 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
881 r_shadowstage = SHADOWSTAGE_STENCIL;
882 qglClear(GL_STENCIL_BUFFER_BIT);
884 // LordHavoc note: many shadow volumes reside entirely inside the world
885 // (that is to say they are entirely bounded by their lit surfaces),
886 // which can be optimized by handling things as an inverted light volume,
887 // with the shadow boundaries of the world being simulated by an altered
888 // (129) bias to stencil clearing on such lights
889 // FIXME: generate inverted light volumes for use as shadow volumes and
890 // optimize for them as noted above
893 void R_Shadow_Stage_LightWithoutShadows(void)
896 memset(&m, 0, sizeof(m));
897 R_Mesh_State_Texture(&m);
898 GL_BlendFunc(GL_ONE, GL_ONE);
901 qglPolygonOffset(0, 0);
902 //qglDisable(GL_POLYGON_OFFSET_FILL);
903 GL_Color(1, 1, 1, 1);
904 qglColorMask(1, 1, 1, 1);
905 qglDepthFunc(GL_EQUAL);
906 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
907 qglDisable(GL_STENCIL_TEST);
908 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
909 qglStencilFunc(GL_EQUAL, 128, 0xFF);
910 r_shadowstage = SHADOWSTAGE_LIGHT;
914 void R_Shadow_Stage_LightWithShadows(void)
917 memset(&m, 0, sizeof(m));
918 R_Mesh_State_Texture(&m);
919 GL_BlendFunc(GL_ONE, GL_ONE);
922 qglPolygonOffset(0, 0);
923 //qglDisable(GL_POLYGON_OFFSET_FILL);
924 GL_Color(1, 1, 1, 1);
925 qglColorMask(1, 1, 1, 1);
926 qglDepthFunc(GL_EQUAL);
927 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
928 qglEnable(GL_STENCIL_TEST);
929 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
930 // only draw light where this geometry was already rendered AND the
931 // stencil is 128 (values other than this mean shadow)
932 qglStencilFunc(GL_EQUAL, 128, 0xFF);
933 r_shadowstage = SHADOWSTAGE_LIGHT;
937 void R_Shadow_Stage_End(void)
940 memset(&m, 0, sizeof(m));
941 R_Mesh_State_Texture(&m);
942 GL_BlendFunc(GL_ONE, GL_ZERO);
945 qglPolygonOffset(0, 0);
946 //qglDisable(GL_POLYGON_OFFSET_FILL);
947 GL_Color(1, 1, 1, 1);
948 qglColorMask(1, 1, 1, 1);
949 qglDisable(GL_SCISSOR_TEST);
950 qglDepthFunc(GL_LEQUAL);
951 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
952 qglDisable(GL_STENCIL_TEST);
953 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
954 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
955 r_shadowstage = SHADOWSTAGE_NONE;
958 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
960 int i, ix1, iy1, ix2, iy2;
961 float x1, y1, x2, y2, x, y, f;
964 if (!r_shadow_scissor.integer)
966 // if view is inside the box, just say yes it's visible
967 // LordHavoc: for some odd reason scissor seems broken without stencil
968 // (?!? seems like a driver bug) so abort if gl_stencil is false
969 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
971 qglDisable(GL_SCISSOR_TEST);
974 for (i = 0;i < 3;i++)
976 if (r_viewforward[i] >= 0)
987 f = DotProduct(r_viewforward, r_vieworigin) + 1;
988 if (DotProduct(r_viewforward, v2) <= f)
990 // entirely behind nearclip plane
993 if (DotProduct(r_viewforward, v) >= f)
995 // entirely infront of nearclip plane
996 x1 = y1 = x2 = y2 = 0;
997 for (i = 0;i < 8;i++)
999 v[0] = (i & 1) ? mins[0] : maxs[0];
1000 v[1] = (i & 2) ? mins[1] : maxs[1];
1001 v[2] = (i & 4) ? mins[2] : maxs[2];
1003 GL_TransformToScreen(v, v2);
1004 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1023 // clipped by nearclip plane
1024 // this is nasty and crude...
1025 // create viewspace bbox
1026 for (i = 0;i < 8;i++)
1028 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1029 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1030 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1031 v2[0] = -DotProduct(v, r_viewleft);
1032 v2[1] = DotProduct(v, r_viewup);
1033 v2[2] = DotProduct(v, r_viewforward);
1036 if (smins[0] > v2[0]) smins[0] = v2[0];
1037 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1038 if (smins[1] > v2[1]) smins[1] = v2[1];
1039 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1040 if (smins[2] > v2[2]) smins[2] = v2[2];
1041 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1045 smins[0] = smaxs[0] = v2[0];
1046 smins[1] = smaxs[1] = v2[1];
1047 smins[2] = smaxs[2] = v2[2];
1050 // now we have a bbox in viewspace
1051 // clip it to the view plane
1054 // return true if that culled the box
1055 if (smins[2] >= smaxs[2])
1057 // ok some of it is infront of the view, transform each corner back to
1058 // worldspace and then to screenspace and make screen rect
1059 // initialize these variables just to avoid compiler warnings
1060 x1 = y1 = x2 = y2 = 0;
1061 for (i = 0;i < 8;i++)
1063 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1064 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1065 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1066 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1067 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1068 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1070 GL_TransformToScreen(v, v2);
1071 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1088 // this code doesn't handle boxes with any points behind view properly
1089 x1 = 1000;x2 = -1000;
1090 y1 = 1000;y2 = -1000;
1091 for (i = 0;i < 8;i++)
1093 v[0] = (i & 1) ? mins[0] : maxs[0];
1094 v[1] = (i & 2) ? mins[1] : maxs[1];
1095 v[2] = (i & 4) ? mins[2] : maxs[2];
1097 GL_TransformToScreen(v, v2);
1098 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1116 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1117 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1118 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1119 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1120 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1121 if (ix2 <= ix1 || iy2 <= iy1)
1123 // set up the scissor rectangle
1124 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1125 qglEnable(GL_SCISSOR_TEST);
1130 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1132 float *color4f = varray_color4f;
1133 float dist, dot, intensity, v[3], n[3];
1134 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1136 Matrix4x4_Transform(m, vertex3f, v);
1137 if ((dist = DotProduct(v, v)) < 1)
1139 Matrix4x4_Transform3x3(m, normal3f, n);
1140 if ((dot = DotProduct(n, v)) > 0)
1143 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1144 VectorScale(lightcolor, intensity, color4f);
1149 VectorClear(color4f);
1155 VectorClear(color4f);
1161 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1163 float *color4f = varray_color4f;
1164 float dist, dot, intensity, v[3], n[3];
1165 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1167 Matrix4x4_Transform(m, vertex3f, v);
1168 if ((dist = fabs(v[2])) < 1)
1170 Matrix4x4_Transform3x3(m, normal3f, n);
1171 if ((dot = DotProduct(n, v)) > 0)
1173 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1174 VectorScale(lightcolor, intensity, color4f);
1179 VectorClear(color4f);
1185 VectorClear(color4f);
1191 // FIXME: this should be done in a vertex program when possible
1192 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1193 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1197 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1198 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1199 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1206 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1210 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1211 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1218 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1222 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1224 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1225 // the cubemap normalizes this for us
1226 out3f[0] = DotProduct(svector3f, lightdir);
1227 out3f[1] = DotProduct(tvector3f, lightdir);
1228 out3f[2] = DotProduct(normal3f, lightdir);
1232 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1235 float lightdir[3], eyedir[3], halfdir[3];
1236 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1238 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1239 VectorNormalizeFast(lightdir);
1240 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1241 VectorNormalizeFast(eyedir);
1242 VectorAdd(lightdir, eyedir, halfdir);
1243 // the cubemap normalizes this for us
1244 out3f[0] = DotProduct(svector3f, halfdir);
1245 out3f[1] = DotProduct(tvector3f, halfdir);
1246 out3f[2] = DotProduct(normal3f, halfdir);
1250 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1253 float color[3], color2[3];
1255 GL_VertexPointer(vertex3f);
1256 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1259 bumptexture = r_shadow_blankbumptexture;
1261 // colorscale accounts for how much we multiply the brightness during combine
1262 // mult is how many times the final pass of the lighting will be
1263 // performed to get more brightness than otherwise possible
1264 // limit mult to 64 for sanity sake
1265 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1267 // 3/2 3D combine path (Geforce3, Radeon 8500)
1268 memset(&m, 0, sizeof(m));
1269 m.tex[0] = R_GetTexture(bumptexture);
1270 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1271 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1272 m.texcombinergb[0] = GL_REPLACE;
1273 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1274 m.pointer_texcoord[0] = texcoord2f;
1275 m.pointer_texcoord[1] = varray_texcoord3f[1];
1276 m.pointer_texcoord[2] = varray_texcoord3f[2];
1277 R_Mesh_State_Texture(&m);
1278 qglColorMask(0,0,0,1);
1279 GL_BlendFunc(GL_ONE, GL_ZERO);
1280 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1281 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1282 R_Mesh_Draw(numverts, numtriangles, elements);
1284 c_rt_lighttris += numtriangles;
1286 memset(&m, 0, sizeof(m));
1287 m.tex[0] = R_GetTexture(basetexture);
1288 m.texcubemap[1] = R_GetTexture(lightcubemap);
1289 m.pointer_texcoord[0] = texcoord2f;
1290 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1291 R_Mesh_State_Texture(&m);
1292 qglColorMask(1,1,1,0);
1293 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1295 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1296 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1297 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1299 color[0] = bound(0, color2[0], 1);
1300 color[1] = bound(0, color2[1], 1);
1301 color[2] = bound(0, color2[2], 1);
1302 GL_Color(color[0], color[1], color[2], 1);
1303 R_Mesh_Draw(numverts, numtriangles, elements);
1305 c_rt_lighttris += numtriangles;
1308 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1310 // 1/2/2 3D combine path (original Radeon)
1311 memset(&m, 0, sizeof(m));
1312 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1313 m.pointer_texcoord[0] = varray_texcoord3f[0];
1314 R_Mesh_State_Texture(&m);
1315 qglColorMask(0,0,0,1);
1316 GL_BlendFunc(GL_ONE, GL_ZERO);
1317 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1318 R_Mesh_Draw(numverts, numtriangles, elements);
1320 c_rt_lighttris += numtriangles;
1322 memset(&m, 0, sizeof(m));
1323 m.tex[0] = R_GetTexture(bumptexture);
1324 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1325 m.texcombinergb[0] = GL_REPLACE;
1326 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1327 m.pointer_texcoord[0] = texcoord2f;
1328 m.pointer_texcoord[1] = varray_texcoord3f[1];
1329 R_Mesh_State_Texture(&m);
1330 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1331 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1332 R_Mesh_Draw(numverts, numtriangles, elements);
1334 c_rt_lighttris += numtriangles;
1336 memset(&m, 0, sizeof(m));
1337 m.tex[0] = R_GetTexture(basetexture);
1338 m.texcubemap[1] = R_GetTexture(lightcubemap);
1339 m.pointer_texcoord[0] = texcoord2f;
1340 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1341 R_Mesh_State_Texture(&m);
1342 qglColorMask(1,1,1,0);
1343 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1345 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1346 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1347 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1349 color[0] = bound(0, color2[0], 1);
1350 color[1] = bound(0, color2[1], 1);
1351 color[2] = bound(0, color2[2], 1);
1352 GL_Color(color[0], color[1], color[2], 1);
1353 R_Mesh_Draw(numverts, numtriangles, elements);
1355 c_rt_lighttris += numtriangles;
1358 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1360 // 2/2 3D combine path (original Radeon)
1361 memset(&m, 0, sizeof(m));
1362 m.tex[0] = R_GetTexture(bumptexture);
1363 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1364 m.texcombinergb[0] = GL_REPLACE;
1365 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1366 m.pointer_texcoord[0] = texcoord2f;
1367 m.pointer_texcoord[1] = varray_texcoord3f[1];
1368 R_Mesh_State_Texture(&m);
1369 qglColorMask(0,0,0,1);
1370 GL_BlendFunc(GL_ONE, GL_ZERO);
1371 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1372 R_Mesh_Draw(numverts, numtriangles, elements);
1374 c_rt_lighttris += numtriangles;
1376 memset(&m, 0, sizeof(m));
1377 m.tex[0] = R_GetTexture(basetexture);
1378 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1379 m.pointer_texcoord[0] = texcoord2f;
1380 m.pointer_texcoord[1] = varray_texcoord3f[1];
1381 R_Mesh_State_Texture(&m);
1382 qglColorMask(1,1,1,0);
1383 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1384 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1385 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1386 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1388 color[0] = bound(0, color2[0], 1);
1389 color[1] = bound(0, color2[1], 1);
1390 color[2] = bound(0, color2[2], 1);
1391 GL_Color(color[0], color[1], color[2], 1);
1392 R_Mesh_Draw(numverts, numtriangles, elements);
1394 c_rt_lighttris += numtriangles;
1397 else if (r_textureunits.integer >= 4)
1399 // 4/2 2D combine path (Geforce3, Radeon 8500)
1400 memset(&m, 0, sizeof(m));
1401 m.tex[0] = R_GetTexture(bumptexture);
1402 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1403 m.texcombinergb[0] = GL_REPLACE;
1404 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1405 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1406 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1407 m.pointer_texcoord[0] = texcoord2f;
1408 m.pointer_texcoord[1] = varray_texcoord3f[1];
1409 m.pointer_texcoord[2] = varray_texcoord2f[2];
1410 m.pointer_texcoord[3] = varray_texcoord2f[3];
1411 R_Mesh_State_Texture(&m);
1412 qglColorMask(0,0,0,1);
1413 GL_BlendFunc(GL_ONE, GL_ZERO);
1414 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1415 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1416 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1417 R_Mesh_Draw(numverts, numtriangles, elements);
1419 c_rt_lighttris += numtriangles;
1421 memset(&m, 0, sizeof(m));
1422 m.tex[0] = R_GetTexture(basetexture);
1423 m.texcubemap[1] = R_GetTexture(lightcubemap);
1424 m.pointer_texcoord[0] = texcoord2f;
1425 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1426 R_Mesh_State_Texture(&m);
1427 qglColorMask(1,1,1,0);
1428 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1430 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1431 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1432 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
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;
1445 // 2/2/2 2D combine path (any dot3 card)
1446 memset(&m, 0, sizeof(m));
1447 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1448 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1449 m.pointer_texcoord[0] = varray_texcoord2f[0];
1450 m.pointer_texcoord[1] = varray_texcoord2f[1];
1451 R_Mesh_State_Texture(&m);
1452 qglColorMask(0,0,0,1);
1453 GL_BlendFunc(GL_ONE, GL_ZERO);
1454 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1455 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1456 R_Mesh_Draw(numverts, numtriangles, elements);
1458 c_rt_lighttris += numtriangles;
1460 memset(&m, 0, sizeof(m));
1461 m.tex[0] = R_GetTexture(bumptexture);
1462 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1463 m.texcombinergb[0] = GL_REPLACE;
1464 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1465 m.pointer_texcoord[0] = texcoord2f;
1466 m.pointer_texcoord[1] = varray_texcoord3f[1];
1467 R_Mesh_State_Texture(&m);
1468 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1469 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1470 R_Mesh_Draw(numverts, numtriangles, elements);
1472 c_rt_lighttris += numtriangles;
1474 memset(&m, 0, sizeof(m));
1475 m.tex[0] = R_GetTexture(basetexture);
1476 m.texcubemap[1] = R_GetTexture(lightcubemap);
1477 m.pointer_texcoord[0] = texcoord2f;
1478 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1479 R_Mesh_State_Texture(&m);
1480 qglColorMask(1,1,1,0);
1481 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1483 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1484 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1485 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1487 color[0] = bound(0, color2[0], 1);
1488 color[1] = bound(0, color2[1], 1);
1489 color[2] = bound(0, color2[2], 1);
1490 GL_Color(color[0], color[1], color[2], 1);
1491 R_Mesh_Draw(numverts, numtriangles, elements);
1493 c_rt_lighttris += numtriangles;
1499 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1500 GL_DepthMask(false);
1502 GL_ColorPointer(varray_color4f);
1503 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1504 memset(&m, 0, sizeof(m));
1505 m.tex[0] = R_GetTexture(basetexture);
1506 m.pointer_texcoord[0] = texcoord2f;
1507 if (r_textureunits.integer >= 2)
1510 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1511 m.pointer_texcoord[1] = varray_texcoord2f[1];
1512 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1514 R_Mesh_State_Texture(&m);
1515 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1517 color[0] = bound(0, color2[0], 1);
1518 color[1] = bound(0, color2[1], 1);
1519 color[2] = bound(0, color2[2], 1);
1520 if (r_textureunits.integer >= 2)
1521 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1523 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1524 R_Mesh_Draw(numverts, numtriangles, elements);
1526 c_rt_lighttris += numtriangles;
1531 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1534 float color[3], color2[3], colorscale;
1536 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1539 glosstexture = r_shadow_blankglosstexture;
1540 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1542 colorscale = r_shadow_glossintensity.value;
1544 bumptexture = r_shadow_blankbumptexture;
1545 if (glosstexture == r_shadow_blankglosstexture)
1546 colorscale *= r_shadow_gloss2intensity.value;
1547 GL_VertexPointer(vertex3f);
1549 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1551 // 2/0/0/1/2 3D combine blendsquare path
1552 memset(&m, 0, sizeof(m));
1553 m.tex[0] = R_GetTexture(bumptexture);
1554 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1555 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1556 m.pointer_texcoord[0] = texcoord2f;
1557 m.pointer_texcoord[1] = varray_texcoord3f[1];
1558 R_Mesh_State_Texture(&m);
1559 qglColorMask(0,0,0,1);
1560 // this squares the result
1561 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1562 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1563 R_Mesh_Draw(numverts, numtriangles, elements);
1565 c_rt_lighttris += numtriangles;
1567 memset(&m, 0, sizeof(m));
1568 R_Mesh_State_Texture(&m);
1569 // square alpha in framebuffer a few times to make it shiny
1570 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1571 // these comments are a test run through this math for intensity 0.5
1572 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1573 // 0.25 * 0.25 = 0.0625 (this is another pass)
1574 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1575 R_Mesh_Draw(numverts, numtriangles, elements);
1577 c_rt_lighttris += numtriangles;
1578 R_Mesh_Draw(numverts, numtriangles, elements);
1580 c_rt_lighttris += numtriangles;
1582 memset(&m, 0, sizeof(m));
1583 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1584 m.pointer_texcoord[0] = varray_texcoord3f[0];
1585 R_Mesh_State_Texture(&m);
1586 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1587 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1588 R_Mesh_Draw(numverts, numtriangles, elements);
1590 c_rt_lighttris += numtriangles;
1592 memset(&m, 0, sizeof(m));
1593 m.tex[0] = R_GetTexture(glosstexture);
1594 m.texcubemap[1] = R_GetTexture(lightcubemap);
1595 m.pointer_texcoord[0] = texcoord2f;
1596 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1597 R_Mesh_State_Texture(&m);
1598 qglColorMask(1,1,1,0);
1599 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1601 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1602 VectorScale(lightcolor, colorscale, color2);
1603 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1605 color[0] = bound(0, color2[0], 1);
1606 color[1] = bound(0, color2[1], 1);
1607 color[2] = bound(0, color2[2], 1);
1608 GL_Color(color[0], color[1], color[2], 1);
1609 R_Mesh_Draw(numverts, numtriangles, elements);
1611 c_rt_lighttris += numtriangles;
1614 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1616 // 2/0/0/2 3D combine blendsquare path
1617 memset(&m, 0, sizeof(m));
1618 m.tex[0] = R_GetTexture(bumptexture);
1619 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1620 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1621 m.pointer_texcoord[0] = texcoord2f;
1622 m.pointer_texcoord[1] = varray_texcoord3f[1];
1623 R_Mesh_State_Texture(&m);
1624 qglColorMask(0,0,0,1);
1625 // this squares the result
1626 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1627 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1628 R_Mesh_Draw(numverts, numtriangles, elements);
1630 c_rt_lighttris += numtriangles;
1632 memset(&m, 0, sizeof(m));
1633 R_Mesh_State_Texture(&m);
1634 // square alpha in framebuffer a few times to make it shiny
1635 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1636 // these comments are a test run through this math for intensity 0.5
1637 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1638 // 0.25 * 0.25 = 0.0625 (this is another pass)
1639 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1640 R_Mesh_Draw(numverts, numtriangles, elements);
1642 c_rt_lighttris += numtriangles;
1643 R_Mesh_Draw(numverts, numtriangles, elements);
1645 c_rt_lighttris += numtriangles;
1647 memset(&m, 0, sizeof(m));
1648 m.tex[0] = R_GetTexture(glosstexture);
1649 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1650 m.pointer_texcoord[0] = texcoord2f;
1651 m.pointer_texcoord[1] = varray_texcoord3f[1];
1652 R_Mesh_State_Texture(&m);
1653 qglColorMask(1,1,1,0);
1654 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1655 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1656 VectorScale(lightcolor, colorscale, color2);
1657 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1659 color[0] = bound(0, color2[0], 1);
1660 color[1] = bound(0, color2[1], 1);
1661 color[2] = bound(0, color2[2], 1);
1662 GL_Color(color[0], color[1], color[2], 1);
1663 R_Mesh_Draw(numverts, numtriangles, elements);
1665 c_rt_lighttris += numtriangles;
1668 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1670 // 2/0/0/2/2 2D combine blendsquare path
1671 memset(&m, 0, sizeof(m));
1672 m.tex[0] = R_GetTexture(bumptexture);
1673 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1674 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1675 m.pointer_texcoord[0] = texcoord2f;
1676 m.pointer_texcoord[1] = varray_texcoord3f[1];
1677 R_Mesh_State_Texture(&m);
1678 qglColorMask(0,0,0,1);
1679 // this squares the result
1680 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1681 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1682 R_Mesh_Draw(numverts, numtriangles, elements);
1684 c_rt_lighttris += numtriangles;
1686 memset(&m, 0, sizeof(m));
1687 R_Mesh_State_Texture(&m);
1688 // square alpha in framebuffer a few times to make it shiny
1689 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1690 // these comments are a test run through this math for intensity 0.5
1691 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1692 // 0.25 * 0.25 = 0.0625 (this is another pass)
1693 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1694 R_Mesh_Draw(numverts, numtriangles, elements);
1696 c_rt_lighttris += numtriangles;
1697 R_Mesh_Draw(numverts, numtriangles, elements);
1699 c_rt_lighttris += numtriangles;
1701 memset(&m, 0, sizeof(m));
1702 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1703 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1704 m.pointer_texcoord[0] = varray_texcoord2f[0];
1705 m.pointer_texcoord[1] = varray_texcoord2f[1];
1706 R_Mesh_State_Texture(&m);
1707 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1708 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1709 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1710 R_Mesh_Draw(numverts, numtriangles, elements);
1712 c_rt_lighttris += numtriangles;
1714 memset(&m, 0, sizeof(m));
1715 m.tex[0] = R_GetTexture(glosstexture);
1716 m.texcubemap[1] = R_GetTexture(lightcubemap);
1717 m.pointer_texcoord[0] = texcoord2f;
1718 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1719 R_Mesh_State_Texture(&m);
1720 qglColorMask(1,1,1,0);
1721 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1723 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1724 VectorScale(lightcolor, colorscale, color2);
1725 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1727 color[0] = bound(0, color2[0], 1);
1728 color[1] = bound(0, color2[1], 1);
1729 color[2] = bound(0, color2[2], 1);
1730 GL_Color(color[0], color[1], color[2], 1);
1731 R_Mesh_Draw(numverts, numtriangles, elements);
1733 c_rt_lighttris += numtriangles;
1739 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1741 R_Mesh_Matrix(matrix);
1742 if (r_shadow_showtris.integer)
1746 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1747 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1748 qglDisable(GL_DEPTH_TEST);
1749 qglDisable(GL_STENCIL_TEST);
1750 //qglDisable(GL_CULL_FACE);
1751 qglColorMask(1,1,1,1);
1752 memset(&m, 0, sizeof(m));
1753 R_Mesh_State_Texture(&m);
1754 GL_Color(0,0.1,0,1);
1755 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1756 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1758 GL_VertexPointer(mesh->vertex3f);
1759 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1761 //qglEnable(GL_CULL_FACE);
1763 qglEnable(GL_DEPTH_TEST);
1766 qglEnable(GL_STENCIL_TEST);
1767 qglColorMask(0,0,0,0);
1770 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1773 void R_Shadow_DrawStaticWorldLight_Light(worldlight_t *light, matrix4x4_t *matrix, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz)
1776 R_Mesh_Matrix(matrix);
1777 if (r_shadow_showtris.integer)
1780 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1781 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1782 qglDisable(GL_DEPTH_TEST);
1783 qglDisable(GL_STENCIL_TEST);
1784 //qglDisable(GL_CULL_FACE);
1785 memset(&m, 0, sizeof(m));
1786 R_Mesh_State_Texture(&m);
1787 GL_Color(0.2,0,0,1);
1788 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1789 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1791 GL_VertexPointer(mesh->vertex3f);
1792 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1794 //qglEnable(GL_CULL_FACE);
1796 qglEnable(GL_DEPTH_TEST);
1798 qglEnable(GL_STENCIL_TEST);
1800 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1802 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, NULL);
1803 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, NULL);
1807 cvar_t r_editlights = {0, "r_editlights", "0"};
1808 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1809 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1810 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1811 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1812 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1813 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1814 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1815 worldlight_t *r_shadow_worldlightchain;
1816 worldlight_t *r_shadow_selectedlight;
1817 vec3_t r_editlights_cursorlocation;
1819 static int lightpvsbytes;
1820 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1822 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1824 int i, j, k, l, maxverts = 256, tris;
1825 float *vertex3f = NULL, mins[3], maxs[3];
1827 shadowmesh_t *mesh, *castmesh = NULL;
1829 if (radius < 15 || DotProduct(color, color) < 0.03)
1831 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1835 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1836 VectorCopy(origin, e->origin);
1837 VectorCopy(color, e->light);
1838 e->lightradius = radius;
1840 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1842 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1845 e->castshadows = castshadow;
1847 e->cullradius = e->lightradius;
1848 for (k = 0;k < 3;k++)
1850 mins[k] = e->origin[k] - e->lightradius;
1851 maxs[k] = e->origin[k] + e->lightradius;
1854 e->next = r_shadow_worldlightchain;
1855 r_shadow_worldlightchain = e;
1856 if (cubemapname && cubemapname[0])
1858 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1859 strcpy(e->cubemapname, cubemapname);
1860 // FIXME: add cubemap loading (and don't load a cubemap twice)
1862 // FIXME: rewrite this to store ALL geometry into a cache in the light
1864 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1865 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1868 if (cl.worldmodel->brushq3.num_leafs)
1872 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1873 VectorCopy(e->origin, e->mins);
1874 VectorCopy(e->origin, e->maxs);
1875 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1876 face->lighttemp_castshadow = false;
1877 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1879 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1881 for (k = 0;k < 3;k++)
1883 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1884 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1886 for (j = 0;j < leaf->numleaffaces;j++)
1888 face = leaf->firstleafface[j];
1889 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1890 face->lighttemp_castshadow = true;
1895 // add surfaces to shadow casting mesh and light mesh
1896 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1898 if (face->lighttemp_castshadow)
1900 face->lighttemp_castshadow = false;
1901 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1904 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1905 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1906 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1907 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1912 else if (cl.worldmodel->brushq1.numleafs)
1916 VectorCopy(e->origin, e->mins);
1917 VectorCopy(e->origin, e->maxs);
1918 i = CL_PointQ1Contents(e->origin);
1920 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1921 surf->lighttemp_castshadow = false;
1923 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1926 qbyte *bytesurfacepvs;
1928 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1929 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1931 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1933 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1935 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1937 for (k = 0;k < 3;k++)
1939 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1940 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1945 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1946 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1947 surf->lighttemp_castshadow = true;
1949 Mem_Free(byteleafpvs);
1950 Mem_Free(bytesurfacepvs);
1954 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1955 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1957 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1959 for (k = 0;k < 3;k++)
1961 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1962 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1964 for (j = 0;j < leaf->nummarksurfaces;j++)
1966 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1967 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1968 surf->lighttemp_castshadow = true;
1974 // add surfaces to shadow casting mesh and light mesh
1975 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1977 if (surf->lighttemp_castshadow)
1979 surf->lighttemp_castshadow = false;
1980 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1981 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
1982 if (!(surf->flags & SURF_DRAWSKY))
1983 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1989 // limit box to light bounds (in case it grew larger)
1990 for (k = 0;k < 3;k++)
1992 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1993 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1995 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1997 // cast shadow volume from castmesh
1998 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2002 for (mesh = castmesh;mesh;mesh = mesh->next)
2004 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2005 maxverts = max(maxverts, mesh->numverts * 2);
2010 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2011 // now that we have the buffers big enough, construct and add
2012 // the shadow volume mesh
2014 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2015 for (mesh = castmesh;mesh;mesh = mesh->next)
2017 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2018 if ((tris = R_Shadow_ConstructShadowVolume(castmesh->numverts, 0, castmesh->numtriangles, castmesh->element3i, castmesh->neighbor3i, castmesh->vertex3f, NULL, shadowelements, vertex3f, e->origin, r_shadow_projectdistance.value)))
2019 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2024 // we're done with castmesh now
2025 Mod_ShadowMesh_Free(castmesh);
2028 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2029 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2032 if (e->meshchain_shadow)
2033 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2034 k += mesh->numtriangles;
2036 if (e->meshchain_light)
2037 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2038 l += mesh->numtriangles;
2039 Con_Printf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], k, l);
2042 void R_Shadow_FreeWorldLight(worldlight_t *light)
2044 worldlight_t **lightpointer;
2045 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2046 if (*lightpointer != light)
2047 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2048 *lightpointer = light->next;
2049 if (light->cubemapname)
2050 Mem_Free(light->cubemapname);
2051 if (light->meshchain_shadow)
2052 Mod_ShadowMesh_Free(light->meshchain_shadow);
2053 if (light->meshchain_light)
2054 Mod_ShadowMesh_Free(light->meshchain_light);
2058 void R_Shadow_ClearWorldLights(void)
2060 while (r_shadow_worldlightchain)
2061 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2062 r_shadow_selectedlight = NULL;
2065 void R_Shadow_SelectLight(worldlight_t *light)
2067 if (r_shadow_selectedlight)
2068 r_shadow_selectedlight->selected = false;
2069 r_shadow_selectedlight = light;
2070 if (r_shadow_selectedlight)
2071 r_shadow_selectedlight->selected = true;
2074 rtexture_t *lighttextures[5];
2076 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2078 float scale = r_editlights_cursorgrid.value * 0.5f;
2079 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2082 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2085 const worldlight_t *light;
2088 if (light->selected)
2089 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2090 if (!light->meshchain_shadow)
2092 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2095 void R_Shadow_DrawLightSprites(void)
2099 worldlight_t *light;
2101 for (i = 0;i < 5;i++)
2103 lighttextures[i] = NULL;
2104 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2105 lighttextures[i] = pic->tex;
2108 for (light = r_shadow_worldlightchain;light;light = light->next)
2109 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2110 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2113 void R_Shadow_SelectLightInView(void)
2115 float bestrating, rating, temp[3];
2116 worldlight_t *best, *light;
2119 for (light = r_shadow_worldlightchain;light;light = light->next)
2121 VectorSubtract(light->origin, r_vieworigin, temp);
2122 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2125 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2126 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2128 bestrating = rating;
2133 R_Shadow_SelectLight(best);
2136 void R_Shadow_LoadWorldLights(void)
2138 int n, a, style, shadow;
2139 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2140 float origin[3], radius, color[3];
2141 if (cl.worldmodel == NULL)
2143 Con_Printf("No map loaded.\n");
2146 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2147 strlcat (name, ".rtlights", sizeof (name));
2148 lightsstring = FS_LoadFile(name, false);
2156 while (*s && *s != '\n')
2162 // check for modifier flags
2168 a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname);
2174 Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
2177 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2178 radius *= r_editlights_rtlightssizescale.value;
2179 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2184 Con_Printf("invalid rtlights file \"%s\"\n", name);
2185 Mem_Free(lightsstring);
2189 void R_Shadow_SaveWorldLights(void)
2191 worldlight_t *light;
2192 int bufchars, bufmaxchars;
2194 char name[MAX_QPATH];
2196 if (!r_shadow_worldlightchain)
2198 if (cl.worldmodel == NULL)
2200 Con_Printf("No map loaded.\n");
2203 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2204 strlcat (name, ".rtlights", sizeof (name));
2205 bufchars = bufmaxchars = 0;
2207 for (light = r_shadow_worldlightchain;light;light = light->next)
2209 sprintf(line, "%s%f %f %f %f %f %f %f %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2210 if (bufchars + (int) strlen(line) > bufmaxchars)
2212 bufmaxchars = bufchars + strlen(line) + 2048;
2214 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2218 memcpy(buf, oldbuf, bufchars);
2224 memcpy(buf + bufchars, line, strlen(line));
2225 bufchars += strlen(line);
2229 FS_WriteFile(name, buf, bufchars);
2234 void R_Shadow_LoadLightsFile(void)
2237 char name[MAX_QPATH], *lightsstring, *s, *t;
2238 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2239 if (cl.worldmodel == NULL)
2241 Con_Printf("No map loaded.\n");
2244 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2245 strlcat (name, ".lights", sizeof (name));
2246 lightsstring = FS_LoadFile(name, false);
2254 while (*s && *s != '\n')
2259 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &origin[0], &origin[1], &origin[2], &falloff, &color[0], &color[1], &color[2], &subtract, &spotdir[0], &spotdir[1], &spotdir[2], &spotcone, &distbias, &style);
2263 Con_Printf("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
2266 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2267 radius = bound(15, radius, 4096);
2268 VectorScale(color, (2.0f / (8388608.0f)), color);
2269 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2274 Con_Printf("invalid lights file \"%s\"\n", name);
2275 Mem_Free(lightsstring);
2279 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2281 int entnum, style, islight;
2282 char key[256], value[1024];
2283 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2286 if (cl.worldmodel == NULL)
2288 Con_Printf("No map loaded.\n");
2291 data = cl.worldmodel->brush.entities;
2294 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2297 origin[0] = origin[1] = origin[2] = 0;
2298 originhack[0] = originhack[1] = originhack[2] = 0;
2299 color[0] = color[1] = color[2] = 1;
2300 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2307 if (!COM_ParseToken(&data, false))
2309 if (com_token[0] == '}')
2310 break; // end of entity
2311 if (com_token[0] == '_')
2312 strcpy(key, com_token + 1);
2314 strcpy(key, com_token);
2315 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2316 key[strlen(key)-1] = 0;
2317 if (!COM_ParseToken(&data, false))
2319 strcpy(value, com_token);
2321 // now that we have the key pair worked out...
2322 if (!strcmp("light", key))
2323 light = atof(value);
2324 else if (!strcmp("origin", key))
2325 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2326 else if (!strcmp("color", key))
2327 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2328 else if (!strcmp("wait", key))
2329 fadescale = atof(value);
2330 else if (!strcmp("classname", key))
2332 if (!strncmp(value, "light", 5))
2335 if (!strcmp(value, "light_fluoro"))
2340 overridecolor[0] = 1;
2341 overridecolor[1] = 1;
2342 overridecolor[2] = 1;
2344 if (!strcmp(value, "light_fluorospark"))
2349 overridecolor[0] = 1;
2350 overridecolor[1] = 1;
2351 overridecolor[2] = 1;
2353 if (!strcmp(value, "light_globe"))
2358 overridecolor[0] = 1;
2359 overridecolor[1] = 0.8;
2360 overridecolor[2] = 0.4;
2362 if (!strcmp(value, "light_flame_large_yellow"))
2367 overridecolor[0] = 1;
2368 overridecolor[1] = 0.5;
2369 overridecolor[2] = 0.1;
2371 if (!strcmp(value, "light_flame_small_yellow"))
2376 overridecolor[0] = 1;
2377 overridecolor[1] = 0.5;
2378 overridecolor[2] = 0.1;
2380 if (!strcmp(value, "light_torch_small_white"))
2385 overridecolor[0] = 1;
2386 overridecolor[1] = 0.5;
2387 overridecolor[2] = 0.1;
2389 if (!strcmp(value, "light_torch_small_walltorch"))
2394 overridecolor[0] = 1;
2395 overridecolor[1] = 0.5;
2396 overridecolor[2] = 0.1;
2400 else if (!strcmp("style", key))
2401 style = atoi(value);
2402 else if (cl.worldmodel->type == mod_brushq3)
2404 if (!strcmp("scale", key))
2405 lightscale = atof(value);
2406 if (!strcmp("fade", key))
2407 fadescale = atof(value);
2410 if (light <= 0 && islight)
2412 if (lightscale <= 0)
2416 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2417 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2418 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2419 VectorCopy(overridecolor, color);
2420 VectorScale(color, light, color);
2421 VectorAdd(origin, originhack, origin);
2423 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2428 void R_Shadow_SetCursorLocationForView(void)
2430 vec_t dist, push, frac;
2431 vec3_t dest, endpos, normal;
2432 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2433 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2436 dist = frac * r_editlights_cursordistance.value;
2437 push = r_editlights_cursorpushback.value;
2441 VectorMA(endpos, push, r_viewforward, endpos);
2442 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2444 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2445 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2446 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2449 void R_Shadow_UpdateWorldLightSelection(void)
2451 if (r_editlights.integer)
2453 R_Shadow_SetCursorLocationForView();
2454 R_Shadow_SelectLightInView();
2455 R_Shadow_DrawLightSprites();
2458 R_Shadow_SelectLight(NULL);
2461 void R_Shadow_EditLights_Clear_f(void)
2463 R_Shadow_ClearWorldLights();
2466 void R_Shadow_EditLights_Reload_f(void)
2468 r_shadow_reloadlights = true;
2471 void R_Shadow_EditLights_Save_f(void)
2474 R_Shadow_SaveWorldLights();
2477 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2479 R_Shadow_ClearWorldLights();
2480 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2483 void R_Shadow_EditLights_ImportLightsFile_f(void)
2485 R_Shadow_ClearWorldLights();
2486 R_Shadow_LoadLightsFile();
2489 void R_Shadow_EditLights_Spawn_f(void)
2492 if (!r_editlights.integer)
2494 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2497 if (Cmd_Argc() != 1)
2499 Con_Printf("r_editlights_spawn does not take parameters\n");
2502 color[0] = color[1] = color[2] = 1;
2503 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2506 void R_Shadow_EditLights_Edit_f(void)
2508 vec3_t origin, color;
2511 char cubemapname[1024];
2512 if (!r_editlights.integer)
2514 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2517 if (!r_shadow_selectedlight)
2519 Con_Printf("No selected light.\n");
2522 VectorCopy(r_shadow_selectedlight->origin, origin);
2523 radius = r_shadow_selectedlight->lightradius;
2524 VectorCopy(r_shadow_selectedlight->light, color);
2525 style = r_shadow_selectedlight->style;
2526 if (r_shadow_selectedlight->cubemapname)
2527 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2530 shadows = r_shadow_selectedlight->castshadows;
2531 if (!strcmp(Cmd_Argv(1), "origin"))
2533 if (Cmd_Argc() != 5)
2535 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2538 origin[0] = atof(Cmd_Argv(2));
2539 origin[1] = atof(Cmd_Argv(3));
2540 origin[2] = atof(Cmd_Argv(4));
2542 else if (!strcmp(Cmd_Argv(1), "originx"))
2544 if (Cmd_Argc() != 3)
2546 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2549 origin[0] = atof(Cmd_Argv(2));
2551 else if (!strcmp(Cmd_Argv(1), "originy"))
2553 if (Cmd_Argc() != 3)
2555 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2558 origin[1] = atof(Cmd_Argv(2));
2560 else if (!strcmp(Cmd_Argv(1), "originz"))
2562 if (Cmd_Argc() != 3)
2564 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2567 origin[2] = atof(Cmd_Argv(2));
2569 else if (!strcmp(Cmd_Argv(1), "move"))
2571 if (Cmd_Argc() != 5)
2573 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2576 origin[0] += atof(Cmd_Argv(2));
2577 origin[1] += atof(Cmd_Argv(3));
2578 origin[2] += atof(Cmd_Argv(4));
2580 else if (!strcmp(Cmd_Argv(1), "movex"))
2582 if (Cmd_Argc() != 3)
2584 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2587 origin[0] += atof(Cmd_Argv(2));
2589 else if (!strcmp(Cmd_Argv(1), "movey"))
2591 if (Cmd_Argc() != 3)
2593 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2596 origin[1] += atof(Cmd_Argv(2));
2598 else if (!strcmp(Cmd_Argv(1), "movez"))
2600 if (Cmd_Argc() != 3)
2602 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2605 origin[2] += atof(Cmd_Argv(2));
2607 else if (!strcmp(Cmd_Argv(1), "color"))
2609 if (Cmd_Argc() != 5)
2611 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2614 color[0] = atof(Cmd_Argv(2));
2615 color[1] = atof(Cmd_Argv(3));
2616 color[2] = atof(Cmd_Argv(4));
2618 else if (!strcmp(Cmd_Argv(1), "radius"))
2620 if (Cmd_Argc() != 3)
2622 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2625 radius = atof(Cmd_Argv(2));
2627 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2629 if (Cmd_Argc() != 3)
2631 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2634 style = atoi(Cmd_Argv(2));
2636 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2640 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2643 if (Cmd_Argc() == 3)
2644 strcpy(cubemapname, Cmd_Argv(2));
2648 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2650 if (Cmd_Argc() != 3)
2652 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2655 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2659 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2660 Con_Printf("Selected light's properties:\n");
2661 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2662 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2663 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2664 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2665 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2666 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2669 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2670 r_shadow_selectedlight = NULL;
2671 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2674 extern int con_vislines;
2675 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2679 if (r_shadow_selectedlight == NULL)
2683 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2684 sprintf(temp, "Origin %f %f %f", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2685 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2686 sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2687 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2688 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2689 sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2692 void R_Shadow_EditLights_ToggleShadow_f(void)
2694 if (!r_editlights.integer)
2696 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2699 if (!r_shadow_selectedlight)
2701 Con_Printf("No selected light.\n");
2704 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
2705 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2706 r_shadow_selectedlight = NULL;
2709 void R_Shadow_EditLights_Remove_f(void)
2711 if (!r_editlights.integer)
2713 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2716 if (!r_shadow_selectedlight)
2718 Con_Printf("No selected light.\n");
2721 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2722 r_shadow_selectedlight = NULL;
2725 void R_Shadow_EditLights_Help_f(void)
2728 "Documentation on r_editlights system:\n"
2730 "r_editlights : enable/disable editing mode\n"
2731 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2732 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2733 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2734 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2735 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2736 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2737 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2739 "r_editlights_help : this help\n"
2740 "r_editlights_clear : remove all lights\n"
2741 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2742 "r_editlights_save : save to .rtlights file\n"
2743 "r_editlights_spawn : create a light with default settings\n"
2744 "r_editlights_edit command : edit selected light - more documentation below\n"
2745 "r_editlights_remove : remove selected light\n"
2746 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2747 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2748 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2750 "origin x y z : set light location\n"
2751 "originx x: set x component of light location\n"
2752 "originy y: set y component of light location\n"
2753 "originz z: set z component of light location\n"
2754 "move x y z : adjust light location\n"
2755 "movex x: adjust x component of light location\n"
2756 "movey y: adjust y component of light location\n"
2757 "movez z: adjust z component of light location\n"
2758 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2759 "radius radius : set radius (size) of light\n"
2760 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2761 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2762 "shadows 1/0 : turn on/off shadows\n"
2763 "<nothing> : print light properties to console\n"
2767 void R_Shadow_EditLights_Init(void)
2769 Cvar_RegisterVariable(&r_editlights);
2770 Cvar_RegisterVariable(&r_editlights_cursordistance);
2771 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2772 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2773 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2774 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2775 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2776 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2777 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2778 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2779 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2780 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2781 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2782 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2783 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2784 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2785 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2786 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);