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).
21 Terminology: Stencil Light Volume (sometimes called Light Volumes)
22 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
23 areas in shadow it contanis the areas in light, this can only be built
24 quickly for certain limited cases (such as portal visibility from a point),
25 but is quite useful for some effects (sunlight coming from sky polygons is
26 one possible example, translucent occluders is another example).
30 Terminology: Optimized Stencil Shadow Volume
31 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
32 no duplicate coverage of areas (no need to shadow an area twice), often this
33 greatly improves performance but is an operation too costly to use on moving
34 lights (however completely optimal Stencil Light Volumes can be constructed
39 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
40 Per pixel evaluation of lighting equations, at a bare minimum this involves
41 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
42 vector and surface normal, using a texture of the surface bumps, called a
43 NormalMap) if supported by hardware; in our case there is support for cards
44 which are incapable of DOT3, the quality is quite poor however. Additionally
45 it is desirable to have specular evaluation per pixel, per vertex
46 normalization of specular halfangle vectors causes noticable distortion but
47 is unavoidable on hardware without GL_ARB_fragment_program.
51 Terminology: Normalization CubeMap
52 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
53 encoded as RGB colors) for any possible direction, this technique allows per
54 pixel calculation of incidence vector for per pixel lighting purposes, which
55 would not otherwise be possible per pixel without GL_ARB_fragment_program.
59 Terminology: 2D Attenuation Texturing
60 A very crude approximation of light attenuation with distance which results
61 in cylindrical light shapes which fade vertically as a streak (some games
62 such as Doom3 allow this to be rotated to be less noticable in specific
63 cases), the technique is simply modulating lighting by two 2D textures (which
64 can be the same) on different axes of projection (XY and Z, typically), this
65 is the best technique available without 3D Attenuation Texturing or
66 GL_ARB_fragment_program technology.
70 Terminology: 3D Attenuation Texturing
71 A slightly crude approximation of light attenuation with distance, its flaws
72 are limited radius and resolution (performance tradeoffs).
76 Terminology: 3D Attenuation-Normalization Texturing
77 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
78 vectors shorter the lighting becomes darker, a very effective optimization of
79 diffuse lighting if 3D Attenuation Textures are already used.
83 Terminology: Light Cubemap Filtering
84 A technique for modeling non-uniform light distribution according to
85 direction, for example projecting a stained glass window image onto a wall,
86 this is done by texturing the lighting with a cubemap.
90 Terminology: Light Projection Filtering
91 A technique for modeling shadowing of light passing through translucent
92 surfaces, allowing stained glass windows and other effects to be done more
93 elegantly than possible with Light Cubemap Filtering by applying an occluder
94 texture to the lighting combined with a stencil light volume to limit the lit
95 area (this allows evaluating multiple translucent occluders in a scene).
99 Terminology: Doom3 Lighting
100 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
101 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
102 the (currently upcoming) game Doom3.
105 #include "quakedef.h"
106 #include "r_shadow.h"
107 #include "cl_collision.h"
110 extern void R_Shadow_EditLights_Init(void);
112 #define SHADOWSTAGE_NONE 0
113 #define SHADOWSTAGE_STENCIL 1
114 #define SHADOWSTAGE_LIGHT 2
115 #define SHADOWSTAGE_ERASESTENCIL 3
117 int r_shadowstage = SHADOWSTAGE_NONE;
118 int r_shadow_reloadlights = false;
120 mempool_t *r_shadow_mempool;
122 int maxshadowelements;
124 int maxtrianglefacinglight;
125 qbyte *trianglefacinglight;
126 int *trianglefacinglightlist;
133 rtexturepool_t *r_shadow_texturepool;
134 rtexture_t *r_shadow_normalcubetexture;
135 rtexture_t *r_shadow_attenuation2dtexture;
136 rtexture_t *r_shadow_attenuation3dtexture;
137 rtexture_t *r_shadow_blankbumptexture;
138 rtexture_t *r_shadow_blankglosstexture;
139 rtexture_t *r_shadow_blankwhitetexture;
141 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
142 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
143 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
144 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
145 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
146 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
147 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
148 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
149 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
150 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
151 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
152 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
153 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
154 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
155 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
156 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
157 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
158 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
160 int c_rt_lights, c_rt_clears, c_rt_scissored;
161 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
162 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
164 void R_Shadow_ClearWorldLights(void);
165 void R_Shadow_SaveWorldLights(void);
166 void R_Shadow_LoadWorldLights(void);
167 void R_Shadow_LoadLightsFile(void);
168 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
170 void r_shadow_start(void)
172 // allocate vertex processing arrays
173 r_shadow_mempool = Mem_AllocPool("R_Shadow");
174 maxshadowelements = 0;
175 shadowelements = NULL;
180 maxtrianglefacinglight = 0;
181 trianglefacinglight = NULL;
182 trianglefacinglightlist = NULL;
183 r_shadow_normalcubetexture = NULL;
184 r_shadow_attenuation2dtexture = NULL;
185 r_shadow_attenuation3dtexture = NULL;
186 r_shadow_blankbumptexture = NULL;
187 r_shadow_blankglosstexture = NULL;
188 r_shadow_blankwhitetexture = NULL;
189 r_shadow_texturepool = NULL;
190 R_Shadow_ClearWorldLights();
191 r_shadow_reloadlights = true;
194 void r_shadow_shutdown(void)
196 R_Shadow_ClearWorldLights();
197 r_shadow_reloadlights = true;
198 r_shadow_normalcubetexture = NULL;
199 r_shadow_attenuation2dtexture = NULL;
200 r_shadow_attenuation3dtexture = NULL;
201 r_shadow_blankbumptexture = NULL;
202 r_shadow_blankglosstexture = NULL;
203 r_shadow_blankwhitetexture = NULL;
204 R_FreeTexturePool(&r_shadow_texturepool);
205 maxshadowelements = 0;
206 shadowelements = NULL;
211 maxtrianglefacinglight = 0;
212 trianglefacinglight = NULL;
213 trianglefacinglightlist = NULL;
214 Mem_FreePool(&r_shadow_mempool);
217 void r_shadow_newmap(void)
219 R_Shadow_ClearWorldLights();
220 r_shadow_reloadlights = true;
223 void R_Shadow_Init(void)
225 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
226 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
227 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
228 Cvar_RegisterVariable(&r_shadow_realtime_world);
229 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
230 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
231 Cvar_RegisterVariable(&r_shadow_gloss);
232 Cvar_RegisterVariable(&r_shadow_glossintensity);
233 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
234 Cvar_RegisterVariable(&r_shadow_debuglight);
235 Cvar_RegisterVariable(&r_shadow_scissor);
236 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
237 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
238 Cvar_RegisterVariable(&r_shadow_polygonoffset);
239 Cvar_RegisterVariable(&r_shadow_portallight);
240 Cvar_RegisterVariable(&r_shadow_projectdistance);
241 Cvar_RegisterVariable(&r_shadow_texture3d);
242 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
243 R_Shadow_EditLights_Init();
244 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
247 void R_Shadow_ResizeTriangleFacingLight(int numtris)
249 // make sure trianglefacinglight is big enough for this volume
250 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
251 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
252 if (maxtrianglefacinglight < numtris)
254 maxtrianglefacinglight = numtris;
255 if (trianglefacinglight)
256 Mem_Free(trianglefacinglight);
257 if (trianglefacinglightlist)
258 Mem_Free(trianglefacinglightlist);
259 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
260 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
264 int *R_Shadow_ResizeShadowElements(int numtris)
266 // make sure shadowelements is big enough for this volume
267 if (maxshadowelements < numtris * 24)
269 maxshadowelements = numtris * 24;
271 Mem_Free(shadowelements);
272 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
274 return shadowelements;
278 // readable version of some code found below
279 //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]))))
280 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
282 float dir0[3], dir1[3], normal[3];
284 // calculate two mostly perpendicular edge directions
285 VectorSubtract(a, b, dir0);
286 VectorSubtract(c, b, dir1);
288 // we have two edge directions, we can calculate a third vector from
289 // them, which is the direction of the surface normal (it's magnitude
291 CrossProduct(dir0, dir1, normal);
293 // compare distance of light along normal, with distance of any point
294 // of the triangle along the same normal (the triangle is planar,
295 // I.E. flat, so all points give the same answer)
296 return DotProduct(p, normal) > DotProduct(a, normal);
298 int checkcastshadowfromedge(int t, int i)
302 if (t >= trianglerange_start && t < trianglerange_end)
304 if (t < i && !trianglefacinglight[t])
315 te = inelement3i + t * 3;
316 v[0] = invertex3f + te[0] * 3;
317 v[1] = invertex3f + te[1] * 3;
318 v[2] = invertex3f + te[2] * 3;
319 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
328 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)
330 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
332 const int *e, *n, *te;
335 // make sure trianglefacinglight is big enough for this volume
336 if (maxtrianglefacinglight < trianglerange_end)
337 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
339 if (maxvertexupdate < innumvertices)
341 maxvertexupdate = innumvertices;
343 Mem_Free(vertexupdate);
345 Mem_Free(vertexremap);
346 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
347 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
351 if (r_shadow_singlepassvolumegeneration.integer)
353 // one pass approach (identify lit/dark faces and generate sides while doing so)
354 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
356 // calculate triangle facing flag
357 v[0] = invertex3f + e[0] * 3;
358 v[1] = invertex3f + e[1] * 3;
359 v[2] = invertex3f + e[2] * 3;
360 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
362 // make sure the vertices are created
363 for (j = 0;j < 3;j++)
365 if (vertexupdate[e[j]] != vertexupdatenum)
367 vertexupdate[e[j]] = vertexupdatenum;
368 vertexremap[e[j]] = outvertices;
369 VectorCopy(v[j], outvertex3f);
370 VectorSubtract(v[j], relativelightorigin, temp);
371 f = projectdistance / VectorLength(temp);
372 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
377 // output the front and back triangles
378 vr[0] = vertexremap[e[0]];
379 vr[1] = vertexremap[e[1]];
380 vr[2] = vertexremap[e[2]];
381 outelement3i[0] = vr[0];
382 outelement3i[1] = vr[1];
383 outelement3i[2] = vr[2];
384 outelement3i[3] = vr[2] + 1;
385 outelement3i[4] = vr[1] + 1;
386 outelement3i[5] = vr[0] + 1;
389 // output the sides (facing outward from this triangle)
391 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]))))
393 outelement3i[0] = vr[1];
394 outelement3i[1] = vr[0];
395 outelement3i[2] = vr[0] + 1;
396 outelement3i[3] = vr[1];
397 outelement3i[4] = vr[0] + 1;
398 outelement3i[5] = vr[1] + 1;
403 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]))))
405 outelement3i[0] = vr[2];
406 outelement3i[1] = vr[1];
407 outelement3i[2] = vr[1] + 1;
408 outelement3i[3] = vr[2];
409 outelement3i[4] = vr[1] + 1;
410 outelement3i[5] = vr[2] + 1;
415 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]))))
417 outelement3i[0] = vr[0];
418 outelement3i[1] = vr[2];
419 outelement3i[2] = vr[2] + 1;
420 outelement3i[3] = vr[0];
421 outelement3i[4] = vr[2] + 1;
422 outelement3i[5] = vr[0] + 1;
429 // this triangle is not facing the light
430 // output the sides (facing inward to this triangle)
432 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
434 vr[0] = vertexremap[e[0]];
435 vr[1] = vertexremap[e[1]];
436 outelement3i[0] = vr[1];
437 outelement3i[1] = vr[0] + 1;
438 outelement3i[2] = vr[0];
439 outelement3i[3] = vr[1];
440 outelement3i[4] = vr[1] + 1;
441 outelement3i[5] = vr[0] + 1;
446 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
448 vr[1] = vertexremap[e[1]];
449 vr[2] = vertexremap[e[2]];
450 outelement3i[0] = vr[2];
451 outelement3i[1] = vr[1] + 1;
452 outelement3i[2] = vr[1];
453 outelement3i[3] = vr[2];
454 outelement3i[4] = vr[2] + 1;
455 outelement3i[5] = vr[1] + 1;
460 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
462 vr[0] = vertexremap[e[0]];
463 vr[2] = vertexremap[e[2]];
464 outelement3i[0] = vr[0];
465 outelement3i[1] = vr[2] + 1;
466 outelement3i[2] = vr[2];
467 outelement3i[3] = vr[0];
468 outelement3i[4] = vr[0] + 1;
469 outelement3i[5] = vr[2] + 1;
478 // two pass approach (identify lit/dark faces and then generate sides)
479 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
481 // calculate triangle facing flag
482 v[0] = invertex3f + e[0] * 3;
483 v[1] = invertex3f + e[1] * 3;
484 v[2] = invertex3f + e[2] * 3;
485 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
487 trianglefacinglightlist[numfacing++] = i;
488 // make sure the vertices are created
489 for (j = 0;j < 3;j++)
491 if (vertexupdate[e[j]] != vertexupdatenum)
493 vertexupdate[e[j]] = vertexupdatenum;
494 vertexremap[e[j]] = outvertices;
495 VectorSubtract(v[j], relativelightorigin, temp);
496 f = projectdistance / VectorLength(temp);
497 VectorCopy(v[j], outvertex3f);
498 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
503 // output the front and back triangles
504 outelement3i[0] = vertexremap[e[0]];
505 outelement3i[1] = vertexremap[e[1]];
506 outelement3i[2] = vertexremap[e[2]];
507 outelement3i[3] = vertexremap[e[2]] + 1;
508 outelement3i[4] = vertexremap[e[1]] + 1;
509 outelement3i[5] = vertexremap[e[0]] + 1;
514 for (i = 0;i < numfacing;i++)
516 t = trianglefacinglightlist[i];
517 e = inelement3i + t * 3;
518 n = inneighbor3i + t * 3;
519 // output the sides (facing outward from this triangle)
521 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]))))
523 vr[0] = vertexremap[e[0]];
524 vr[1] = vertexremap[e[1]];
525 outelement3i[0] = vr[1];
526 outelement3i[1] = vr[0];
527 outelement3i[2] = vr[0] + 1;
528 outelement3i[3] = vr[1];
529 outelement3i[4] = vr[0] + 1;
530 outelement3i[5] = vr[1] + 1;
535 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]))))
537 vr[1] = vertexremap[e[1]];
538 vr[2] = vertexremap[e[2]];
539 outelement3i[0] = vr[2];
540 outelement3i[1] = vr[1];
541 outelement3i[2] = vr[1] + 1;
542 outelement3i[3] = vr[2];
543 outelement3i[4] = vr[1] + 1;
544 outelement3i[5] = vr[2] + 1;
549 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]))))
551 vr[0] = vertexremap[e[0]];
552 vr[2] = vertexremap[e[2]];
553 outelement3i[0] = vr[0];
554 outelement3i[1] = vr[2];
555 outelement3i[2] = vr[2] + 1;
556 outelement3i[3] = vr[0];
557 outelement3i[4] = vr[2] + 1;
558 outelement3i[5] = vr[0] + 1;
565 *outnumvertices = outvertices;
569 float varray_vertex3f2[65536*3];
571 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
574 if (projectdistance < 0.1)
576 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
582 // make sure shadowelements is big enough for this volume
583 if (maxshadowelements < numtris * 24)
584 R_Shadow_ResizeShadowElements(numtris);
586 // check which triangles are facing the light, and then output
587 // triangle elements and vertices... by clever use of elements we
588 // can construct the whole shadow from the unprojected vertices and
589 // the projected vertices
590 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
592 GL_VertexPointer(varray_vertex3f2);
593 if (r_shadowstage == SHADOWSTAGE_STENCIL)
595 // increment stencil if backface is behind depthbuffer
596 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
597 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
598 R_Mesh_Draw(outverts, tris, shadowelements);
600 c_rt_shadowtris += numtris;
601 // decrement stencil if frontface is behind depthbuffer
602 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
603 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
605 R_Mesh_Draw(outverts, tris, shadowelements);
607 c_rt_shadowtris += numtris;
611 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
614 if (r_shadowstage == SHADOWSTAGE_STENCIL)
616 // increment stencil if backface is behind depthbuffer
617 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
618 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
619 for (mesh = firstmesh;mesh;mesh = mesh->next)
621 GL_VertexPointer(mesh->vertex3f);
622 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
623 c_rtcached_shadowmeshes++;
624 c_rtcached_shadowtris += mesh->numtriangles;
626 // decrement stencil if frontface is behind depthbuffer
627 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
628 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
630 for (mesh = firstmesh;mesh;mesh = mesh->next)
632 GL_VertexPointer(mesh->vertex3f);
633 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
634 c_rtcached_shadowmeshes++;
635 c_rtcached_shadowtris += mesh->numtriangles;
639 float r_shadow_attenpower, r_shadow_attenscale;
640 static void R_Shadow_MakeTextures(void)
642 int x, y, z, d, side;
643 float v[3], s, t, intensity;
645 R_FreeTexturePool(&r_shadow_texturepool);
646 r_shadow_texturepool = R_AllocTexturePool();
647 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
648 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
650 #define ATTEN2DSIZE 64
651 #define ATTEN3DSIZE 32
652 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
657 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
662 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
667 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
668 if (gl_texturecubemap)
670 for (side = 0;side < 6;side++)
672 for (y = 0;y < NORMSIZE;y++)
674 for (x = 0;x < NORMSIZE;x++)
676 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
677 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
711 intensity = 127.0f / sqrt(DotProduct(v, v));
712 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
713 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
714 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
715 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
719 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
722 r_shadow_normalcubetexture = NULL;
723 for (y = 0;y < ATTEN2DSIZE;y++)
725 for (x = 0;x < ATTEN2DSIZE;x++)
727 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
728 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
730 intensity = 1.0f - sqrt(DotProduct(v, v));
732 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
733 d = bound(0, intensity, 255);
734 data[(y*ATTEN2DSIZE+x)*4+0] = d;
735 data[(y*ATTEN2DSIZE+x)*4+1] = d;
736 data[(y*ATTEN2DSIZE+x)*4+2] = d;
737 data[(y*ATTEN2DSIZE+x)*4+3] = d;
740 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
741 if (r_shadow_texture3d.integer)
743 for (z = 0;z < ATTEN3DSIZE;z++)
745 for (y = 0;y < ATTEN3DSIZE;y++)
747 for (x = 0;x < ATTEN3DSIZE;x++)
749 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
750 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
751 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
752 intensity = 1.0f - sqrt(DotProduct(v, v));
754 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
755 d = bound(0, intensity, 255);
756 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
757 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
758 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
759 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
763 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
768 void R_Shadow_Stage_Begin(void)
772 if (r_shadow_texture3d.integer && !gl_texture3d)
773 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
775 //cl.worldmodel->brushq1.numlights = min(cl.worldmodel->brushq1.numlights, 1);
776 if (!r_shadow_attenuation2dtexture
777 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
778 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
779 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
780 R_Shadow_MakeTextures();
782 memset(&m, 0, sizeof(m));
783 GL_BlendFunc(GL_ONE, GL_ZERO);
786 R_Mesh_State_Texture(&m);
787 GL_Color(0, 0, 0, 1);
788 qglDisable(GL_SCISSOR_TEST);
789 r_shadowstage = SHADOWSTAGE_NONE;
791 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
792 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
793 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
796 void R_Shadow_LoadWorldLightsIfNeeded(void)
798 if (r_shadow_reloadlights && cl.worldmodel)
800 R_Shadow_ClearWorldLights();
801 r_shadow_reloadlights = false;
802 R_Shadow_LoadWorldLights();
803 if (r_shadow_worldlightchain == NULL)
805 R_Shadow_LoadLightsFile();
806 if (r_shadow_worldlightchain == NULL)
807 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
812 void R_Shadow_Stage_ShadowVolumes(void)
815 memset(&m, 0, sizeof(m));
816 R_Mesh_State_Texture(&m);
817 GL_Color(1, 1, 1, 1);
818 qglColorMask(0, 0, 0, 0);
819 GL_BlendFunc(GL_ONE, GL_ZERO);
822 if (r_shadow_polygonoffset.value != 0)
824 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
825 qglEnable(GL_POLYGON_OFFSET_FILL);
828 qglDisable(GL_POLYGON_OFFSET_FILL);
829 qglDepthFunc(GL_LESS);
830 qglEnable(GL_STENCIL_TEST);
831 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
832 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
833 r_shadowstage = SHADOWSTAGE_STENCIL;
834 qglClear(GL_STENCIL_BUFFER_BIT);
836 // LordHavoc note: many shadow volumes reside entirely inside the world
837 // (that is to say they are entirely bounded by their lit surfaces),
838 // which can be optimized by handling things as an inverted light volume,
839 // with the shadow boundaries of the world being simulated by an altered
840 // (129) bias to stencil clearing on such lights
841 // FIXME: generate inverted light volumes for use as shadow volumes and
842 // optimize for them as noted above
845 void R_Shadow_Stage_LightWithoutShadows(void)
848 memset(&m, 0, sizeof(m));
849 R_Mesh_State_Texture(&m);
850 GL_BlendFunc(GL_ONE, GL_ONE);
853 qglDisable(GL_POLYGON_OFFSET_FILL);
854 GL_Color(1, 1, 1, 1);
855 qglColorMask(1, 1, 1, 1);
856 qglDepthFunc(GL_EQUAL);
857 qglDisable(GL_STENCIL_TEST);
858 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
859 qglStencilFunc(GL_EQUAL, 128, 0xFF);
860 r_shadowstage = SHADOWSTAGE_LIGHT;
864 void R_Shadow_Stage_LightWithShadows(void)
867 memset(&m, 0, sizeof(m));
868 R_Mesh_State_Texture(&m);
869 GL_BlendFunc(GL_ONE, GL_ONE);
872 qglDisable(GL_POLYGON_OFFSET_FILL);
873 GL_Color(1, 1, 1, 1);
874 qglColorMask(1, 1, 1, 1);
875 qglDepthFunc(GL_EQUAL);
876 qglEnable(GL_STENCIL_TEST);
877 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
878 // only draw light where this geometry was already rendered AND the
879 // stencil is 128 (values other than this mean shadow)
880 qglStencilFunc(GL_EQUAL, 128, 0xFF);
881 r_shadowstage = SHADOWSTAGE_LIGHT;
885 void R_Shadow_Stage_End(void)
888 memset(&m, 0, sizeof(m));
889 R_Mesh_State_Texture(&m);
890 GL_BlendFunc(GL_ONE, GL_ZERO);
893 qglDisable(GL_POLYGON_OFFSET_FILL);
894 GL_Color(1, 1, 1, 1);
895 qglColorMask(1, 1, 1, 1);
896 qglDisable(GL_SCISSOR_TEST);
897 qglDepthFunc(GL_LEQUAL);
898 qglDisable(GL_STENCIL_TEST);
899 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
900 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
901 r_shadowstage = SHADOWSTAGE_NONE;
905 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
907 int i, ix1, iy1, ix2, iy2;
908 float x1, y1, x2, y2, x, y;
911 if (!r_shadow_scissor.integer)
913 // if view is inside the box, just say yes it's visible
914 if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
915 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
916 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
918 qglDisable(GL_SCISSOR_TEST);
921 VectorSubtract(r_origin, origin, v);
922 if (DotProduct(v, v) < radius * radius)
924 qglDisable(GL_SCISSOR_TEST);
927 // create viewspace bbox
928 for (i = 0;i < 8;i++)
930 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
931 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
932 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
933 v2[0] = DotProduct(v, vright);
934 v2[1] = DotProduct(v, vup);
935 v2[2] = DotProduct(v, vpn);
938 if (smins[0] > v2[0]) smins[0] = v2[0];
939 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
940 if (smins[1] > v2[1]) smins[1] = v2[1];
941 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
942 if (smins[2] > v2[2]) smins[2] = v2[2];
943 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
947 smins[0] = smaxs[0] = v2[0];
948 smins[1] = smaxs[1] = v2[1];
949 smins[2] = smaxs[2] = v2[2];
952 // now we have a bbox in viewspace
953 // clip it to the viewspace version of the sphere
954 v[0] = origin[0] - r_origin[0];
955 v[1] = origin[1] - r_origin[1];
956 v[2] = origin[2] - r_origin[2];
957 v2[0] = DotProduct(v, vright);
958 v2[1] = DotProduct(v, vup);
959 v2[2] = DotProduct(v, vpn);
960 if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
961 if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
962 if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
963 if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
964 if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
965 if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
966 // clip it to the view plane
969 // return true if that culled the box
970 if (smins[2] >= smaxs[2])
972 // ok some of it is infront of the view, transform each corner back to
973 // worldspace and then to screenspace and make screen rect
974 // initialize these variables just to avoid compiler warnings
975 x1 = y1 = x2 = y2 = 0;
976 for (i = 0;i < 8;i++)
978 v2[0] = (i & 1) ? smins[0] : smaxs[0];
979 v2[1] = (i & 2) ? smins[1] : smaxs[1];
980 v2[2] = (i & 4) ? smins[2] : smaxs[2];
981 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
982 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
983 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
985 GL_TransformToScreen(v, v2);
986 //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]);
1003 // this code doesn't handle boxes with any points behind view properly
1004 x1 = 1000;x2 = -1000;
1005 y1 = 1000;y2 = -1000;
1006 for (i = 0;i < 8;i++)
1008 v[0] = (i & 1) ? mins[0] : maxs[0];
1009 v[1] = (i & 2) ? mins[1] : maxs[1];
1010 v[2] = (i & 4) ? mins[2] : maxs[2];
1012 GL_TransformToScreen(v, v2);
1013 //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]);
1030 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1031 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1032 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1033 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1034 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1035 if (ix2 <= ix1 || iy2 <= iy1)
1037 // set up the scissor rectangle
1038 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1039 qglEnable(GL_SCISSOR_TEST);
1045 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1047 int i, ix1, iy1, ix2, iy2;
1048 float x1, y1, x2, y2, x, y, f;
1049 vec3_t smins, smaxs;
1051 if (!r_shadow_scissor.integer)
1053 // if view is inside the box, just say yes it's visible
1054 // LordHavoc: for some odd reason scissor seems broken without stencil
1055 // (?!? seems like a driver bug) so abort if gl_stencil is false
1056 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1058 qglDisable(GL_SCISSOR_TEST);
1061 for (i = 0;i < 3;i++)
1074 f = DotProduct(vpn, r_origin) + 1;
1075 if (DotProduct(vpn, v2) <= f)
1077 // entirely behind nearclip plane
1080 if (DotProduct(vpn, v) >= f)
1082 // entirely infront of nearclip plane
1083 x1 = y1 = x2 = y2 = 0;
1084 for (i = 0;i < 8;i++)
1086 v[0] = (i & 1) ? mins[0] : maxs[0];
1087 v[1] = (i & 2) ? mins[1] : maxs[1];
1088 v[2] = (i & 4) ? mins[2] : maxs[2];
1090 GL_TransformToScreen(v, v2);
1091 //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]);
1110 // clipped by nearclip plane
1111 // this is nasty and crude...
1112 // create viewspace bbox
1113 for (i = 0;i < 8;i++)
1115 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1116 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1117 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1118 v2[0] = DotProduct(v, vright);
1119 v2[1] = DotProduct(v, vup);
1120 v2[2] = DotProduct(v, vpn);
1123 if (smins[0] > v2[0]) smins[0] = v2[0];
1124 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1125 if (smins[1] > v2[1]) smins[1] = v2[1];
1126 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1127 if (smins[2] > v2[2]) smins[2] = v2[2];
1128 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1132 smins[0] = smaxs[0] = v2[0];
1133 smins[1] = smaxs[1] = v2[1];
1134 smins[2] = smaxs[2] = v2[2];
1137 // now we have a bbox in viewspace
1138 // clip it to the view plane
1141 // return true if that culled the box
1142 if (smins[2] >= smaxs[2])
1144 // ok some of it is infront of the view, transform each corner back to
1145 // worldspace and then to screenspace and make screen rect
1146 // initialize these variables just to avoid compiler warnings
1147 x1 = y1 = x2 = y2 = 0;
1148 for (i = 0;i < 8;i++)
1150 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1151 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1152 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1153 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1154 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1155 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1157 GL_TransformToScreen(v, v2);
1158 //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]);
1175 // this code doesn't handle boxes with any points behind view properly
1176 x1 = 1000;x2 = -1000;
1177 y1 = 1000;y2 = -1000;
1178 for (i = 0;i < 8;i++)
1180 v[0] = (i & 1) ? mins[0] : maxs[0];
1181 v[1] = (i & 2) ? mins[1] : maxs[1];
1182 v[2] = (i & 4) ? mins[2] : maxs[2];
1184 GL_TransformToScreen(v, v2);
1185 //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]);
1203 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1204 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1205 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1206 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1207 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1208 if (ix2 <= ix1 || iy2 <= iy1)
1210 // set up the scissor rectangle
1211 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1212 qglEnable(GL_SCISSOR_TEST);
1217 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1219 float *color4f = varray_color4f;
1220 float dist, dot, intensity, v[3], n[3];
1221 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1223 Matrix4x4_Transform(m, vertex3f, v);
1224 if ((dist = DotProduct(v, v)) < 1)
1226 Matrix4x4_Transform3x3(m, normal3f, n);
1227 if ((dot = DotProduct(n, v)) > 0)
1230 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1231 VectorScale(lightcolor, intensity, color4f);
1236 VectorClear(color4f);
1242 VectorClear(color4f);
1248 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1250 float *color4f = varray_color4f;
1251 float dist, dot, intensity, v[3], n[3];
1252 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1254 Matrix4x4_Transform(m, vertex3f, v);
1255 if ((dist = fabs(v[2])) < 1)
1257 Matrix4x4_Transform3x3(m, normal3f, n);
1258 if ((dot = DotProduct(n, v)) > 0)
1260 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1261 VectorScale(lightcolor, intensity, color4f);
1266 VectorClear(color4f);
1272 VectorClear(color4f);
1278 // FIXME: this should be done in a vertex program when possible
1279 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1280 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1284 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1285 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1286 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1293 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1297 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1298 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1305 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)
1309 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1311 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1312 // the cubemap normalizes this for us
1313 out3f[0] = DotProduct(svector3f, lightdir);
1314 out3f[1] = DotProduct(tvector3f, lightdir);
1315 out3f[2] = DotProduct(normal3f, lightdir);
1319 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)
1322 float lightdir[3], eyedir[3], halfdir[3];
1323 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1325 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1326 VectorNormalizeFast(lightdir);
1327 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1328 VectorNormalizeFast(eyedir);
1329 VectorAdd(lightdir, eyedir, halfdir);
1330 // the cubemap normalizes this for us
1331 out3f[0] = DotProduct(svector3f, halfdir);
1332 out3f[1] = DotProduct(tvector3f, halfdir);
1333 out3f[2] = DotProduct(normal3f, halfdir);
1337 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)
1340 float color[3], color2[3];
1342 GL_VertexPointer(vertex3f);
1343 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1346 bumptexture = r_shadow_blankbumptexture;
1348 // colorscale accounts for how much we multiply the brightness during combine
1349 // mult is how many times the final pass of the lighting will be
1350 // performed to get more brightness than otherwise possible
1351 // limit mult to 64 for sanity sake
1352 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1354 // 3/2 3D combine path (Geforce3, Radeon 8500)
1355 memset(&m, 0, sizeof(m));
1356 m.tex[0] = R_GetTexture(bumptexture);
1357 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1358 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1359 m.texcombinergb[0] = GL_REPLACE;
1360 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1361 m.pointer_texcoord[0] = texcoord2f;
1362 m.pointer_texcoord[1] = varray_texcoord3f[1];
1363 m.pointer_texcoord[2] = varray_texcoord3f[2];
1364 R_Mesh_State_Texture(&m);
1365 qglColorMask(0,0,0,1);
1366 GL_BlendFunc(GL_ONE, GL_ZERO);
1367 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1368 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1369 R_Mesh_Draw(numverts, numtriangles, elements);
1371 c_rt_lighttris += numtriangles;
1373 memset(&m, 0, sizeof(m));
1374 m.tex[0] = R_GetTexture(basetexture);
1375 m.texcubemap[1] = R_GetTexture(lightcubemap);
1376 m.pointer_texcoord[0] = texcoord2f;
1377 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1378 R_Mesh_State_Texture(&m);
1379 qglColorMask(1,1,1,0);
1380 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1382 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1383 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1384 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1386 color[0] = bound(0, color2[0], 1);
1387 color[1] = bound(0, color2[1], 1);
1388 color[2] = bound(0, color2[2], 1);
1389 GL_Color(color[0], color[1], color[2], 1);
1390 R_Mesh_Draw(numverts, numtriangles, elements);
1392 c_rt_lighttris += numtriangles;
1395 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1397 // 1/2/2 3D combine path (original Radeon)
1398 memset(&m, 0, sizeof(m));
1399 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1400 m.pointer_texcoord[0] = varray_texcoord3f[0];
1401 R_Mesh_State_Texture(&m);
1402 qglColorMask(0,0,0,1);
1403 GL_BlendFunc(GL_ONE, GL_ZERO);
1404 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1405 R_Mesh_Draw(numverts, numtriangles, elements);
1407 c_rt_lighttris += numtriangles;
1409 memset(&m, 0, sizeof(m));
1410 m.tex[0] = R_GetTexture(bumptexture);
1411 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1412 m.texcombinergb[0] = GL_REPLACE;
1413 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1414 m.pointer_texcoord[0] = texcoord2f;
1415 m.pointer_texcoord[1] = varray_texcoord3f[1];
1416 R_Mesh_State_Texture(&m);
1417 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1418 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1419 R_Mesh_Draw(numverts, numtriangles, elements);
1421 c_rt_lighttris += numtriangles;
1423 memset(&m, 0, sizeof(m));
1424 m.tex[0] = R_GetTexture(basetexture);
1425 m.texcubemap[1] = R_GetTexture(lightcubemap);
1426 m.pointer_texcoord[0] = texcoord2f;
1427 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1428 R_Mesh_State_Texture(&m);
1429 qglColorMask(1,1,1,0);
1430 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1432 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1433 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1434 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1436 color[0] = bound(0, color2[0], 1);
1437 color[1] = bound(0, color2[1], 1);
1438 color[2] = bound(0, color2[2], 1);
1439 GL_Color(color[0], color[1], color[2], 1);
1440 R_Mesh_Draw(numverts, numtriangles, elements);
1442 c_rt_lighttris += numtriangles;
1445 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1447 // 2/2 3D combine path (original Radeon)
1448 memset(&m, 0, sizeof(m));
1449 m.tex[0] = R_GetTexture(bumptexture);
1450 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1451 m.texcombinergb[0] = GL_REPLACE;
1452 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1453 m.pointer_texcoord[0] = texcoord2f;
1454 m.pointer_texcoord[1] = varray_texcoord3f[1];
1455 R_Mesh_State_Texture(&m);
1456 qglColorMask(0,0,0,1);
1457 GL_BlendFunc(GL_ONE, GL_ZERO);
1458 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1459 R_Mesh_Draw(numverts, numtriangles, elements);
1461 c_rt_lighttris += numtriangles;
1463 memset(&m, 0, sizeof(m));
1464 m.tex[0] = R_GetTexture(basetexture);
1465 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1466 m.pointer_texcoord[0] = texcoord2f;
1467 m.pointer_texcoord[1] = varray_texcoord3f[1];
1468 R_Mesh_State_Texture(&m);
1469 qglColorMask(1,1,1,0);
1470 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1471 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1472 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1473 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1475 color[0] = bound(0, color2[0], 1);
1476 color[1] = bound(0, color2[1], 1);
1477 color[2] = bound(0, color2[2], 1);
1478 GL_Color(color[0], color[1], color[2], 1);
1479 R_Mesh_Draw(numverts, numtriangles, elements);
1481 c_rt_lighttris += numtriangles;
1484 else if (r_textureunits.integer >= 4)
1486 // 4/2 2D combine path (Geforce3, Radeon 8500)
1487 memset(&m, 0, sizeof(m));
1488 m.tex[0] = R_GetTexture(bumptexture);
1489 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1490 m.texcombinergb[0] = GL_REPLACE;
1491 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1492 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1493 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1494 m.pointer_texcoord[0] = texcoord2f;
1495 m.pointer_texcoord[1] = varray_texcoord3f[1];
1496 m.pointer_texcoord[2] = varray_texcoord2f[2];
1497 m.pointer_texcoord[3] = varray_texcoord2f[3];
1498 R_Mesh_State_Texture(&m);
1499 qglColorMask(0,0,0,1);
1500 GL_BlendFunc(GL_ONE, GL_ZERO);
1501 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1502 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1503 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1504 R_Mesh_Draw(numverts, numtriangles, elements);
1506 c_rt_lighttris += numtriangles;
1508 memset(&m, 0, sizeof(m));
1509 m.tex[0] = R_GetTexture(basetexture);
1510 m.texcubemap[1] = R_GetTexture(lightcubemap);
1511 m.pointer_texcoord[0] = texcoord2f;
1512 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1513 R_Mesh_State_Texture(&m);
1514 qglColorMask(1,1,1,0);
1515 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1517 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1518 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1519 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1521 color[0] = bound(0, color2[0], 1);
1522 color[1] = bound(0, color2[1], 1);
1523 color[2] = bound(0, color2[2], 1);
1524 GL_Color(color[0], color[1], color[2], 1);
1525 R_Mesh_Draw(numverts, numtriangles, elements);
1527 c_rt_lighttris += numtriangles;
1532 // 2/2/2 2D combine path (any dot3 card)
1533 memset(&m, 0, sizeof(m));
1534 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1535 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1536 m.pointer_texcoord[0] = varray_texcoord2f[0];
1537 m.pointer_texcoord[1] = varray_texcoord2f[1];
1538 R_Mesh_State_Texture(&m);
1539 qglColorMask(0,0,0,1);
1540 GL_BlendFunc(GL_ONE, GL_ZERO);
1541 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1542 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1543 R_Mesh_Draw(numverts, numtriangles, elements);
1545 c_rt_lighttris += numtriangles;
1547 memset(&m, 0, sizeof(m));
1548 m.tex[0] = R_GetTexture(bumptexture);
1549 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1550 m.texcombinergb[0] = GL_REPLACE;
1551 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1552 m.pointer_texcoord[0] = texcoord2f;
1553 m.pointer_texcoord[1] = varray_texcoord3f[1];
1554 R_Mesh_State_Texture(&m);
1555 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1556 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1557 R_Mesh_Draw(numverts, numtriangles, elements);
1559 c_rt_lighttris += numtriangles;
1561 memset(&m, 0, sizeof(m));
1562 m.tex[0] = R_GetTexture(basetexture);
1563 m.texcubemap[1] = R_GetTexture(lightcubemap);
1564 m.pointer_texcoord[0] = texcoord2f;
1565 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1566 R_Mesh_State_Texture(&m);
1567 qglColorMask(1,1,1,0);
1568 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1570 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1571 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1572 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1574 color[0] = bound(0, color2[0], 1);
1575 color[1] = bound(0, color2[1], 1);
1576 color[2] = bound(0, color2[2], 1);
1577 GL_Color(color[0], color[1], color[2], 1);
1578 R_Mesh_Draw(numverts, numtriangles, elements);
1580 c_rt_lighttris += numtriangles;
1586 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1587 GL_DepthMask(false);
1589 GL_ColorPointer(varray_color4f);
1590 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1591 memset(&m, 0, sizeof(m));
1592 m.tex[0] = R_GetTexture(basetexture);
1593 m.pointer_texcoord[0] = texcoord2f;
1594 if (r_textureunits.integer >= 2)
1597 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1598 m.pointer_texcoord[1] = varray_texcoord2f[1];
1599 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1601 R_Mesh_State_Texture(&m);
1602 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1604 color[0] = bound(0, color2[0], 1);
1605 color[1] = bound(0, color2[1], 1);
1606 color[2] = bound(0, color2[2], 1);
1607 if (r_textureunits.integer >= 2)
1608 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1610 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1611 R_Mesh_Draw(numverts, numtriangles, elements);
1613 c_rt_lighttris += numtriangles;
1618 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)
1621 float color[3], color2[3], colorscale;
1623 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1626 glosstexture = r_shadow_blankglosstexture;
1627 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1629 colorscale = r_colorscale * r_shadow_glossintensity.value;
1631 bumptexture = r_shadow_blankbumptexture;
1632 if (glosstexture == r_shadow_blankglosstexture)
1633 colorscale *= r_shadow_gloss2intensity.value;
1634 GL_VertexPointer(vertex3f);
1636 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1638 // 2/0/0/1/2 3D combine blendsquare path
1639 memset(&m, 0, sizeof(m));
1640 m.tex[0] = R_GetTexture(bumptexture);
1641 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1642 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1643 m.pointer_texcoord[0] = texcoord2f;
1644 m.pointer_texcoord[1] = varray_texcoord3f[1];
1645 R_Mesh_State_Texture(&m);
1646 qglColorMask(0,0,0,1);
1647 // this squares the result
1648 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1649 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1650 R_Mesh_Draw(numverts, numtriangles, elements);
1652 c_rt_lighttris += numtriangles;
1654 memset(&m, 0, sizeof(m));
1655 R_Mesh_State_Texture(&m);
1656 // square alpha in framebuffer a few times to make it shiny
1657 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1658 // these comments are a test run through this math for intensity 0.5
1659 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1660 // 0.25 * 0.25 = 0.0625 (this is another pass)
1661 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1662 R_Mesh_Draw(numverts, numtriangles, elements);
1664 c_rt_lighttris += numtriangles;
1665 R_Mesh_Draw(numverts, numtriangles, elements);
1667 c_rt_lighttris += numtriangles;
1669 memset(&m, 0, sizeof(m));
1670 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1671 m.pointer_texcoord[0] = varray_texcoord3f[0];
1672 R_Mesh_State_Texture(&m);
1673 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1674 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1675 R_Mesh_Draw(numverts, numtriangles, elements);
1677 c_rt_lighttris += numtriangles;
1679 memset(&m, 0, sizeof(m));
1680 m.tex[0] = R_GetTexture(glosstexture);
1681 m.texcubemap[1] = R_GetTexture(lightcubemap);
1682 m.pointer_texcoord[0] = texcoord2f;
1683 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1684 R_Mesh_State_Texture(&m);
1685 qglColorMask(1,1,1,0);
1686 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1688 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1689 VectorScale(lightcolor, colorscale, color2);
1690 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1692 color[0] = bound(0, color2[0], 1);
1693 color[1] = bound(0, color2[1], 1);
1694 color[2] = bound(0, color2[2], 1);
1695 GL_Color(color[0], color[1], color[2], 1);
1696 R_Mesh_Draw(numverts, numtriangles, elements);
1698 c_rt_lighttris += numtriangles;
1701 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1703 // 2/0/0/2 3D combine blendsquare path
1704 memset(&m, 0, sizeof(m));
1705 m.tex[0] = R_GetTexture(bumptexture);
1706 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1707 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1708 m.pointer_texcoord[0] = texcoord2f;
1709 m.pointer_texcoord[1] = varray_texcoord3f[1];
1710 R_Mesh_State_Texture(&m);
1711 qglColorMask(0,0,0,1);
1712 // this squares the result
1713 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1714 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1715 R_Mesh_Draw(numverts, numtriangles, elements);
1717 c_rt_lighttris += numtriangles;
1719 memset(&m, 0, sizeof(m));
1720 R_Mesh_State_Texture(&m);
1721 // square alpha in framebuffer a few times to make it shiny
1722 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1723 // these comments are a test run through this math for intensity 0.5
1724 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1725 // 0.25 * 0.25 = 0.0625 (this is another pass)
1726 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1727 R_Mesh_Draw(numverts, numtriangles, elements);
1729 c_rt_lighttris += numtriangles;
1730 R_Mesh_Draw(numverts, numtriangles, elements);
1732 c_rt_lighttris += numtriangles;
1734 memset(&m, 0, sizeof(m));
1735 m.tex[0] = R_GetTexture(glosstexture);
1736 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1737 m.pointer_texcoord[0] = texcoord2f;
1738 m.pointer_texcoord[1] = varray_texcoord3f[1];
1739 R_Mesh_State_Texture(&m);
1740 qglColorMask(1,1,1,0);
1741 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1742 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1743 VectorScale(lightcolor, colorscale, color2);
1744 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1746 color[0] = bound(0, color2[0], 1);
1747 color[1] = bound(0, color2[1], 1);
1748 color[2] = bound(0, color2[2], 1);
1749 GL_Color(color[0], color[1], color[2], 1);
1750 R_Mesh_Draw(numverts, numtriangles, elements);
1752 c_rt_lighttris += numtriangles;
1755 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1757 // 2/0/0/2/2 2D combine blendsquare path
1758 memset(&m, 0, sizeof(m));
1759 m.tex[0] = R_GetTexture(bumptexture);
1760 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1761 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1762 m.pointer_texcoord[0] = texcoord2f;
1763 m.pointer_texcoord[1] = varray_texcoord3f[1];
1764 R_Mesh_State_Texture(&m);
1765 qglColorMask(0,0,0,1);
1766 // this squares the result
1767 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1768 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1769 R_Mesh_Draw(numverts, numtriangles, elements);
1771 c_rt_lighttris += numtriangles;
1773 memset(&m, 0, sizeof(m));
1774 R_Mesh_State_Texture(&m);
1775 // square alpha in framebuffer a few times to make it shiny
1776 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1777 // these comments are a test run through this math for intensity 0.5
1778 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1779 // 0.25 * 0.25 = 0.0625 (this is another pass)
1780 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1781 R_Mesh_Draw(numverts, numtriangles, elements);
1783 c_rt_lighttris += numtriangles;
1784 R_Mesh_Draw(numverts, numtriangles, elements);
1786 c_rt_lighttris += numtriangles;
1788 memset(&m, 0, sizeof(m));
1789 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1790 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1791 m.pointer_texcoord[0] = varray_texcoord2f[0];
1792 m.pointer_texcoord[1] = varray_texcoord2f[1];
1793 R_Mesh_State_Texture(&m);
1794 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1795 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1796 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1797 R_Mesh_Draw(numverts, numtriangles, elements);
1799 c_rt_lighttris += numtriangles;
1801 memset(&m, 0, sizeof(m));
1802 m.tex[0] = R_GetTexture(glosstexture);
1803 m.texcubemap[1] = R_GetTexture(lightcubemap);
1804 m.pointer_texcoord[0] = texcoord2f;
1805 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1806 R_Mesh_State_Texture(&m);
1807 qglColorMask(1,1,1,0);
1808 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1810 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1811 VectorScale(lightcolor, colorscale, color2);
1812 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1814 color[0] = bound(0, color2[0], 1);
1815 color[1] = bound(0, color2[1], 1);
1816 color[2] = bound(0, color2[2], 1);
1817 GL_Color(color[0], color[1], color[2], 1);
1818 R_Mesh_Draw(numverts, numtriangles, elements);
1820 c_rt_lighttris += numtriangles;
1826 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1828 R_Mesh_Matrix(matrix);
1829 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1832 cvar_t r_editlights = {0, "r_editlights", "0"};
1833 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1834 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1835 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1836 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1837 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1838 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1839 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1840 worldlight_t *r_shadow_worldlightchain;
1841 worldlight_t *r_shadow_selectedlight;
1842 vec3_t r_editlights_cursorlocation;
1844 static int lightpvsbytes;
1845 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1847 static int castshadowcount = 1;
1848 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1850 int i, j, k, l, maxverts = 256, *mark, tris;
1851 float *vertex3f = NULL;
1853 shadowmesh_t *mesh, *castmesh;
1856 surfmesh_t *surfmesh;
1858 if (radius < 15 || DotProduct(color, color) < 0.03)
1860 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1864 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1865 VectorCopy(origin, e->origin);
1866 VectorCopy(color, e->light);
1867 e->lightradius = radius;
1869 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1871 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1874 e->castshadows = castshadow;
1876 e->cullradius = e->lightradius;
1877 for (k = 0;k < 3;k++)
1879 e->mins[k] = e->origin[k] - e->lightradius;
1880 e->maxs[k] = e->origin[k] + e->lightradius;
1883 e->next = r_shadow_worldlightchain;
1884 r_shadow_worldlightchain = e;
1885 if (cubemapname && cubemapname[0])
1887 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1888 strcpy(e->cubemapname, cubemapname);
1889 // FIXME: add cubemap loading (and don't load a cubemap twice)
1894 i = CL_PointContents(e->origin);
1895 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1898 qbyte *bytesurfacepvs;
1900 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1901 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1903 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1905 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1906 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1907 leaf->worldnodeframe = castshadowcount;
1909 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1910 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1911 surf->castshadow = castshadowcount;
1913 Mem_Free(byteleafpvs);
1914 Mem_Free(bytesurfacepvs);
1918 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1919 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs - 1;i++, leaf++)
1921 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1923 leaf->worldnodeframe = castshadowcount;
1924 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1926 surf = cl.worldmodel->brushq1.surfaces + *mark;
1927 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1928 surf->castshadow = castshadowcount;
1935 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1936 if (leaf->worldnodeframe == castshadowcount)
1939 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1940 if (surf->castshadow == castshadowcount)
1944 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1946 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1948 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1949 if (leaf->worldnodeframe == castshadowcount)
1950 e->leafs[e->numleafs++] = leaf;
1952 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1953 if (surf->castshadow == castshadowcount)
1954 e->surfaces[e->numsurfaces++] = surf;
1956 // find bounding box of lit leafs
1957 VectorCopy(e->origin, e->mins);
1958 VectorCopy(e->origin, e->maxs);
1959 for (j = 0;j < e->numleafs;j++)
1962 for (k = 0;k < 3;k++)
1964 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1965 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1969 for (k = 0;k < 3;k++)
1971 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1972 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1974 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1979 for (j = 0;j < e->numsurfaces;j++)
1981 surf = e->surfaces[j];
1982 if (surf->flags & SURF_SHADOWCAST)
1984 surf->castshadow = castshadowcount;
1985 if (maxverts < surf->poly_numverts)
1986 maxverts = surf->poly_numverts;
1989 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1990 // make a mesh to cast a shadow volume from
1991 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1992 for (j = 0;j < e->numsurfaces;j++)
1993 if (e->surfaces[j]->castshadow == castshadowcount)
1994 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1995 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1996 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1998 // cast shadow volume from castmesh
1999 for (mesh = castmesh;mesh;mesh = mesh->next)
2001 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
2003 if (maxverts < castmesh->numverts * 2)
2005 maxverts = castmesh->numverts * 2;
2010 if (vertex3f == NULL && maxverts > 0)
2011 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2013 // now that we have the buffers big enough, construct and add
2014 // the shadow volume mesh
2015 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)))
2016 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
2021 // we're done with castmesh now
2022 Mod_ShadowMesh_Free(castmesh);
2023 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
2024 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
2025 l += mesh->numtriangles;
2026 Con_Printf("static shadow volume built containing %i triangles\n", l);
2029 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
2032 void R_Shadow_FreeWorldLight(worldlight_t *light)
2034 worldlight_t **lightpointer;
2035 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2036 if (*lightpointer != light)
2037 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2038 *lightpointer = light->next;
2039 if (light->cubemapname)
2040 Mem_Free(light->cubemapname);
2041 if (light->shadowvolume)
2042 Mod_ShadowMesh_Free(light->shadowvolume);
2043 if (light->surfaces)
2044 Mem_Free(light->surfaces);
2046 Mem_Free(light->leafs);
2050 void R_Shadow_ClearWorldLights(void)
2052 while (r_shadow_worldlightchain)
2053 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2054 r_shadow_selectedlight = NULL;
2057 void R_Shadow_SelectLight(worldlight_t *light)
2059 if (r_shadow_selectedlight)
2060 r_shadow_selectedlight->selected = false;
2061 r_shadow_selectedlight = light;
2062 if (r_shadow_selectedlight)
2063 r_shadow_selectedlight->selected = true;
2066 rtexture_t *lighttextures[5];
2068 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2070 float scale = r_editlights_cursorgrid.value * 0.5f;
2071 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2074 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2077 const worldlight_t *light;
2080 if (light->selected)
2081 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2082 if (!light->shadowvolume)
2084 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2087 void R_Shadow_DrawLightSprites(void)
2091 worldlight_t *light;
2093 for (i = 0;i < 5;i++)
2095 lighttextures[i] = NULL;
2096 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2097 lighttextures[i] = pic->tex;
2100 for (light = r_shadow_worldlightchain;light;light = light->next)
2101 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2102 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2105 void R_Shadow_SelectLightInView(void)
2107 float bestrating, rating, temp[3];
2108 worldlight_t *best, *light;
2111 for (light = r_shadow_worldlightchain;light;light = light->next)
2113 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2114 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2117 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2118 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2120 bestrating = rating;
2125 R_Shadow_SelectLight(best);
2128 void R_Shadow_LoadWorldLights(void)
2130 int n, a, style, shadow;
2131 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2132 float origin[3], radius, color[3];
2133 if (cl.worldmodel == NULL)
2135 Con_Printf("No map loaded.\n");
2138 FS_StripExtension(cl.worldmodel->name, name);
2139 strcat(name, ".rtlights");
2140 lightsstring = FS_LoadFile(name, false);
2148 while (*s && *s != '\n')
2154 // check for modifier flags
2160 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);
2166 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);
2169 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2170 radius *= r_editlights_rtlightssizescale.value;
2171 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2176 Con_Printf("invalid rtlights file \"%s\"\n", name);
2177 Mem_Free(lightsstring);
2181 void R_Shadow_SaveWorldLights(void)
2183 worldlight_t *light;
2184 int bufchars, bufmaxchars;
2186 char name[MAX_QPATH];
2188 if (!r_shadow_worldlightchain)
2190 if (cl.worldmodel == NULL)
2192 Con_Printf("No map loaded.\n");
2195 FS_StripExtension(cl.worldmodel->name, name);
2196 strcat(name, ".rtlights");
2197 bufchars = bufmaxchars = 0;
2199 for (light = r_shadow_worldlightchain;light;light = light->next)
2201 sprintf(line, "%s%g %g %g %g %g %g %g %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 : "");
2202 if (bufchars + (int) strlen(line) > bufmaxchars)
2204 bufmaxchars = bufchars + strlen(line) + 2048;
2206 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2210 memcpy(buf, oldbuf, bufchars);
2216 memcpy(buf + bufchars, line, strlen(line));
2217 bufchars += strlen(line);
2221 FS_WriteFile(name, buf, bufchars);
2226 void R_Shadow_LoadLightsFile(void)
2229 char name[MAX_QPATH], *lightsstring, *s, *t;
2230 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2231 if (cl.worldmodel == NULL)
2233 Con_Printf("No map loaded.\n");
2236 FS_StripExtension(cl.worldmodel->name, name);
2237 strcat(name, ".lights");
2238 lightsstring = FS_LoadFile(name, false);
2246 while (*s && *s != '\n')
2251 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);
2255 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);
2258 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2259 radius = bound(15, radius, 4096);
2260 VectorScale(color, (2.0f / (8388608.0f)), color);
2261 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2266 Con_Printf("invalid lights file \"%s\"\n", name);
2267 Mem_Free(lightsstring);
2271 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2273 int entnum, style, islight;
2274 char key[256], value[1024];
2275 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2278 if (cl.worldmodel == NULL)
2280 Con_Printf("No map loaded.\n");
2283 data = cl.worldmodel->brush.entities;
2286 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2289 origin[0] = origin[1] = origin[2] = 0;
2290 originhack[0] = originhack[1] = originhack[2] = 0;
2291 color[0] = color[1] = color[2] = 1;
2292 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2298 if (!COM_ParseToken(&data, false))
2300 if (com_token[0] == '}')
2301 break; // end of entity
2302 if (com_token[0] == '_')
2303 strcpy(key, com_token + 1);
2305 strcpy(key, com_token);
2306 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2307 key[strlen(key)-1] = 0;
2308 if (!COM_ParseToken(&data, false))
2310 strcpy(value, com_token);
2312 // now that we have the key pair worked out...
2313 if (!strcmp("light", key))
2314 light = atof(value);
2315 else if (!strcmp("origin", key))
2316 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2317 else if (!strcmp("color", key))
2318 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2319 else if (!strcmp("wait", key))
2320 scale = atof(value);
2321 else if (!strcmp("classname", key))
2323 if (!strncmp(value, "light", 5))
2326 if (!strcmp(value, "light_fluoro"))
2331 overridecolor[0] = 1;
2332 overridecolor[1] = 1;
2333 overridecolor[2] = 1;
2335 if (!strcmp(value, "light_fluorospark"))
2340 overridecolor[0] = 1;
2341 overridecolor[1] = 1;
2342 overridecolor[2] = 1;
2344 if (!strcmp(value, "light_globe"))
2349 overridecolor[0] = 1;
2350 overridecolor[1] = 0.8;
2351 overridecolor[2] = 0.4;
2353 if (!strcmp(value, "light_flame_large_yellow"))
2358 overridecolor[0] = 1;
2359 overridecolor[1] = 0.5;
2360 overridecolor[2] = 0.1;
2362 if (!strcmp(value, "light_flame_small_yellow"))
2367 overridecolor[0] = 1;
2368 overridecolor[1] = 0.5;
2369 overridecolor[2] = 0.1;
2371 if (!strcmp(value, "light_torch_small_white"))
2376 overridecolor[0] = 1;
2377 overridecolor[1] = 0.5;
2378 overridecolor[2] = 0.1;
2380 if (!strcmp(value, "light_torch_small_walltorch"))
2385 overridecolor[0] = 1;
2386 overridecolor[1] = 0.5;
2387 overridecolor[2] = 0.1;
2391 else if (!strcmp("style", key))
2392 style = atoi(value);
2394 if (light <= 0 && islight)
2396 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2397 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2398 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2399 VectorCopy(overridecolor, color);
2400 VectorScale(color, light, color);
2401 VectorAdd(origin, originhack, origin);
2403 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2408 void R_Shadow_SetCursorLocationForView(void)
2410 vec_t dist, push, frac;
2411 vec3_t dest, endpos, normal;
2412 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2413 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2416 dist = frac * r_editlights_cursordistance.value;
2417 push = r_editlights_cursorpushback.value;
2421 VectorMA(endpos, push, vpn, endpos);
2422 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2424 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2425 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2426 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2429 void R_Shadow_UpdateWorldLightSelection(void)
2431 R_Shadow_SetCursorLocationForView();
2432 if (r_editlights.integer)
2434 R_Shadow_SelectLightInView();
2435 R_Shadow_DrawLightSprites();
2438 R_Shadow_SelectLight(NULL);
2441 void R_Shadow_EditLights_Clear_f(void)
2443 R_Shadow_ClearWorldLights();
2446 void R_Shadow_EditLights_Reload_f(void)
2448 r_shadow_reloadlights = true;
2451 void R_Shadow_EditLights_Save_f(void)
2454 R_Shadow_SaveWorldLights();
2457 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2459 R_Shadow_ClearWorldLights();
2460 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2463 void R_Shadow_EditLights_ImportLightsFile_f(void)
2465 R_Shadow_ClearWorldLights();
2466 R_Shadow_LoadLightsFile();
2469 void R_Shadow_EditLights_Spawn_f(void)
2472 if (!r_editlights.integer)
2474 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2477 if (Cmd_Argc() != 1)
2479 Con_Printf("r_editlights_spawn does not take parameters\n");
2482 color[0] = color[1] = color[2] = 1;
2483 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2486 void R_Shadow_EditLights_Edit_f(void)
2488 vec3_t origin, color;
2491 char cubemapname[1024];
2492 if (!r_editlights.integer)
2494 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2497 if (!r_shadow_selectedlight)
2499 Con_Printf("No selected light.\n");
2502 VectorCopy(r_shadow_selectedlight->origin, origin);
2503 radius = r_shadow_selectedlight->lightradius;
2504 VectorCopy(r_shadow_selectedlight->light, color);
2505 style = r_shadow_selectedlight->style;
2506 if (r_shadow_selectedlight->cubemapname)
2507 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2510 shadows = r_shadow_selectedlight->castshadows;
2511 if (!strcmp(Cmd_Argv(1), "origin"))
2513 if (Cmd_Argc() != 5)
2515 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2518 origin[0] = atof(Cmd_Argv(2));
2519 origin[1] = atof(Cmd_Argv(3));
2520 origin[2] = atof(Cmd_Argv(4));
2522 else if (!strcmp(Cmd_Argv(1), "originx"))
2524 if (Cmd_Argc() != 3)
2526 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2529 origin[0] = atof(Cmd_Argv(2));
2531 else if (!strcmp(Cmd_Argv(1), "originy"))
2533 if (Cmd_Argc() != 3)
2535 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2538 origin[1] = atof(Cmd_Argv(2));
2540 else if (!strcmp(Cmd_Argv(1), "originz"))
2542 if (Cmd_Argc() != 3)
2544 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2547 origin[2] = atof(Cmd_Argv(2));
2549 else if (!strcmp(Cmd_Argv(1), "move"))
2551 if (Cmd_Argc() != 5)
2553 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2556 origin[0] += atof(Cmd_Argv(2));
2557 origin[1] += atof(Cmd_Argv(3));
2558 origin[2] += atof(Cmd_Argv(4));
2560 else if (!strcmp(Cmd_Argv(1), "movex"))
2562 if (Cmd_Argc() != 3)
2564 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2567 origin[0] += atof(Cmd_Argv(2));
2569 else if (!strcmp(Cmd_Argv(1), "movey"))
2571 if (Cmd_Argc() != 3)
2573 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2576 origin[1] += atof(Cmd_Argv(2));
2578 else if (!strcmp(Cmd_Argv(1), "movez"))
2580 if (Cmd_Argc() != 3)
2582 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2585 origin[2] += atof(Cmd_Argv(2));
2587 else if (!strcmp(Cmd_Argv(1), "color"))
2589 if (Cmd_Argc() != 5)
2591 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2594 color[0] = atof(Cmd_Argv(2));
2595 color[1] = atof(Cmd_Argv(3));
2596 color[2] = atof(Cmd_Argv(4));
2598 else if (!strcmp(Cmd_Argv(1), "radius"))
2600 if (Cmd_Argc() != 3)
2602 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2605 radius = atof(Cmd_Argv(2));
2607 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2609 if (Cmd_Argc() != 3)
2611 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2614 style = atoi(Cmd_Argv(2));
2616 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2620 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2623 if (Cmd_Argc() == 3)
2624 strcpy(cubemapname, Cmd_Argv(2));
2628 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2630 if (Cmd_Argc() != 3)
2632 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2635 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2639 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2640 Con_Printf("Selected light's properties:\n");
2641 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2642 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2643 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2644 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2645 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2646 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2649 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2650 r_shadow_selectedlight = NULL;
2651 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2654 extern int con_vislines;
2655 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2659 if (r_shadow_selectedlight == NULL)
2663 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2664 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;
2665 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2666 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;
2667 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2668 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2669 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;
2672 void R_Shadow_EditLights_ToggleShadow_f(void)
2674 if (!r_editlights.integer)
2676 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2679 if (!r_shadow_selectedlight)
2681 Con_Printf("No selected light.\n");
2684 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);
2685 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2686 r_shadow_selectedlight = NULL;
2689 void R_Shadow_EditLights_Remove_f(void)
2691 if (!r_editlights.integer)
2693 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2696 if (!r_shadow_selectedlight)
2698 Con_Printf("No selected light.\n");
2701 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2702 r_shadow_selectedlight = NULL;
2705 void R_Shadow_EditLights_Init(void)
2707 Cvar_RegisterVariable(&r_editlights);
2708 Cvar_RegisterVariable(&r_editlights_cursordistance);
2709 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2710 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2711 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2712 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2713 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2714 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2715 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2716 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2717 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2718 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2719 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2720 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2721 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2722 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2723 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);