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 castshadowcount = 1;
1845 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1847 int i, j, k, l, maxverts = 256, *mark, tris;
1848 float *vertex3f = NULL;
1850 shadowmesh_t *mesh, *castmesh;
1854 surfmesh_t *surfmesh;
1856 if (radius < 15 || DotProduct(color, color) < 0.03)
1858 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1862 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1863 VectorCopy(origin, e->origin);
1864 VectorCopy(color, e->light);
1865 e->lightradius = radius;
1867 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1869 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1872 e->castshadows = castshadow;
1874 e->cullradius = e->lightradius;
1875 for (k = 0;k < 3;k++)
1877 e->mins[k] = e->origin[k] - e->lightradius;
1878 e->maxs[k] = e->origin[k] + e->lightradius;
1881 e->next = r_shadow_worldlightchain;
1882 r_shadow_worldlightchain = e;
1883 if (cubemapname && cubemapname[0])
1885 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1886 strcpy(e->cubemapname, cubemapname);
1887 // FIXME: add cubemap loading (and don't load a cubemap twice)
1892 i = CL_PointContents(e->origin);
1893 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1896 qbyte *bytesurfacepvs;
1898 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs + 1);
1899 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1901 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1903 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1904 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1905 leaf->worldnodeframe = castshadowcount;
1907 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1908 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1909 surf->castshadow = castshadowcount;
1911 Mem_Free(byteleafpvs);
1912 Mem_Free(bytesurfacepvs);
1916 leaf = cl.worldmodel->brushq1.PointInLeaf(cl.worldmodel, origin);
1917 pvs = cl.worldmodel->brushq1.LeafPVS(cl.worldmodel, leaf);
1918 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1920 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1922 leaf->worldnodeframe = castshadowcount;
1923 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1925 surf = cl.worldmodel->brushq1.surfaces + *mark;
1926 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1927 surf->castshadow = castshadowcount;
1934 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1935 if (leaf->worldnodeframe == castshadowcount)
1938 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1939 if (surf->castshadow == castshadowcount)
1943 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1945 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1947 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1948 if (leaf->worldnodeframe == castshadowcount)
1949 e->leafs[e->numleafs++] = leaf;
1951 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1952 if (surf->castshadow == castshadowcount)
1953 e->surfaces[e->numsurfaces++] = surf;
1955 // find bounding box of lit leafs
1956 VectorCopy(e->origin, e->mins);
1957 VectorCopy(e->origin, e->maxs);
1958 for (j = 0;j < e->numleafs;j++)
1961 for (k = 0;k < 3;k++)
1963 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1964 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1968 for (k = 0;k < 3;k++)
1970 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1971 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1973 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1978 for (j = 0;j < e->numsurfaces;j++)
1980 surf = e->surfaces[j];
1981 if (surf->flags & SURF_SHADOWCAST)
1983 surf->castshadow = castshadowcount;
1984 if (maxverts < surf->poly_numverts)
1985 maxverts = surf->poly_numverts;
1988 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1989 // make a mesh to cast a shadow volume from
1990 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1991 for (j = 0;j < e->numsurfaces;j++)
1992 if (e->surfaces[j]->castshadow == castshadowcount)
1993 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1994 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1995 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1997 // cast shadow volume from castmesh
1998 for (mesh = castmesh;mesh;mesh = mesh->next)
2000 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
2002 if (maxverts < castmesh->numverts * 2)
2004 maxverts = castmesh->numverts * 2;
2009 if (vertex3f == NULL && maxverts > 0)
2010 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2012 // now that we have the buffers big enough, construct and add
2013 // the shadow volume mesh
2014 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)))
2015 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
2020 // we're done with castmesh now
2021 Mod_ShadowMesh_Free(castmesh);
2022 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
2023 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
2024 l += mesh->numtriangles;
2025 Con_Printf("static shadow volume built containing %i triangles\n", l);
2028 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);
2031 void R_Shadow_FreeWorldLight(worldlight_t *light)
2033 worldlight_t **lightpointer;
2034 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2035 if (*lightpointer != light)
2036 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2037 *lightpointer = light->next;
2038 if (light->cubemapname)
2039 Mem_Free(light->cubemapname);
2040 if (light->shadowvolume)
2041 Mod_ShadowMesh_Free(light->shadowvolume);
2042 if (light->surfaces)
2043 Mem_Free(light->surfaces);
2045 Mem_Free(light->leafs);
2049 void R_Shadow_ClearWorldLights(void)
2051 while (r_shadow_worldlightchain)
2052 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2053 r_shadow_selectedlight = NULL;
2056 void R_Shadow_SelectLight(worldlight_t *light)
2058 if (r_shadow_selectedlight)
2059 r_shadow_selectedlight->selected = false;
2060 r_shadow_selectedlight = light;
2061 if (r_shadow_selectedlight)
2062 r_shadow_selectedlight->selected = true;
2065 rtexture_t *lighttextures[5];
2067 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2069 float scale = r_editlights_cursorgrid.value * 0.5f;
2070 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2073 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2076 const worldlight_t *light;
2079 if (light->selected)
2080 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2081 if (!light->shadowvolume)
2083 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2086 void R_Shadow_DrawLightSprites(void)
2090 worldlight_t *light;
2092 for (i = 0;i < 5;i++)
2094 lighttextures[i] = NULL;
2095 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2096 lighttextures[i] = pic->tex;
2099 for (light = r_shadow_worldlightchain;light;light = light->next)
2100 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2101 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2104 void R_Shadow_SelectLightInView(void)
2106 float bestrating, rating, temp[3];
2107 worldlight_t *best, *light;
2110 for (light = r_shadow_worldlightchain;light;light = light->next)
2112 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2113 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2116 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2117 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2119 bestrating = rating;
2124 R_Shadow_SelectLight(best);
2127 void R_Shadow_LoadWorldLights(void)
2129 int n, a, style, shadow;
2130 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2131 float origin[3], radius, color[3];
2132 if (cl.worldmodel == NULL)
2134 Con_Printf("No map loaded.\n");
2137 FS_StripExtension(cl.worldmodel->name, name);
2138 strcat(name, ".rtlights");
2139 lightsstring = FS_LoadFile(name, false);
2147 while (*s && *s != '\n')
2153 // check for modifier flags
2159 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);
2165 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);
2168 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2169 radius *= r_editlights_rtlightssizescale.value;
2170 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2175 Con_Printf("invalid rtlights file \"%s\"\n", name);
2176 Mem_Free(lightsstring);
2180 void R_Shadow_SaveWorldLights(void)
2182 worldlight_t *light;
2183 int bufchars, bufmaxchars;
2185 char name[MAX_QPATH];
2187 if (!r_shadow_worldlightchain)
2189 if (cl.worldmodel == NULL)
2191 Con_Printf("No map loaded.\n");
2194 FS_StripExtension(cl.worldmodel->name, name);
2195 strcat(name, ".rtlights");
2196 bufchars = bufmaxchars = 0;
2198 for (light = r_shadow_worldlightchain;light;light = light->next)
2200 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 : "");
2201 if (bufchars + (int) strlen(line) > bufmaxchars)
2203 bufmaxchars = bufchars + strlen(line) + 2048;
2205 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2209 memcpy(buf, oldbuf, bufchars);
2215 memcpy(buf + bufchars, line, strlen(line));
2216 bufchars += strlen(line);
2220 FS_WriteFile(name, buf, bufchars);
2225 void R_Shadow_LoadLightsFile(void)
2228 char name[MAX_QPATH], *lightsstring, *s, *t;
2229 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2230 if (cl.worldmodel == NULL)
2232 Con_Printf("No map loaded.\n");
2235 FS_StripExtension(cl.worldmodel->name, name);
2236 strcat(name, ".lights");
2237 lightsstring = FS_LoadFile(name, false);
2245 while (*s && *s != '\n')
2250 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);
2254 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);
2257 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2258 radius = bound(15, radius, 4096);
2259 VectorScale(color, (2.0f / (8388608.0f)), color);
2260 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2265 Con_Printf("invalid lights file \"%s\"\n", name);
2266 Mem_Free(lightsstring);
2270 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2272 int entnum, style, islight;
2273 char key[256], value[1024];
2274 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2277 if (cl.worldmodel == NULL)
2279 Con_Printf("No map loaded.\n");
2282 data = cl.worldmodel->brush.entities;
2285 for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2288 origin[0] = origin[1] = origin[2] = 0;
2289 originhack[0] = originhack[1] = originhack[2] = 0;
2290 color[0] = color[1] = color[2] = 1;
2291 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2297 if (!COM_ParseToken(&data))
2299 if (com_token[0] == '}')
2300 break; // end of entity
2301 if (com_token[0] == '_')
2302 strcpy(key, com_token + 1);
2304 strcpy(key, com_token);
2305 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2306 key[strlen(key)-1] = 0;
2307 if (!COM_ParseToken(&data))
2309 strcpy(value, com_token);
2311 // now that we have the key pair worked out...
2312 if (!strcmp("light", key))
2313 light = atof(value);
2314 else if (!strcmp("origin", key))
2315 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2316 else if (!strcmp("color", key))
2317 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2318 else if (!strcmp("wait", key))
2319 scale = atof(value);
2320 else if (!strcmp("classname", key))
2322 if (!strncmp(value, "light", 5))
2325 if (!strcmp(value, "light_fluoro"))
2330 overridecolor[0] = 1;
2331 overridecolor[1] = 1;
2332 overridecolor[2] = 1;
2334 if (!strcmp(value, "light_fluorospark"))
2339 overridecolor[0] = 1;
2340 overridecolor[1] = 1;
2341 overridecolor[2] = 1;
2343 if (!strcmp(value, "light_globe"))
2348 overridecolor[0] = 1;
2349 overridecolor[1] = 0.8;
2350 overridecolor[2] = 0.4;
2352 if (!strcmp(value, "light_flame_large_yellow"))
2357 overridecolor[0] = 1;
2358 overridecolor[1] = 0.5;
2359 overridecolor[2] = 0.1;
2361 if (!strcmp(value, "light_flame_small_yellow"))
2366 overridecolor[0] = 1;
2367 overridecolor[1] = 0.5;
2368 overridecolor[2] = 0.1;
2370 if (!strcmp(value, "light_torch_small_white"))
2375 overridecolor[0] = 1;
2376 overridecolor[1] = 0.5;
2377 overridecolor[2] = 0.1;
2379 if (!strcmp(value, "light_torch_small_walltorch"))
2384 overridecolor[0] = 1;
2385 overridecolor[1] = 0.5;
2386 overridecolor[2] = 0.1;
2390 else if (!strcmp("style", key))
2391 style = atoi(value);
2393 if (light <= 0 && islight)
2395 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2396 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2397 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2398 VectorCopy(overridecolor, color);
2399 VectorScale(color, light, color);
2400 VectorAdd(origin, originhack, origin);
2402 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2407 void R_Shadow_SetCursorLocationForView(void)
2409 vec_t dist, push, frac;
2410 vec3_t dest, endpos, normal;
2411 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2412 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2415 dist = frac * r_editlights_cursordistance.value;
2416 push = r_editlights_cursorpushback.value;
2420 VectorMA(endpos, push, vpn, endpos);
2421 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2423 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2424 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2425 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2428 void R_Shadow_UpdateWorldLightSelection(void)
2430 R_Shadow_SetCursorLocationForView();
2431 if (r_editlights.integer)
2433 R_Shadow_SelectLightInView();
2434 R_Shadow_DrawLightSprites();
2437 R_Shadow_SelectLight(NULL);
2440 void R_Shadow_EditLights_Clear_f(void)
2442 R_Shadow_ClearWorldLights();
2445 void R_Shadow_EditLights_Reload_f(void)
2447 r_shadow_reloadlights = true;
2450 void R_Shadow_EditLights_Save_f(void)
2453 R_Shadow_SaveWorldLights();
2456 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2458 R_Shadow_ClearWorldLights();
2459 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2462 void R_Shadow_EditLights_ImportLightsFile_f(void)
2464 R_Shadow_ClearWorldLights();
2465 R_Shadow_LoadLightsFile();
2468 void R_Shadow_EditLights_Spawn_f(void)
2471 if (!r_editlights.integer)
2473 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2476 if (Cmd_Argc() != 1)
2478 Con_Printf("r_editlights_spawn does not take parameters\n");
2481 color[0] = color[1] = color[2] = 1;
2482 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2485 void R_Shadow_EditLights_Edit_f(void)
2487 vec3_t origin, color;
2490 char cubemapname[1024];
2491 if (!r_editlights.integer)
2493 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2496 if (!r_shadow_selectedlight)
2498 Con_Printf("No selected light.\n");
2501 VectorCopy(r_shadow_selectedlight->origin, origin);
2502 radius = r_shadow_selectedlight->lightradius;
2503 VectorCopy(r_shadow_selectedlight->light, color);
2504 style = r_shadow_selectedlight->style;
2505 if (r_shadow_selectedlight->cubemapname)
2506 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2509 shadows = r_shadow_selectedlight->castshadows;
2510 if (!strcmp(Cmd_Argv(1), "origin"))
2512 if (Cmd_Argc() != 5)
2514 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2517 origin[0] = atof(Cmd_Argv(2));
2518 origin[1] = atof(Cmd_Argv(3));
2519 origin[2] = atof(Cmd_Argv(4));
2521 else if (!strcmp(Cmd_Argv(1), "originx"))
2523 if (Cmd_Argc() != 3)
2525 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2528 origin[0] = atof(Cmd_Argv(2));
2530 else if (!strcmp(Cmd_Argv(1), "originy"))
2532 if (Cmd_Argc() != 3)
2534 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2537 origin[1] = atof(Cmd_Argv(2));
2539 else if (!strcmp(Cmd_Argv(1), "originz"))
2541 if (Cmd_Argc() != 3)
2543 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2546 origin[2] = atof(Cmd_Argv(2));
2548 else if (!strcmp(Cmd_Argv(1), "move"))
2550 if (Cmd_Argc() != 5)
2552 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2555 origin[0] += atof(Cmd_Argv(2));
2556 origin[1] += atof(Cmd_Argv(3));
2557 origin[2] += atof(Cmd_Argv(4));
2559 else if (!strcmp(Cmd_Argv(1), "movex"))
2561 if (Cmd_Argc() != 3)
2563 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2566 origin[0] += atof(Cmd_Argv(2));
2568 else if (!strcmp(Cmd_Argv(1), "movey"))
2570 if (Cmd_Argc() != 3)
2572 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2575 origin[1] += atof(Cmd_Argv(2));
2577 else if (!strcmp(Cmd_Argv(1), "movez"))
2579 if (Cmd_Argc() != 3)
2581 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2584 origin[2] += atof(Cmd_Argv(2));
2586 else if (!strcmp(Cmd_Argv(1), "color"))
2588 if (Cmd_Argc() != 5)
2590 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2593 color[0] = atof(Cmd_Argv(2));
2594 color[1] = atof(Cmd_Argv(3));
2595 color[2] = atof(Cmd_Argv(4));
2597 else if (!strcmp(Cmd_Argv(1), "radius"))
2599 if (Cmd_Argc() != 3)
2601 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2604 radius = atof(Cmd_Argv(2));
2606 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2608 if (Cmd_Argc() != 3)
2610 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2613 style = atoi(Cmd_Argv(2));
2615 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2619 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2622 if (Cmd_Argc() == 3)
2623 strcpy(cubemapname, Cmd_Argv(2));
2627 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2629 if (Cmd_Argc() != 3)
2631 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2634 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2638 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2639 Con_Printf("Selected light's properties:\n");
2640 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2641 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2642 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2643 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2644 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2645 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2648 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2649 r_shadow_selectedlight = NULL;
2650 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2653 extern int con_vislines;
2654 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2658 if (r_shadow_selectedlight == NULL)
2662 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2663 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;
2664 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2665 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;
2666 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2667 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2668 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;
2671 void R_Shadow_EditLights_ToggleShadow_f(void)
2673 if (!r_editlights.integer)
2675 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2678 if (!r_shadow_selectedlight)
2680 Con_Printf("No selected light.\n");
2683 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);
2684 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2685 r_shadow_selectedlight = NULL;
2688 void R_Shadow_EditLights_Remove_f(void)
2690 if (!r_editlights.integer)
2692 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2695 if (!r_shadow_selectedlight)
2697 Con_Printf("No selected light.\n");
2700 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2701 r_shadow_selectedlight = NULL;
2704 void R_Shadow_EditLights_Init(void)
2706 Cvar_RegisterVariable(&r_editlights);
2707 Cvar_RegisterVariable(&r_editlights_cursordistance);
2708 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2709 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2710 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2711 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2712 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2713 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2714 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2715 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2716 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2717 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2718 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2719 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2720 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2721 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2722 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);