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 if (!r_shadow_attenuation2dtexture
776 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
777 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
778 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
779 R_Shadow_MakeTextures();
781 memset(&m, 0, sizeof(m));
782 GL_BlendFunc(GL_ONE, GL_ZERO);
785 R_Mesh_State_Texture(&m);
786 GL_Color(0, 0, 0, 1);
787 qglDisable(GL_SCISSOR_TEST);
788 r_shadowstage = SHADOWSTAGE_NONE;
790 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
791 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
792 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
795 void R_Shadow_LoadWorldLightsIfNeeded(void)
797 if (r_shadow_reloadlights && cl.worldmodel)
799 R_Shadow_ClearWorldLights();
800 r_shadow_reloadlights = false;
801 R_Shadow_LoadWorldLights();
802 if (r_shadow_worldlightchain == NULL)
804 R_Shadow_LoadLightsFile();
805 if (r_shadow_worldlightchain == NULL)
806 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
811 void R_Shadow_Stage_ShadowVolumes(void)
814 memset(&m, 0, sizeof(m));
815 R_Mesh_State_Texture(&m);
816 GL_Color(1, 1, 1, 1);
817 qglColorMask(0, 0, 0, 0);
818 GL_BlendFunc(GL_ONE, GL_ZERO);
821 if (r_shadow_polygonoffset.value != 0)
823 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
824 qglEnable(GL_POLYGON_OFFSET_FILL);
827 qglDisable(GL_POLYGON_OFFSET_FILL);
828 qglDepthFunc(GL_LESS);
829 qglEnable(GL_STENCIL_TEST);
830 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
831 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
832 r_shadowstage = SHADOWSTAGE_STENCIL;
833 qglClear(GL_STENCIL_BUFFER_BIT);
835 // LordHavoc note: many shadow volumes reside entirely inside the world
836 // (that is to say they are entirely bounded by their lit surfaces),
837 // which can be optimized by handling things as an inverted light volume,
838 // with the shadow boundaries of the world being simulated by an altered
839 // (129) bias to stencil clearing on such lights
840 // FIXME: generate inverted light volumes for use as shadow volumes and
841 // optimize for them as noted above
844 void R_Shadow_Stage_LightWithoutShadows(void)
847 memset(&m, 0, sizeof(m));
848 R_Mesh_State_Texture(&m);
849 GL_BlendFunc(GL_ONE, GL_ONE);
852 qglDisable(GL_POLYGON_OFFSET_FILL);
853 GL_Color(1, 1, 1, 1);
854 qglColorMask(1, 1, 1, 1);
855 qglDepthFunc(GL_EQUAL);
856 qglDisable(GL_STENCIL_TEST);
857 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
858 qglStencilFunc(GL_EQUAL, 128, 0xFF);
859 r_shadowstage = SHADOWSTAGE_LIGHT;
863 void R_Shadow_Stage_LightWithShadows(void)
866 memset(&m, 0, sizeof(m));
867 R_Mesh_State_Texture(&m);
868 GL_BlendFunc(GL_ONE, GL_ONE);
871 qglDisable(GL_POLYGON_OFFSET_FILL);
872 GL_Color(1, 1, 1, 1);
873 qglColorMask(1, 1, 1, 1);
874 qglDepthFunc(GL_EQUAL);
875 qglEnable(GL_STENCIL_TEST);
876 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
877 // only draw light where this geometry was already rendered AND the
878 // stencil is 128 (values other than this mean shadow)
879 qglStencilFunc(GL_EQUAL, 128, 0xFF);
880 r_shadowstage = SHADOWSTAGE_LIGHT;
884 void R_Shadow_Stage_End(void)
887 memset(&m, 0, sizeof(m));
888 R_Mesh_State_Texture(&m);
889 GL_BlendFunc(GL_ONE, GL_ZERO);
892 qglDisable(GL_POLYGON_OFFSET_FILL);
893 GL_Color(1, 1, 1, 1);
894 qglColorMask(1, 1, 1, 1);
895 qglDisable(GL_SCISSOR_TEST);
896 qglDepthFunc(GL_LEQUAL);
897 qglDisable(GL_STENCIL_TEST);
898 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
899 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
900 r_shadowstage = SHADOWSTAGE_NONE;
903 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
905 int i, ix1, iy1, ix2, iy2;
906 float x1, y1, x2, y2, x, y, f;
909 if (!r_shadow_scissor.integer)
911 // if view is inside the box, just say yes it's visible
912 // LordHavoc: for some odd reason scissor seems broken without stencil
913 // (?!? seems like a driver bug) so abort if gl_stencil is false
914 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
916 qglDisable(GL_SCISSOR_TEST);
919 for (i = 0;i < 3;i++)
932 f = DotProduct(vpn, r_origin) + 1;
933 if (DotProduct(vpn, v2) <= f)
935 // entirely behind nearclip plane
938 if (DotProduct(vpn, v) >= f)
940 // entirely infront of nearclip plane
941 x1 = y1 = x2 = y2 = 0;
942 for (i = 0;i < 8;i++)
944 v[0] = (i & 1) ? mins[0] : maxs[0];
945 v[1] = (i & 2) ? mins[1] : maxs[1];
946 v[2] = (i & 4) ? mins[2] : maxs[2];
948 GL_TransformToScreen(v, v2);
949 //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]);
968 // clipped by nearclip plane
969 // this is nasty and crude...
970 // create viewspace bbox
971 for (i = 0;i < 8;i++)
973 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
974 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
975 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
976 v2[0] = DotProduct(v, vright);
977 v2[1] = DotProduct(v, vup);
978 v2[2] = DotProduct(v, vpn);
981 if (smins[0] > v2[0]) smins[0] = v2[0];
982 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
983 if (smins[1] > v2[1]) smins[1] = v2[1];
984 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
985 if (smins[2] > v2[2]) smins[2] = v2[2];
986 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
990 smins[0] = smaxs[0] = v2[0];
991 smins[1] = smaxs[1] = v2[1];
992 smins[2] = smaxs[2] = v2[2];
995 // now we have a bbox in viewspace
996 // clip it to the view plane
999 // return true if that culled the box
1000 if (smins[2] >= smaxs[2])
1002 // ok some of it is infront of the view, transform each corner back to
1003 // worldspace and then to screenspace and make screen rect
1004 // initialize these variables just to avoid compiler warnings
1005 x1 = y1 = x2 = y2 = 0;
1006 for (i = 0;i < 8;i++)
1008 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1009 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1010 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1011 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1012 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1013 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1015 GL_TransformToScreen(v, v2);
1016 //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]);
1033 // this code doesn't handle boxes with any points behind view properly
1034 x1 = 1000;x2 = -1000;
1035 y1 = 1000;y2 = -1000;
1036 for (i = 0;i < 8;i++)
1038 v[0] = (i & 1) ? mins[0] : maxs[0];
1039 v[1] = (i & 2) ? mins[1] : maxs[1];
1040 v[2] = (i & 4) ? mins[2] : maxs[2];
1042 GL_TransformToScreen(v, v2);
1043 //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]);
1061 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1062 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1063 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1064 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1065 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1066 if (ix2 <= ix1 || iy2 <= iy1)
1068 // set up the scissor rectangle
1069 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1070 qglEnable(GL_SCISSOR_TEST);
1075 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1077 float *color4f = varray_color4f;
1078 float dist, dot, intensity, v[3], n[3];
1079 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1081 Matrix4x4_Transform(m, vertex3f, v);
1082 if ((dist = DotProduct(v, v)) < 1)
1084 Matrix4x4_Transform3x3(m, normal3f, n);
1085 if ((dot = DotProduct(n, v)) > 0)
1088 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1089 VectorScale(lightcolor, intensity, color4f);
1094 VectorClear(color4f);
1100 VectorClear(color4f);
1106 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1108 float *color4f = varray_color4f;
1109 float dist, dot, intensity, v[3], n[3];
1110 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1112 Matrix4x4_Transform(m, vertex3f, v);
1113 if ((dist = fabs(v[2])) < 1)
1115 Matrix4x4_Transform3x3(m, normal3f, n);
1116 if ((dot = DotProduct(n, v)) > 0)
1118 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1119 VectorScale(lightcolor, intensity, color4f);
1124 VectorClear(color4f);
1130 VectorClear(color4f);
1136 // FIXME: this should be done in a vertex program when possible
1137 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1138 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1142 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1143 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1144 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1151 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1155 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1156 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1163 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)
1167 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1169 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1170 // the cubemap normalizes this for us
1171 out3f[0] = DotProduct(svector3f, lightdir);
1172 out3f[1] = DotProduct(tvector3f, lightdir);
1173 out3f[2] = DotProduct(normal3f, lightdir);
1177 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)
1180 float lightdir[3], eyedir[3], halfdir[3];
1181 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1183 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1184 VectorNormalizeFast(lightdir);
1185 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1186 VectorNormalizeFast(eyedir);
1187 VectorAdd(lightdir, eyedir, halfdir);
1188 // the cubemap normalizes this for us
1189 out3f[0] = DotProduct(svector3f, halfdir);
1190 out3f[1] = DotProduct(tvector3f, halfdir);
1191 out3f[2] = DotProduct(normal3f, halfdir);
1195 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)
1198 float color[3], color2[3];
1200 GL_VertexPointer(vertex3f);
1201 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1204 bumptexture = r_shadow_blankbumptexture;
1206 // colorscale accounts for how much we multiply the brightness during combine
1207 // mult is how many times the final pass of the lighting will be
1208 // performed to get more brightness than otherwise possible
1209 // limit mult to 64 for sanity sake
1210 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1212 // 3/2 3D combine path (Geforce3, Radeon 8500)
1213 memset(&m, 0, sizeof(m));
1214 m.tex[0] = R_GetTexture(bumptexture);
1215 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1216 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1217 m.texcombinergb[0] = GL_REPLACE;
1218 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1219 m.pointer_texcoord[0] = texcoord2f;
1220 m.pointer_texcoord[1] = varray_texcoord3f[1];
1221 m.pointer_texcoord[2] = varray_texcoord3f[2];
1222 R_Mesh_State_Texture(&m);
1223 qglColorMask(0,0,0,1);
1224 GL_BlendFunc(GL_ONE, GL_ZERO);
1225 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1226 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1227 R_Mesh_Draw(numverts, numtriangles, elements);
1229 c_rt_lighttris += numtriangles;
1231 memset(&m, 0, sizeof(m));
1232 m.tex[0] = R_GetTexture(basetexture);
1233 m.texcubemap[1] = R_GetTexture(lightcubemap);
1234 m.pointer_texcoord[0] = texcoord2f;
1235 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1236 R_Mesh_State_Texture(&m);
1237 qglColorMask(1,1,1,0);
1238 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1240 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1241 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1242 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1244 color[0] = bound(0, color2[0], 1);
1245 color[1] = bound(0, color2[1], 1);
1246 color[2] = bound(0, color2[2], 1);
1247 GL_Color(color[0], color[1], color[2], 1);
1248 R_Mesh_Draw(numverts, numtriangles, elements);
1250 c_rt_lighttris += numtriangles;
1253 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1255 // 1/2/2 3D combine path (original Radeon)
1256 memset(&m, 0, sizeof(m));
1257 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1258 m.pointer_texcoord[0] = varray_texcoord3f[0];
1259 R_Mesh_State_Texture(&m);
1260 qglColorMask(0,0,0,1);
1261 GL_BlendFunc(GL_ONE, GL_ZERO);
1262 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1263 R_Mesh_Draw(numverts, numtriangles, elements);
1265 c_rt_lighttris += numtriangles;
1267 memset(&m, 0, sizeof(m));
1268 m.tex[0] = R_GetTexture(bumptexture);
1269 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1270 m.texcombinergb[0] = GL_REPLACE;
1271 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1272 m.pointer_texcoord[0] = texcoord2f;
1273 m.pointer_texcoord[1] = varray_texcoord3f[1];
1274 R_Mesh_State_Texture(&m);
1275 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1276 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1277 R_Mesh_Draw(numverts, numtriangles, elements);
1279 c_rt_lighttris += numtriangles;
1281 memset(&m, 0, sizeof(m));
1282 m.tex[0] = R_GetTexture(basetexture);
1283 m.texcubemap[1] = R_GetTexture(lightcubemap);
1284 m.pointer_texcoord[0] = texcoord2f;
1285 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1286 R_Mesh_State_Texture(&m);
1287 qglColorMask(1,1,1,0);
1288 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1290 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1291 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1292 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1294 color[0] = bound(0, color2[0], 1);
1295 color[1] = bound(0, color2[1], 1);
1296 color[2] = bound(0, color2[2], 1);
1297 GL_Color(color[0], color[1], color[2], 1);
1298 R_Mesh_Draw(numverts, numtriangles, elements);
1300 c_rt_lighttris += numtriangles;
1303 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1305 // 2/2 3D combine path (original Radeon)
1306 memset(&m, 0, sizeof(m));
1307 m.tex[0] = R_GetTexture(bumptexture);
1308 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1309 m.texcombinergb[0] = GL_REPLACE;
1310 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1311 m.pointer_texcoord[0] = texcoord2f;
1312 m.pointer_texcoord[1] = varray_texcoord3f[1];
1313 R_Mesh_State_Texture(&m);
1314 qglColorMask(0,0,0,1);
1315 GL_BlendFunc(GL_ONE, GL_ZERO);
1316 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1317 R_Mesh_Draw(numverts, numtriangles, elements);
1319 c_rt_lighttris += numtriangles;
1321 memset(&m, 0, sizeof(m));
1322 m.tex[0] = R_GetTexture(basetexture);
1323 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1324 m.pointer_texcoord[0] = texcoord2f;
1325 m.pointer_texcoord[1] = varray_texcoord3f[1];
1326 R_Mesh_State_Texture(&m);
1327 qglColorMask(1,1,1,0);
1328 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1329 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1330 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1331 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1333 color[0] = bound(0, color2[0], 1);
1334 color[1] = bound(0, color2[1], 1);
1335 color[2] = bound(0, color2[2], 1);
1336 GL_Color(color[0], color[1], color[2], 1);
1337 R_Mesh_Draw(numverts, numtriangles, elements);
1339 c_rt_lighttris += numtriangles;
1342 else if (r_textureunits.integer >= 4)
1344 // 4/2 2D combine path (Geforce3, Radeon 8500)
1345 memset(&m, 0, sizeof(m));
1346 m.tex[0] = R_GetTexture(bumptexture);
1347 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1348 m.texcombinergb[0] = GL_REPLACE;
1349 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1350 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1351 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1352 m.pointer_texcoord[0] = texcoord2f;
1353 m.pointer_texcoord[1] = varray_texcoord3f[1];
1354 m.pointer_texcoord[2] = varray_texcoord2f[2];
1355 m.pointer_texcoord[3] = varray_texcoord2f[3];
1356 R_Mesh_State_Texture(&m);
1357 qglColorMask(0,0,0,1);
1358 GL_BlendFunc(GL_ONE, GL_ZERO);
1359 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1360 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1361 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1362 R_Mesh_Draw(numverts, numtriangles, elements);
1364 c_rt_lighttris += numtriangles;
1366 memset(&m, 0, sizeof(m));
1367 m.tex[0] = R_GetTexture(basetexture);
1368 m.texcubemap[1] = R_GetTexture(lightcubemap);
1369 m.pointer_texcoord[0] = texcoord2f;
1370 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1371 R_Mesh_State_Texture(&m);
1372 qglColorMask(1,1,1,0);
1373 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1375 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1376 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1377 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1379 color[0] = bound(0, color2[0], 1);
1380 color[1] = bound(0, color2[1], 1);
1381 color[2] = bound(0, color2[2], 1);
1382 GL_Color(color[0], color[1], color[2], 1);
1383 R_Mesh_Draw(numverts, numtriangles, elements);
1385 c_rt_lighttris += numtriangles;
1390 // 2/2/2 2D combine path (any dot3 card)
1391 memset(&m, 0, sizeof(m));
1392 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1393 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1394 m.pointer_texcoord[0] = varray_texcoord2f[0];
1395 m.pointer_texcoord[1] = varray_texcoord2f[1];
1396 R_Mesh_State_Texture(&m);
1397 qglColorMask(0,0,0,1);
1398 GL_BlendFunc(GL_ONE, GL_ZERO);
1399 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1400 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1401 R_Mesh_Draw(numverts, numtriangles, elements);
1403 c_rt_lighttris += numtriangles;
1405 memset(&m, 0, sizeof(m));
1406 m.tex[0] = R_GetTexture(bumptexture);
1407 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1408 m.texcombinergb[0] = GL_REPLACE;
1409 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1410 m.pointer_texcoord[0] = texcoord2f;
1411 m.pointer_texcoord[1] = varray_texcoord3f[1];
1412 R_Mesh_State_Texture(&m);
1413 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1414 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1415 R_Mesh_Draw(numverts, numtriangles, elements);
1417 c_rt_lighttris += numtriangles;
1419 memset(&m, 0, sizeof(m));
1420 m.tex[0] = R_GetTexture(basetexture);
1421 m.texcubemap[1] = R_GetTexture(lightcubemap);
1422 m.pointer_texcoord[0] = texcoord2f;
1423 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1424 R_Mesh_State_Texture(&m);
1425 qglColorMask(1,1,1,0);
1426 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1428 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1429 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1430 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1432 color[0] = bound(0, color2[0], 1);
1433 color[1] = bound(0, color2[1], 1);
1434 color[2] = bound(0, color2[2], 1);
1435 GL_Color(color[0], color[1], color[2], 1);
1436 R_Mesh_Draw(numverts, numtriangles, elements);
1438 c_rt_lighttris += numtriangles;
1444 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1445 GL_DepthMask(false);
1447 GL_ColorPointer(varray_color4f);
1448 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1449 memset(&m, 0, sizeof(m));
1450 m.tex[0] = R_GetTexture(basetexture);
1451 m.pointer_texcoord[0] = texcoord2f;
1452 if (r_textureunits.integer >= 2)
1455 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1456 m.pointer_texcoord[1] = varray_texcoord2f[1];
1457 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1459 R_Mesh_State_Texture(&m);
1460 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1462 color[0] = bound(0, color2[0], 1);
1463 color[1] = bound(0, color2[1], 1);
1464 color[2] = bound(0, color2[2], 1);
1465 if (r_textureunits.integer >= 2)
1466 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1468 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1469 R_Mesh_Draw(numverts, numtriangles, elements);
1471 c_rt_lighttris += numtriangles;
1476 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)
1479 float color[3], color2[3], colorscale;
1481 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1484 glosstexture = r_shadow_blankglosstexture;
1485 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1487 colorscale = r_colorscale * r_shadow_glossintensity.value;
1489 bumptexture = r_shadow_blankbumptexture;
1490 if (glosstexture == r_shadow_blankglosstexture)
1491 colorscale *= r_shadow_gloss2intensity.value;
1492 GL_VertexPointer(vertex3f);
1494 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1496 // 2/0/0/1/2 3D combine blendsquare path
1497 memset(&m, 0, sizeof(m));
1498 m.tex[0] = R_GetTexture(bumptexture);
1499 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1500 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1501 m.pointer_texcoord[0] = texcoord2f;
1502 m.pointer_texcoord[1] = varray_texcoord3f[1];
1503 R_Mesh_State_Texture(&m);
1504 qglColorMask(0,0,0,1);
1505 // this squares the result
1506 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1507 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1508 R_Mesh_Draw(numverts, numtriangles, elements);
1510 c_rt_lighttris += numtriangles;
1512 memset(&m, 0, sizeof(m));
1513 R_Mesh_State_Texture(&m);
1514 // square alpha in framebuffer a few times to make it shiny
1515 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1516 // these comments are a test run through this math for intensity 0.5
1517 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1518 // 0.25 * 0.25 = 0.0625 (this is another pass)
1519 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1520 R_Mesh_Draw(numverts, numtriangles, elements);
1522 c_rt_lighttris += numtriangles;
1523 R_Mesh_Draw(numverts, numtriangles, elements);
1525 c_rt_lighttris += numtriangles;
1527 memset(&m, 0, sizeof(m));
1528 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1529 m.pointer_texcoord[0] = varray_texcoord3f[0];
1530 R_Mesh_State_Texture(&m);
1531 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1532 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1533 R_Mesh_Draw(numverts, numtriangles, elements);
1535 c_rt_lighttris += numtriangles;
1537 memset(&m, 0, sizeof(m));
1538 m.tex[0] = R_GetTexture(glosstexture);
1539 m.texcubemap[1] = R_GetTexture(lightcubemap);
1540 m.pointer_texcoord[0] = texcoord2f;
1541 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1542 R_Mesh_State_Texture(&m);
1543 qglColorMask(1,1,1,0);
1544 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1546 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1547 VectorScale(lightcolor, colorscale, color2);
1548 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1550 color[0] = bound(0, color2[0], 1);
1551 color[1] = bound(0, color2[1], 1);
1552 color[2] = bound(0, color2[2], 1);
1553 GL_Color(color[0], color[1], color[2], 1);
1554 R_Mesh_Draw(numverts, numtriangles, elements);
1556 c_rt_lighttris += numtriangles;
1559 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1561 // 2/0/0/2 3D combine blendsquare path
1562 memset(&m, 0, sizeof(m));
1563 m.tex[0] = R_GetTexture(bumptexture);
1564 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1565 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1566 m.pointer_texcoord[0] = texcoord2f;
1567 m.pointer_texcoord[1] = varray_texcoord3f[1];
1568 R_Mesh_State_Texture(&m);
1569 qglColorMask(0,0,0,1);
1570 // this squares the result
1571 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1572 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1573 R_Mesh_Draw(numverts, numtriangles, elements);
1575 c_rt_lighttris += numtriangles;
1577 memset(&m, 0, sizeof(m));
1578 R_Mesh_State_Texture(&m);
1579 // square alpha in framebuffer a few times to make it shiny
1580 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1581 // these comments are a test run through this math for intensity 0.5
1582 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1583 // 0.25 * 0.25 = 0.0625 (this is another pass)
1584 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1585 R_Mesh_Draw(numverts, numtriangles, elements);
1587 c_rt_lighttris += numtriangles;
1588 R_Mesh_Draw(numverts, numtriangles, elements);
1590 c_rt_lighttris += numtriangles;
1592 memset(&m, 0, sizeof(m));
1593 m.tex[0] = R_GetTexture(glosstexture);
1594 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1595 m.pointer_texcoord[0] = texcoord2f;
1596 m.pointer_texcoord[1] = varray_texcoord3f[1];
1597 R_Mesh_State_Texture(&m);
1598 qglColorMask(1,1,1,0);
1599 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1600 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1601 VectorScale(lightcolor, colorscale, color2);
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 GL_Color(color[0], color[1], color[2], 1);
1608 R_Mesh_Draw(numverts, numtriangles, elements);
1610 c_rt_lighttris += numtriangles;
1613 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1615 // 2/0/0/2/2 2D combine blendsquare path
1616 memset(&m, 0, sizeof(m));
1617 m.tex[0] = R_GetTexture(bumptexture);
1618 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1619 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1620 m.pointer_texcoord[0] = texcoord2f;
1621 m.pointer_texcoord[1] = varray_texcoord3f[1];
1622 R_Mesh_State_Texture(&m);
1623 qglColorMask(0,0,0,1);
1624 // this squares the result
1625 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1626 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1627 R_Mesh_Draw(numverts, numtriangles, elements);
1629 c_rt_lighttris += numtriangles;
1631 memset(&m, 0, sizeof(m));
1632 R_Mesh_State_Texture(&m);
1633 // square alpha in framebuffer a few times to make it shiny
1634 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1635 // these comments are a test run through this math for intensity 0.5
1636 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1637 // 0.25 * 0.25 = 0.0625 (this is another pass)
1638 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1639 R_Mesh_Draw(numverts, numtriangles, elements);
1641 c_rt_lighttris += numtriangles;
1642 R_Mesh_Draw(numverts, numtriangles, elements);
1644 c_rt_lighttris += numtriangles;
1646 memset(&m, 0, sizeof(m));
1647 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1648 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1649 m.pointer_texcoord[0] = varray_texcoord2f[0];
1650 m.pointer_texcoord[1] = varray_texcoord2f[1];
1651 R_Mesh_State_Texture(&m);
1652 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1653 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1654 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1655 R_Mesh_Draw(numverts, numtriangles, elements);
1657 c_rt_lighttris += numtriangles;
1659 memset(&m, 0, sizeof(m));
1660 m.tex[0] = R_GetTexture(glosstexture);
1661 m.texcubemap[1] = R_GetTexture(lightcubemap);
1662 m.pointer_texcoord[0] = texcoord2f;
1663 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1664 R_Mesh_State_Texture(&m);
1665 qglColorMask(1,1,1,0);
1666 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1668 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1669 VectorScale(lightcolor, colorscale, color2);
1670 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1672 color[0] = bound(0, color2[0], 1);
1673 color[1] = bound(0, color2[1], 1);
1674 color[2] = bound(0, color2[2], 1);
1675 GL_Color(color[0], color[1], color[2], 1);
1676 R_Mesh_Draw(numverts, numtriangles, elements);
1678 c_rt_lighttris += numtriangles;
1684 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1686 R_Mesh_Matrix(matrix);
1687 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1690 cvar_t r_editlights = {0, "r_editlights", "0"};
1691 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1692 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1693 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1694 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1695 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1696 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1697 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1698 worldlight_t *r_shadow_worldlightchain;
1699 worldlight_t *r_shadow_selectedlight;
1700 vec3_t r_editlights_cursorlocation;
1702 static int lightpvsbytes;
1703 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1705 static int castshadowcount = 1;
1706 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1708 int i, j, k, l, maxverts = 256, *mark, tris, numsurfaces;
1709 float *vertex3f = NULL, mins[3], maxs[3];
1711 shadowmesh_t *mesh, *castmesh;
1714 surfmesh_t *surfmesh;
1716 if (radius < 15 || DotProduct(color, color) < 0.03)
1718 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1722 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1723 VectorCopy(origin, e->origin);
1724 VectorCopy(color, e->light);
1725 e->lightradius = radius;
1727 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1729 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1732 e->castshadows = castshadow;
1734 e->cullradius = e->lightradius;
1735 for (k = 0;k < 3;k++)
1737 mins[k] = e->origin[k] - e->lightradius;
1738 maxs[k] = e->origin[k] + e->lightradius;
1741 e->next = r_shadow_worldlightchain;
1742 r_shadow_worldlightchain = e;
1743 if (cubemapname && cubemapname[0])
1745 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1746 strcpy(e->cubemapname, cubemapname);
1747 // FIXME: add cubemap loading (and don't load a cubemap twice)
1752 VectorCopy(e->origin, e->mins);
1753 VectorCopy(e->origin, e->maxs);
1754 i = CL_PointQ1Contents(e->origin);
1755 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1758 qbyte *bytesurfacepvs;
1760 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1761 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1763 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1765 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1767 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1769 for (k = 0;k < 3;k++)
1771 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1772 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1777 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1778 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1779 surf->castshadow = castshadowcount;
1781 Mem_Free(byteleafpvs);
1782 Mem_Free(bytesurfacepvs);
1786 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1787 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1789 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1791 for (k = 0;k < 3;k++)
1793 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1794 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1796 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1798 surf = cl.worldmodel->brushq1.surfaces + *mark;
1799 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1800 surf->castshadow = castshadowcount;
1806 for (k = 0;k < 3;k++)
1808 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1809 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1811 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1814 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1815 if (surf->castshadow == castshadowcount)
1818 e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
1820 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1821 if (surf->castshadow == castshadowcount)
1822 e->surfaces[e->numsurfaces++] = surf;
1827 for (j = 0;j < e->numsurfaces;j++)
1829 surf = e->surfaces[j];
1830 if (surf->flags & SURF_SHADOWCAST)
1832 surf->castshadow = castshadowcount;
1833 if (maxverts < surf->poly_numverts)
1834 maxverts = surf->poly_numverts;
1837 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1838 // make a mesh to cast a shadow volume from
1839 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1840 for (j = 0;j < e->numsurfaces;j++)
1841 if (e->surfaces[j]->castshadow == castshadowcount)
1842 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1843 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1844 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1846 // cast shadow volume from castmesh
1847 for (mesh = castmesh;mesh;mesh = mesh->next)
1849 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1851 if (maxverts < castmesh->numverts * 2)
1853 maxverts = castmesh->numverts * 2;
1858 if (vertex3f == NULL && maxverts > 0)
1859 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1861 // now that we have the buffers big enough, construct and add
1862 // the shadow volume mesh
1863 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)))
1864 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1869 // we're done with castmesh now
1870 Mod_ShadowMesh_Free(castmesh);
1871 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1872 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1873 l += mesh->numtriangles;
1874 Con_Printf("static shadow volume built containing %i triangles\n", l);
1877 Con_Printf("%f %f %f, %f %f %f, %f, %f, %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->numsurfaces);
1880 void R_Shadow_FreeWorldLight(worldlight_t *light)
1882 worldlight_t **lightpointer;
1883 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1884 if (*lightpointer != light)
1885 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1886 *lightpointer = light->next;
1887 if (light->cubemapname)
1888 Mem_Free(light->cubemapname);
1889 if (light->shadowvolume)
1890 Mod_ShadowMesh_Free(light->shadowvolume);
1891 if (light->surfaces)
1892 Mem_Free(light->surfaces);
1896 void R_Shadow_ClearWorldLights(void)
1898 while (r_shadow_worldlightchain)
1899 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1900 r_shadow_selectedlight = NULL;
1903 void R_Shadow_SelectLight(worldlight_t *light)
1905 if (r_shadow_selectedlight)
1906 r_shadow_selectedlight->selected = false;
1907 r_shadow_selectedlight = light;
1908 if (r_shadow_selectedlight)
1909 r_shadow_selectedlight->selected = true;
1912 rtexture_t *lighttextures[5];
1914 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1916 float scale = r_editlights_cursorgrid.value * 0.5f;
1917 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
1920 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1923 const worldlight_t *light;
1926 if (light->selected)
1927 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1928 if (!light->shadowvolume)
1930 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
1933 void R_Shadow_DrawLightSprites(void)
1937 worldlight_t *light;
1939 for (i = 0;i < 5;i++)
1941 lighttextures[i] = NULL;
1942 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
1943 lighttextures[i] = pic->tex;
1946 for (light = r_shadow_worldlightchain;light;light = light->next)
1947 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
1948 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1951 void R_Shadow_SelectLightInView(void)
1953 float bestrating, rating, temp[3];
1954 worldlight_t *best, *light;
1957 for (light = r_shadow_worldlightchain;light;light = light->next)
1959 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1960 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1963 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1964 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
1966 bestrating = rating;
1971 R_Shadow_SelectLight(best);
1974 void R_Shadow_LoadWorldLights(void)
1976 int n, a, style, shadow;
1977 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1978 float origin[3], radius, color[3];
1979 if (cl.worldmodel == NULL)
1981 Con_Printf("No map loaded.\n");
1984 FS_StripExtension(cl.worldmodel->name, name);
1985 strcat(name, ".rtlights");
1986 lightsstring = FS_LoadFile(name, false);
1994 while (*s && *s != '\n')
2000 // check for modifier flags
2006 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);
2012 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);
2015 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2016 radius *= r_editlights_rtlightssizescale.value;
2017 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2022 Con_Printf("invalid rtlights file \"%s\"\n", name);
2023 Mem_Free(lightsstring);
2027 void R_Shadow_SaveWorldLights(void)
2029 worldlight_t *light;
2030 int bufchars, bufmaxchars;
2032 char name[MAX_QPATH];
2034 if (!r_shadow_worldlightchain)
2036 if (cl.worldmodel == NULL)
2038 Con_Printf("No map loaded.\n");
2041 FS_StripExtension(cl.worldmodel->name, name);
2042 strcat(name, ".rtlights");
2043 bufchars = bufmaxchars = 0;
2045 for (light = r_shadow_worldlightchain;light;light = light->next)
2047 sprintf(line, "%s%f %f %f %f %f %f %f %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2048 if (bufchars + (int) strlen(line) > bufmaxchars)
2050 bufmaxchars = bufchars + strlen(line) + 2048;
2052 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2056 memcpy(buf, oldbuf, bufchars);
2062 memcpy(buf + bufchars, line, strlen(line));
2063 bufchars += strlen(line);
2067 FS_WriteFile(name, buf, bufchars);
2072 void R_Shadow_LoadLightsFile(void)
2075 char name[MAX_QPATH], *lightsstring, *s, *t;
2076 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2077 if (cl.worldmodel == NULL)
2079 Con_Printf("No map loaded.\n");
2082 FS_StripExtension(cl.worldmodel->name, name);
2083 strcat(name, ".lights");
2084 lightsstring = FS_LoadFile(name, false);
2092 while (*s && *s != '\n')
2097 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);
2101 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);
2104 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2105 radius = bound(15, radius, 4096);
2106 VectorScale(color, (2.0f / (8388608.0f)), color);
2107 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2112 Con_Printf("invalid lights file \"%s\"\n", name);
2113 Mem_Free(lightsstring);
2117 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2119 int entnum, style, islight;
2120 char key[256], value[1024];
2121 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2124 if (cl.worldmodel == NULL)
2126 Con_Printf("No map loaded.\n");
2129 data = cl.worldmodel->brush.entities;
2132 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2135 origin[0] = origin[1] = origin[2] = 0;
2136 originhack[0] = originhack[1] = originhack[2] = 0;
2137 color[0] = color[1] = color[2] = 1;
2138 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2144 if (!COM_ParseToken(&data, false))
2146 if (com_token[0] == '}')
2147 break; // end of entity
2148 if (com_token[0] == '_')
2149 strcpy(key, com_token + 1);
2151 strcpy(key, com_token);
2152 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2153 key[strlen(key)-1] = 0;
2154 if (!COM_ParseToken(&data, false))
2156 strcpy(value, com_token);
2158 // now that we have the key pair worked out...
2159 if (!strcmp("light", key))
2160 light = atof(value);
2161 else if (!strcmp("origin", key))
2162 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2163 else if (!strcmp("color", key))
2164 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2165 else if (!strcmp("wait", key))
2166 scale = atof(value);
2167 else if (!strcmp("classname", key))
2169 if (!strncmp(value, "light", 5))
2172 if (!strcmp(value, "light_fluoro"))
2177 overridecolor[0] = 1;
2178 overridecolor[1] = 1;
2179 overridecolor[2] = 1;
2181 if (!strcmp(value, "light_fluorospark"))
2186 overridecolor[0] = 1;
2187 overridecolor[1] = 1;
2188 overridecolor[2] = 1;
2190 if (!strcmp(value, "light_globe"))
2195 overridecolor[0] = 1;
2196 overridecolor[1] = 0.8;
2197 overridecolor[2] = 0.4;
2199 if (!strcmp(value, "light_flame_large_yellow"))
2204 overridecolor[0] = 1;
2205 overridecolor[1] = 0.5;
2206 overridecolor[2] = 0.1;
2208 if (!strcmp(value, "light_flame_small_yellow"))
2213 overridecolor[0] = 1;
2214 overridecolor[1] = 0.5;
2215 overridecolor[2] = 0.1;
2217 if (!strcmp(value, "light_torch_small_white"))
2222 overridecolor[0] = 1;
2223 overridecolor[1] = 0.5;
2224 overridecolor[2] = 0.1;
2226 if (!strcmp(value, "light_torch_small_walltorch"))
2231 overridecolor[0] = 1;
2232 overridecolor[1] = 0.5;
2233 overridecolor[2] = 0.1;
2237 else if (!strcmp("style", key))
2238 style = atoi(value);
2240 if (light <= 0 && islight)
2242 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2243 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2244 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2245 VectorCopy(overridecolor, color);
2246 VectorScale(color, light, color);
2247 VectorAdd(origin, originhack, origin);
2249 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2254 void R_Shadow_SetCursorLocationForView(void)
2256 vec_t dist, push, frac;
2257 vec3_t dest, endpos, normal;
2258 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2259 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2262 dist = frac * r_editlights_cursordistance.value;
2263 push = r_editlights_cursorpushback.value;
2267 VectorMA(endpos, push, vpn, endpos);
2268 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2270 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2271 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2272 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2275 void R_Shadow_UpdateWorldLightSelection(void)
2277 if (r_editlights.integer)
2279 R_Shadow_SetCursorLocationForView();
2280 R_Shadow_SelectLightInView();
2281 R_Shadow_DrawLightSprites();
2284 R_Shadow_SelectLight(NULL);
2287 void R_Shadow_EditLights_Clear_f(void)
2289 R_Shadow_ClearWorldLights();
2292 void R_Shadow_EditLights_Reload_f(void)
2294 r_shadow_reloadlights = true;
2297 void R_Shadow_EditLights_Save_f(void)
2300 R_Shadow_SaveWorldLights();
2303 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2305 R_Shadow_ClearWorldLights();
2306 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2309 void R_Shadow_EditLights_ImportLightsFile_f(void)
2311 R_Shadow_ClearWorldLights();
2312 R_Shadow_LoadLightsFile();
2315 void R_Shadow_EditLights_Spawn_f(void)
2318 if (!r_editlights.integer)
2320 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2323 if (Cmd_Argc() != 1)
2325 Con_Printf("r_editlights_spawn does not take parameters\n");
2328 color[0] = color[1] = color[2] = 1;
2329 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2332 void R_Shadow_EditLights_Edit_f(void)
2334 vec3_t origin, color;
2337 char cubemapname[1024];
2338 if (!r_editlights.integer)
2340 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2343 if (!r_shadow_selectedlight)
2345 Con_Printf("No selected light.\n");
2348 VectorCopy(r_shadow_selectedlight->origin, origin);
2349 radius = r_shadow_selectedlight->lightradius;
2350 VectorCopy(r_shadow_selectedlight->light, color);
2351 style = r_shadow_selectedlight->style;
2352 if (r_shadow_selectedlight->cubemapname)
2353 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2356 shadows = r_shadow_selectedlight->castshadows;
2357 if (!strcmp(Cmd_Argv(1), "origin"))
2359 if (Cmd_Argc() != 5)
2361 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2364 origin[0] = atof(Cmd_Argv(2));
2365 origin[1] = atof(Cmd_Argv(3));
2366 origin[2] = atof(Cmd_Argv(4));
2368 else if (!strcmp(Cmd_Argv(1), "originx"))
2370 if (Cmd_Argc() != 3)
2372 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2375 origin[0] = atof(Cmd_Argv(2));
2377 else if (!strcmp(Cmd_Argv(1), "originy"))
2379 if (Cmd_Argc() != 3)
2381 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2384 origin[1] = atof(Cmd_Argv(2));
2386 else if (!strcmp(Cmd_Argv(1), "originz"))
2388 if (Cmd_Argc() != 3)
2390 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2393 origin[2] = atof(Cmd_Argv(2));
2395 else if (!strcmp(Cmd_Argv(1), "move"))
2397 if (Cmd_Argc() != 5)
2399 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2402 origin[0] += atof(Cmd_Argv(2));
2403 origin[1] += atof(Cmd_Argv(3));
2404 origin[2] += atof(Cmd_Argv(4));
2406 else if (!strcmp(Cmd_Argv(1), "movex"))
2408 if (Cmd_Argc() != 3)
2410 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2413 origin[0] += atof(Cmd_Argv(2));
2415 else if (!strcmp(Cmd_Argv(1), "movey"))
2417 if (Cmd_Argc() != 3)
2419 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2422 origin[1] += atof(Cmd_Argv(2));
2424 else if (!strcmp(Cmd_Argv(1), "movez"))
2426 if (Cmd_Argc() != 3)
2428 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2431 origin[2] += atof(Cmd_Argv(2));
2433 else if (!strcmp(Cmd_Argv(1), "color"))
2435 if (Cmd_Argc() != 5)
2437 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2440 color[0] = atof(Cmd_Argv(2));
2441 color[1] = atof(Cmd_Argv(3));
2442 color[2] = atof(Cmd_Argv(4));
2444 else if (!strcmp(Cmd_Argv(1), "radius"))
2446 if (Cmd_Argc() != 3)
2448 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2451 radius = atof(Cmd_Argv(2));
2453 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2455 if (Cmd_Argc() != 3)
2457 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2460 style = atoi(Cmd_Argv(2));
2462 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2466 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2469 if (Cmd_Argc() == 3)
2470 strcpy(cubemapname, Cmd_Argv(2));
2474 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2476 if (Cmd_Argc() != 3)
2478 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2481 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2485 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2486 Con_Printf("Selected light's properties:\n");
2487 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2488 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2489 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2490 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2491 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2492 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2495 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2496 r_shadow_selectedlight = NULL;
2497 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2500 extern int con_vislines;
2501 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2505 if (r_shadow_selectedlight == NULL)
2509 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2510 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;
2511 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2512 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;
2513 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2514 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2515 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;
2518 void R_Shadow_EditLights_ToggleShadow_f(void)
2520 if (!r_editlights.integer)
2522 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2525 if (!r_shadow_selectedlight)
2527 Con_Printf("No selected light.\n");
2530 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);
2531 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2532 r_shadow_selectedlight = NULL;
2535 void R_Shadow_EditLights_Remove_f(void)
2537 if (!r_editlights.integer)
2539 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2542 if (!r_shadow_selectedlight)
2544 Con_Printf("No selected light.\n");
2547 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2548 r_shadow_selectedlight = NULL;
2551 void R_Shadow_EditLights_Init(void)
2553 Cvar_RegisterVariable(&r_editlights);
2554 Cvar_RegisterVariable(&r_editlights_cursordistance);
2555 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2556 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2557 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2558 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2559 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2560 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2561 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2562 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2563 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2564 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2565 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2566 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2567 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2568 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2569 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);