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"};
159 cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
161 int c_rt_lights, c_rt_clears, c_rt_scissored;
162 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
163 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
165 void R_Shadow_ClearWorldLights(void);
166 void R_Shadow_SaveWorldLights(void);
167 void R_Shadow_LoadWorldLights(void);
168 void R_Shadow_LoadLightsFile(void);
169 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
171 void r_shadow_start(void)
173 // allocate vertex processing arrays
174 r_shadow_mempool = Mem_AllocPool("R_Shadow");
175 maxshadowelements = 0;
176 shadowelements = NULL;
181 maxtrianglefacinglight = 0;
182 trianglefacinglight = NULL;
183 trianglefacinglightlist = NULL;
184 r_shadow_normalcubetexture = NULL;
185 r_shadow_attenuation2dtexture = NULL;
186 r_shadow_attenuation3dtexture = NULL;
187 r_shadow_blankbumptexture = NULL;
188 r_shadow_blankglosstexture = NULL;
189 r_shadow_blankwhitetexture = NULL;
190 r_shadow_texturepool = NULL;
191 R_Shadow_ClearWorldLights();
192 r_shadow_reloadlights = true;
195 void r_shadow_shutdown(void)
197 R_Shadow_ClearWorldLights();
198 r_shadow_reloadlights = true;
199 r_shadow_normalcubetexture = NULL;
200 r_shadow_attenuation2dtexture = NULL;
201 r_shadow_attenuation3dtexture = NULL;
202 r_shadow_blankbumptexture = NULL;
203 r_shadow_blankglosstexture = NULL;
204 r_shadow_blankwhitetexture = NULL;
205 R_FreeTexturePool(&r_shadow_texturepool);
206 maxshadowelements = 0;
207 shadowelements = NULL;
212 maxtrianglefacinglight = 0;
213 trianglefacinglight = NULL;
214 trianglefacinglightlist = NULL;
215 Mem_FreePool(&r_shadow_mempool);
218 void r_shadow_newmap(void)
220 R_Shadow_ClearWorldLights();
221 r_shadow_reloadlights = true;
224 void R_Shadow_Init(void)
226 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
227 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
228 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
229 Cvar_RegisterVariable(&r_shadow_realtime_world);
230 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
231 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
232 Cvar_RegisterVariable(&r_shadow_gloss);
233 Cvar_RegisterVariable(&r_shadow_glossintensity);
234 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
235 Cvar_RegisterVariable(&r_shadow_debuglight);
236 Cvar_RegisterVariable(&r_shadow_scissor);
237 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
238 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
239 Cvar_RegisterVariable(&r_shadow_polygonoffset);
240 Cvar_RegisterVariable(&r_shadow_portallight);
241 Cvar_RegisterVariable(&r_shadow_projectdistance);
242 Cvar_RegisterVariable(&r_shadow_texture3d);
243 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
244 Cvar_RegisterVariable(&r_shadow_shadows);
245 R_Shadow_EditLights_Init();
246 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
249 void R_Shadow_ResizeTriangleFacingLight(int numtris)
251 // make sure trianglefacinglight is big enough for this volume
252 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
253 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
254 if (maxtrianglefacinglight < numtris)
256 maxtrianglefacinglight = numtris;
257 if (trianglefacinglight)
258 Mem_Free(trianglefacinglight);
259 if (trianglefacinglightlist)
260 Mem_Free(trianglefacinglightlist);
261 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
262 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
266 int *R_Shadow_ResizeShadowElements(int numtris)
268 // make sure shadowelements is big enough for this volume
269 if (maxshadowelements < numtris * 24)
271 maxshadowelements = numtris * 24;
273 Mem_Free(shadowelements);
274 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
276 return shadowelements;
280 // readable version of some code found below
281 //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]))))
282 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
284 float dir0[3], dir1[3], normal[3];
286 // calculate two mostly perpendicular edge directions
287 VectorSubtract(a, b, dir0);
288 VectorSubtract(c, b, dir1);
290 // we have two edge directions, we can calculate a third vector from
291 // them, which is the direction of the surface normal (it's magnitude
293 CrossProduct(dir0, dir1, normal);
295 // compare distance of light along normal, with distance of any point
296 // of the triangle along the same normal (the triangle is planar,
297 // I.E. flat, so all points give the same answer)
298 return DotProduct(p, normal) > DotProduct(a, normal);
300 int checkcastshadowfromedge(int t, int i)
304 if (t >= trianglerange_start && t < trianglerange_end)
306 if (t < i && !trianglefacinglight[t])
317 te = inelement3i + t * 3;
318 v[0] = invertex3f + te[0] * 3;
319 v[1] = invertex3f + te[1] * 3;
320 v[2] = invertex3f + te[2] * 3;
321 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
330 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)
332 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
334 const int *e, *n, *te;
337 // make sure trianglefacinglight is big enough for this volume
338 if (maxtrianglefacinglight < trianglerange_end)
339 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
341 if (maxvertexupdate < innumvertices)
343 maxvertexupdate = innumvertices;
345 Mem_Free(vertexupdate);
347 Mem_Free(vertexremap);
348 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
349 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
353 if (r_shadow_singlepassvolumegeneration.integer)
355 // one pass approach (identify lit/dark faces and generate sides while doing so)
356 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
358 // calculate triangle facing flag
359 v[0] = invertex3f + e[0] * 3;
360 v[1] = invertex3f + e[1] * 3;
361 v[2] = invertex3f + e[2] * 3;
362 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
364 // make sure the vertices are created
365 for (j = 0;j < 3;j++)
367 if (vertexupdate[e[j]] != vertexupdatenum)
369 vertexupdate[e[j]] = vertexupdatenum;
370 vertexremap[e[j]] = outvertices;
371 VectorCopy(v[j], outvertex3f);
372 VectorSubtract(v[j], relativelightorigin, temp);
373 f = projectdistance / VectorLength(temp);
374 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
379 // output the front and back triangles
380 vr[0] = vertexremap[e[0]];
381 vr[1] = vertexremap[e[1]];
382 vr[2] = vertexremap[e[2]];
383 outelement3i[0] = vr[0];
384 outelement3i[1] = vr[1];
385 outelement3i[2] = vr[2];
386 outelement3i[3] = vr[2] + 1;
387 outelement3i[4] = vr[1] + 1;
388 outelement3i[5] = vr[0] + 1;
391 // output the sides (facing outward from this triangle)
393 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]))))
395 outelement3i[0] = vr[1];
396 outelement3i[1] = vr[0];
397 outelement3i[2] = vr[0] + 1;
398 outelement3i[3] = vr[1];
399 outelement3i[4] = vr[0] + 1;
400 outelement3i[5] = vr[1] + 1;
405 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]))))
407 outelement3i[0] = vr[2];
408 outelement3i[1] = vr[1];
409 outelement3i[2] = vr[1] + 1;
410 outelement3i[3] = vr[2];
411 outelement3i[4] = vr[1] + 1;
412 outelement3i[5] = vr[2] + 1;
417 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]))))
419 outelement3i[0] = vr[0];
420 outelement3i[1] = vr[2];
421 outelement3i[2] = vr[2] + 1;
422 outelement3i[3] = vr[0];
423 outelement3i[4] = vr[2] + 1;
424 outelement3i[5] = vr[0] + 1;
431 // this triangle is not facing the light
432 // output the sides (facing inward to this triangle)
434 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
436 vr[0] = vertexremap[e[0]];
437 vr[1] = vertexremap[e[1]];
438 outelement3i[0] = vr[1];
439 outelement3i[1] = vr[0] + 1;
440 outelement3i[2] = vr[0];
441 outelement3i[3] = vr[1];
442 outelement3i[4] = vr[1] + 1;
443 outelement3i[5] = vr[0] + 1;
448 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
450 vr[1] = vertexremap[e[1]];
451 vr[2] = vertexremap[e[2]];
452 outelement3i[0] = vr[2];
453 outelement3i[1] = vr[1] + 1;
454 outelement3i[2] = vr[1];
455 outelement3i[3] = vr[2];
456 outelement3i[4] = vr[2] + 1;
457 outelement3i[5] = vr[1] + 1;
462 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
464 vr[0] = vertexremap[e[0]];
465 vr[2] = vertexremap[e[2]];
466 outelement3i[0] = vr[0];
467 outelement3i[1] = vr[2] + 1;
468 outelement3i[2] = vr[2];
469 outelement3i[3] = vr[0];
470 outelement3i[4] = vr[0] + 1;
471 outelement3i[5] = vr[2] + 1;
480 // two pass approach (identify lit/dark faces and then generate sides)
481 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
483 // calculate triangle facing flag
484 v[0] = invertex3f + e[0] * 3;
485 v[1] = invertex3f + e[1] * 3;
486 v[2] = invertex3f + e[2] * 3;
487 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
489 trianglefacinglightlist[numfacing++] = i;
490 // make sure the vertices are created
491 for (j = 0;j < 3;j++)
493 if (vertexupdate[e[j]] != vertexupdatenum)
495 vertexupdate[e[j]] = vertexupdatenum;
496 vertexremap[e[j]] = outvertices;
497 VectorSubtract(v[j], relativelightorigin, temp);
498 f = projectdistance / VectorLength(temp);
499 VectorCopy(v[j], outvertex3f);
500 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
505 // output the front and back triangles
506 outelement3i[0] = vertexremap[e[0]];
507 outelement3i[1] = vertexremap[e[1]];
508 outelement3i[2] = vertexremap[e[2]];
509 outelement3i[3] = vertexremap[e[2]] + 1;
510 outelement3i[4] = vertexremap[e[1]] + 1;
511 outelement3i[5] = vertexremap[e[0]] + 1;
516 for (i = 0;i < numfacing;i++)
518 t = trianglefacinglightlist[i];
519 e = inelement3i + t * 3;
520 n = inneighbor3i + t * 3;
521 // output the sides (facing outward from this triangle)
523 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]))))
525 vr[0] = vertexremap[e[0]];
526 vr[1] = vertexremap[e[1]];
527 outelement3i[0] = vr[1];
528 outelement3i[1] = vr[0];
529 outelement3i[2] = vr[0] + 1;
530 outelement3i[3] = vr[1];
531 outelement3i[4] = vr[0] + 1;
532 outelement3i[5] = vr[1] + 1;
537 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]))))
539 vr[1] = vertexremap[e[1]];
540 vr[2] = vertexremap[e[2]];
541 outelement3i[0] = vr[2];
542 outelement3i[1] = vr[1];
543 outelement3i[2] = vr[1] + 1;
544 outelement3i[3] = vr[2];
545 outelement3i[4] = vr[1] + 1;
546 outelement3i[5] = vr[2] + 1;
551 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]))))
553 vr[0] = vertexremap[e[0]];
554 vr[2] = vertexremap[e[2]];
555 outelement3i[0] = vr[0];
556 outelement3i[1] = vr[2];
557 outelement3i[2] = vr[2] + 1;
558 outelement3i[3] = vr[0];
559 outelement3i[4] = vr[2] + 1;
560 outelement3i[5] = vr[0] + 1;
567 *outnumvertices = outvertices;
571 float varray_vertex3f2[65536*3];
573 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
576 if (projectdistance < 0.1)
578 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
584 // make sure shadowelements is big enough for this volume
585 if (maxshadowelements < numtris * 24)
586 R_Shadow_ResizeShadowElements(numtris);
588 // check which triangles are facing the light, and then output
589 // triangle elements and vertices... by clever use of elements we
590 // can construct the whole shadow from the unprojected vertices and
591 // the projected vertices
592 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
594 GL_VertexPointer(varray_vertex3f2);
595 if (r_shadowstage == SHADOWSTAGE_STENCIL)
597 // increment stencil if backface is behind depthbuffer
598 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
599 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
600 R_Mesh_Draw(outverts, tris, shadowelements);
602 c_rt_shadowtris += numtris;
603 // decrement stencil if frontface is behind depthbuffer
604 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
605 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
607 R_Mesh_Draw(outverts, tris, shadowelements);
609 c_rt_shadowtris += numtris;
613 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
616 if (r_shadowstage == SHADOWSTAGE_STENCIL)
618 // increment stencil if backface is behind depthbuffer
619 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
620 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
621 for (mesh = firstmesh;mesh;mesh = mesh->next)
623 GL_VertexPointer(mesh->vertex3f);
624 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
625 c_rtcached_shadowmeshes++;
626 c_rtcached_shadowtris += mesh->numtriangles;
628 // decrement stencil if frontface is behind depthbuffer
629 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
630 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
632 for (mesh = firstmesh;mesh;mesh = mesh->next)
634 GL_VertexPointer(mesh->vertex3f);
635 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
636 c_rtcached_shadowmeshes++;
637 c_rtcached_shadowtris += mesh->numtriangles;
641 float r_shadow_attenpower, r_shadow_attenscale;
642 static void R_Shadow_MakeTextures(void)
644 int x, y, z, d, side;
645 float v[3], s, t, intensity;
647 R_FreeTexturePool(&r_shadow_texturepool);
648 r_shadow_texturepool = R_AllocTexturePool();
649 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
650 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
652 #define ATTEN2DSIZE 64
653 #define ATTEN3DSIZE 32
654 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
659 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
664 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
669 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
670 if (gl_texturecubemap)
672 for (side = 0;side < 6;side++)
674 for (y = 0;y < NORMSIZE;y++)
676 for (x = 0;x < NORMSIZE;x++)
678 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
679 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
713 intensity = 127.0f / sqrt(DotProduct(v, v));
714 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
715 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
716 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
717 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
721 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
724 r_shadow_normalcubetexture = NULL;
725 for (y = 0;y < ATTEN2DSIZE;y++)
727 for (x = 0;x < ATTEN2DSIZE;x++)
729 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
730 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
732 intensity = 1.0f - sqrt(DotProduct(v, v));
734 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
735 d = bound(0, intensity, 255);
736 data[(y*ATTEN2DSIZE+x)*4+0] = d;
737 data[(y*ATTEN2DSIZE+x)*4+1] = d;
738 data[(y*ATTEN2DSIZE+x)*4+2] = d;
739 data[(y*ATTEN2DSIZE+x)*4+3] = d;
742 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
743 if (r_shadow_texture3d.integer)
745 for (z = 0;z < ATTEN3DSIZE;z++)
747 for (y = 0;y < ATTEN3DSIZE;y++)
749 for (x = 0;x < ATTEN3DSIZE;x++)
751 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
752 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
753 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
754 intensity = 1.0f - sqrt(DotProduct(v, v));
756 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
757 d = bound(0, intensity, 255);
758 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
759 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
760 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
761 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
765 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
770 void R_Shadow_Stage_Begin(void)
774 if (r_shadow_texture3d.integer && !gl_texture3d)
775 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
777 if (!r_shadow_attenuation2dtexture
778 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
779 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
780 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
781 R_Shadow_MakeTextures();
783 memset(&m, 0, sizeof(m));
784 GL_BlendFunc(GL_ONE, GL_ZERO);
787 R_Mesh_State_Texture(&m);
788 GL_Color(0, 0, 0, 1);
789 qglDisable(GL_SCISSOR_TEST);
790 r_shadowstage = SHADOWSTAGE_NONE;
792 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
793 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
794 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
797 void R_Shadow_LoadWorldLightsIfNeeded(void)
799 if (r_shadow_reloadlights && cl.worldmodel)
801 R_Shadow_ClearWorldLights();
802 r_shadow_reloadlights = false;
803 R_Shadow_LoadWorldLights();
804 if (r_shadow_worldlightchain == NULL)
806 R_Shadow_LoadLightsFile();
807 if (r_shadow_worldlightchain == NULL)
808 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
813 void R_Shadow_Stage_ShadowVolumes(void)
816 memset(&m, 0, sizeof(m));
817 R_Mesh_State_Texture(&m);
818 GL_Color(1, 1, 1, 1);
819 qglColorMask(0, 0, 0, 0);
820 GL_BlendFunc(GL_ONE, GL_ZERO);
823 if (r_shadow_polygonoffset.value != 0)
825 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
826 qglEnable(GL_POLYGON_OFFSET_FILL);
829 qglDisable(GL_POLYGON_OFFSET_FILL);
830 qglDepthFunc(GL_LESS);
831 qglEnable(GL_STENCIL_TEST);
832 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
833 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
834 r_shadowstage = SHADOWSTAGE_STENCIL;
835 qglClear(GL_STENCIL_BUFFER_BIT);
837 // LordHavoc note: many shadow volumes reside entirely inside the world
838 // (that is to say they are entirely bounded by their lit surfaces),
839 // which can be optimized by handling things as an inverted light volume,
840 // with the shadow boundaries of the world being simulated by an altered
841 // (129) bias to stencil clearing on such lights
842 // FIXME: generate inverted light volumes for use as shadow volumes and
843 // optimize for them as noted above
846 void R_Shadow_Stage_LightWithoutShadows(void)
849 memset(&m, 0, sizeof(m));
850 R_Mesh_State_Texture(&m);
851 GL_BlendFunc(GL_ONE, GL_ONE);
854 qglDisable(GL_POLYGON_OFFSET_FILL);
855 GL_Color(1, 1, 1, 1);
856 qglColorMask(1, 1, 1, 1);
857 qglDepthFunc(GL_EQUAL);
858 qglDisable(GL_STENCIL_TEST);
859 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
860 qglStencilFunc(GL_EQUAL, 128, 0xFF);
861 r_shadowstage = SHADOWSTAGE_LIGHT;
865 void R_Shadow_Stage_LightWithShadows(void)
868 memset(&m, 0, sizeof(m));
869 R_Mesh_State_Texture(&m);
870 GL_BlendFunc(GL_ONE, GL_ONE);
873 qglDisable(GL_POLYGON_OFFSET_FILL);
874 GL_Color(1, 1, 1, 1);
875 qglColorMask(1, 1, 1, 1);
876 qglDepthFunc(GL_EQUAL);
877 qglEnable(GL_STENCIL_TEST);
878 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
879 // only draw light where this geometry was already rendered AND the
880 // stencil is 128 (values other than this mean shadow)
881 qglStencilFunc(GL_EQUAL, 128, 0xFF);
882 r_shadowstage = SHADOWSTAGE_LIGHT;
886 void R_Shadow_Stage_End(void)
889 memset(&m, 0, sizeof(m));
890 R_Mesh_State_Texture(&m);
891 GL_BlendFunc(GL_ONE, GL_ZERO);
894 qglDisable(GL_POLYGON_OFFSET_FILL);
895 GL_Color(1, 1, 1, 1);
896 qglColorMask(1, 1, 1, 1);
897 qglDisable(GL_SCISSOR_TEST);
898 qglDepthFunc(GL_LEQUAL);
899 qglDisable(GL_STENCIL_TEST);
900 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
901 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
902 r_shadowstage = SHADOWSTAGE_NONE;
905 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
907 int i, ix1, iy1, ix2, iy2;
908 float x1, y1, x2, y2, x, y, f;
911 if (!r_shadow_scissor.integer)
913 // if view is inside the box, just say yes it's visible
914 // LordHavoc: for some odd reason scissor seems broken without stencil
915 // (?!? seems like a driver bug) so abort if gl_stencil is false
916 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
918 qglDisable(GL_SCISSOR_TEST);
921 for (i = 0;i < 3;i++)
934 f = DotProduct(vpn, r_origin) + 1;
935 if (DotProduct(vpn, v2) <= f)
937 // entirely behind nearclip plane
940 if (DotProduct(vpn, v) >= f)
942 // entirely infront of nearclip plane
943 x1 = y1 = x2 = y2 = 0;
944 for (i = 0;i < 8;i++)
946 v[0] = (i & 1) ? mins[0] : maxs[0];
947 v[1] = (i & 2) ? mins[1] : maxs[1];
948 v[2] = (i & 4) ? mins[2] : maxs[2];
950 GL_TransformToScreen(v, v2);
951 //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]);
970 // clipped by nearclip plane
971 // this is nasty and crude...
972 // create viewspace bbox
973 for (i = 0;i < 8;i++)
975 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
976 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
977 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
978 v2[0] = DotProduct(v, vright);
979 v2[1] = DotProduct(v, vup);
980 v2[2] = DotProduct(v, vpn);
983 if (smins[0] > v2[0]) smins[0] = v2[0];
984 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
985 if (smins[1] > v2[1]) smins[1] = v2[1];
986 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
987 if (smins[2] > v2[2]) smins[2] = v2[2];
988 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
992 smins[0] = smaxs[0] = v2[0];
993 smins[1] = smaxs[1] = v2[1];
994 smins[2] = smaxs[2] = v2[2];
997 // now we have a bbox in viewspace
998 // clip it to the view plane
1001 // return true if that culled the box
1002 if (smins[2] >= smaxs[2])
1004 // ok some of it is infront of the view, transform each corner back to
1005 // worldspace and then to screenspace and make screen rect
1006 // initialize these variables just to avoid compiler warnings
1007 x1 = y1 = x2 = y2 = 0;
1008 for (i = 0;i < 8;i++)
1010 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1011 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1012 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1013 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1014 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1015 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1017 GL_TransformToScreen(v, v2);
1018 //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]);
1035 // this code doesn't handle boxes with any points behind view properly
1036 x1 = 1000;x2 = -1000;
1037 y1 = 1000;y2 = -1000;
1038 for (i = 0;i < 8;i++)
1040 v[0] = (i & 1) ? mins[0] : maxs[0];
1041 v[1] = (i & 2) ? mins[1] : maxs[1];
1042 v[2] = (i & 4) ? mins[2] : maxs[2];
1044 GL_TransformToScreen(v, v2);
1045 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1063 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1064 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1065 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1066 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1067 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1068 if (ix2 <= ix1 || iy2 <= iy1)
1070 // set up the scissor rectangle
1071 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1072 qglEnable(GL_SCISSOR_TEST);
1077 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1079 float *color4f = varray_color4f;
1080 float dist, dot, intensity, v[3], n[3];
1081 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1083 Matrix4x4_Transform(m, vertex3f, v);
1084 if ((dist = DotProduct(v, v)) < 1)
1086 Matrix4x4_Transform3x3(m, normal3f, n);
1087 if ((dot = DotProduct(n, v)) > 0)
1090 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1091 VectorScale(lightcolor, intensity, color4f);
1096 VectorClear(color4f);
1102 VectorClear(color4f);
1108 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1110 float *color4f = varray_color4f;
1111 float dist, dot, intensity, v[3], n[3];
1112 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1114 Matrix4x4_Transform(m, vertex3f, v);
1115 if ((dist = fabs(v[2])) < 1)
1117 Matrix4x4_Transform3x3(m, normal3f, n);
1118 if ((dot = DotProduct(n, v)) > 0)
1120 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1121 VectorScale(lightcolor, intensity, color4f);
1126 VectorClear(color4f);
1132 VectorClear(color4f);
1138 // FIXME: this should be done in a vertex program when possible
1139 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1140 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1144 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1145 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1146 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1153 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1157 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1158 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1165 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)
1169 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1171 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1172 // the cubemap normalizes this for us
1173 out3f[0] = DotProduct(svector3f, lightdir);
1174 out3f[1] = DotProduct(tvector3f, lightdir);
1175 out3f[2] = DotProduct(normal3f, lightdir);
1179 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)
1182 float lightdir[3], eyedir[3], halfdir[3];
1183 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1185 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1186 VectorNormalizeFast(lightdir);
1187 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1188 VectorNormalizeFast(eyedir);
1189 VectorAdd(lightdir, eyedir, halfdir);
1190 // the cubemap normalizes this for us
1191 out3f[0] = DotProduct(svector3f, halfdir);
1192 out3f[1] = DotProduct(tvector3f, halfdir);
1193 out3f[2] = DotProduct(normal3f, halfdir);
1197 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)
1200 float color[3], color2[3];
1202 GL_VertexPointer(vertex3f);
1203 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1206 bumptexture = r_shadow_blankbumptexture;
1208 // colorscale accounts for how much we multiply the brightness during combine
1209 // mult is how many times the final pass of the lighting will be
1210 // performed to get more brightness than otherwise possible
1211 // limit mult to 64 for sanity sake
1212 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1214 // 3/2 3D combine path (Geforce3, Radeon 8500)
1215 memset(&m, 0, sizeof(m));
1216 m.tex[0] = R_GetTexture(bumptexture);
1217 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1218 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1219 m.texcombinergb[0] = GL_REPLACE;
1220 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1221 m.pointer_texcoord[0] = texcoord2f;
1222 m.pointer_texcoord[1] = varray_texcoord3f[1];
1223 m.pointer_texcoord[2] = varray_texcoord3f[2];
1224 R_Mesh_State_Texture(&m);
1225 qglColorMask(0,0,0,1);
1226 GL_BlendFunc(GL_ONE, GL_ZERO);
1227 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1228 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1229 R_Mesh_Draw(numverts, numtriangles, elements);
1231 c_rt_lighttris += numtriangles;
1233 memset(&m, 0, sizeof(m));
1234 m.tex[0] = R_GetTexture(basetexture);
1235 m.texcubemap[1] = R_GetTexture(lightcubemap);
1236 m.pointer_texcoord[0] = texcoord2f;
1237 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1238 R_Mesh_State_Texture(&m);
1239 qglColorMask(1,1,1,0);
1240 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1242 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1243 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1244 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1246 color[0] = bound(0, color2[0], 1);
1247 color[1] = bound(0, color2[1], 1);
1248 color[2] = bound(0, color2[2], 1);
1249 GL_Color(color[0], color[1], color[2], 1);
1250 R_Mesh_Draw(numverts, numtriangles, elements);
1252 c_rt_lighttris += numtriangles;
1255 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1257 // 1/2/2 3D combine path (original Radeon)
1258 memset(&m, 0, sizeof(m));
1259 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1260 m.pointer_texcoord[0] = varray_texcoord3f[0];
1261 R_Mesh_State_Texture(&m);
1262 qglColorMask(0,0,0,1);
1263 GL_BlendFunc(GL_ONE, GL_ZERO);
1264 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1265 R_Mesh_Draw(numverts, numtriangles, elements);
1267 c_rt_lighttris += numtriangles;
1269 memset(&m, 0, sizeof(m));
1270 m.tex[0] = R_GetTexture(bumptexture);
1271 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1272 m.texcombinergb[0] = GL_REPLACE;
1273 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1274 m.pointer_texcoord[0] = texcoord2f;
1275 m.pointer_texcoord[1] = varray_texcoord3f[1];
1276 R_Mesh_State_Texture(&m);
1277 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1278 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1279 R_Mesh_Draw(numverts, numtriangles, elements);
1281 c_rt_lighttris += numtriangles;
1283 memset(&m, 0, sizeof(m));
1284 m.tex[0] = R_GetTexture(basetexture);
1285 m.texcubemap[1] = R_GetTexture(lightcubemap);
1286 m.pointer_texcoord[0] = texcoord2f;
1287 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1288 R_Mesh_State_Texture(&m);
1289 qglColorMask(1,1,1,0);
1290 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1292 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1293 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1294 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1296 color[0] = bound(0, color2[0], 1);
1297 color[1] = bound(0, color2[1], 1);
1298 color[2] = bound(0, color2[2], 1);
1299 GL_Color(color[0], color[1], color[2], 1);
1300 R_Mesh_Draw(numverts, numtriangles, elements);
1302 c_rt_lighttris += numtriangles;
1305 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1307 // 2/2 3D combine path (original Radeon)
1308 memset(&m, 0, sizeof(m));
1309 m.tex[0] = R_GetTexture(bumptexture);
1310 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1311 m.texcombinergb[0] = GL_REPLACE;
1312 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1313 m.pointer_texcoord[0] = texcoord2f;
1314 m.pointer_texcoord[1] = varray_texcoord3f[1];
1315 R_Mesh_State_Texture(&m);
1316 qglColorMask(0,0,0,1);
1317 GL_BlendFunc(GL_ONE, GL_ZERO);
1318 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1319 R_Mesh_Draw(numverts, numtriangles, elements);
1321 c_rt_lighttris += numtriangles;
1323 memset(&m, 0, sizeof(m));
1324 m.tex[0] = R_GetTexture(basetexture);
1325 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1326 m.pointer_texcoord[0] = texcoord2f;
1327 m.pointer_texcoord[1] = varray_texcoord3f[1];
1328 R_Mesh_State_Texture(&m);
1329 qglColorMask(1,1,1,0);
1330 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1331 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1332 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1333 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1335 color[0] = bound(0, color2[0], 1);
1336 color[1] = bound(0, color2[1], 1);
1337 color[2] = bound(0, color2[2], 1);
1338 GL_Color(color[0], color[1], color[2], 1);
1339 R_Mesh_Draw(numverts, numtriangles, elements);
1341 c_rt_lighttris += numtriangles;
1344 else if (r_textureunits.integer >= 4)
1346 // 4/2 2D combine path (Geforce3, Radeon 8500)
1347 memset(&m, 0, sizeof(m));
1348 m.tex[0] = R_GetTexture(bumptexture);
1349 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1350 m.texcombinergb[0] = GL_REPLACE;
1351 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1352 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1353 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1354 m.pointer_texcoord[0] = texcoord2f;
1355 m.pointer_texcoord[1] = varray_texcoord3f[1];
1356 m.pointer_texcoord[2] = varray_texcoord2f[2];
1357 m.pointer_texcoord[3] = varray_texcoord2f[3];
1358 R_Mesh_State_Texture(&m);
1359 qglColorMask(0,0,0,1);
1360 GL_BlendFunc(GL_ONE, GL_ZERO);
1361 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1362 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1363 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1364 R_Mesh_Draw(numverts, numtriangles, elements);
1366 c_rt_lighttris += numtriangles;
1368 memset(&m, 0, sizeof(m));
1369 m.tex[0] = R_GetTexture(basetexture);
1370 m.texcubemap[1] = R_GetTexture(lightcubemap);
1371 m.pointer_texcoord[0] = texcoord2f;
1372 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1373 R_Mesh_State_Texture(&m);
1374 qglColorMask(1,1,1,0);
1375 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1377 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1378 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1379 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1381 color[0] = bound(0, color2[0], 1);
1382 color[1] = bound(0, color2[1], 1);
1383 color[2] = bound(0, color2[2], 1);
1384 GL_Color(color[0], color[1], color[2], 1);
1385 R_Mesh_Draw(numverts, numtriangles, elements);
1387 c_rt_lighttris += numtriangles;
1392 // 2/2/2 2D combine path (any dot3 card)
1393 memset(&m, 0, sizeof(m));
1394 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1395 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1396 m.pointer_texcoord[0] = varray_texcoord2f[0];
1397 m.pointer_texcoord[1] = varray_texcoord2f[1];
1398 R_Mesh_State_Texture(&m);
1399 qglColorMask(0,0,0,1);
1400 GL_BlendFunc(GL_ONE, GL_ZERO);
1401 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1402 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1403 R_Mesh_Draw(numverts, numtriangles, elements);
1405 c_rt_lighttris += numtriangles;
1407 memset(&m, 0, sizeof(m));
1408 m.tex[0] = R_GetTexture(bumptexture);
1409 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1410 m.texcombinergb[0] = GL_REPLACE;
1411 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1412 m.pointer_texcoord[0] = texcoord2f;
1413 m.pointer_texcoord[1] = varray_texcoord3f[1];
1414 R_Mesh_State_Texture(&m);
1415 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1416 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1417 R_Mesh_Draw(numverts, numtriangles, elements);
1419 c_rt_lighttris += numtriangles;
1421 memset(&m, 0, sizeof(m));
1422 m.tex[0] = R_GetTexture(basetexture);
1423 m.texcubemap[1] = R_GetTexture(lightcubemap);
1424 m.pointer_texcoord[0] = texcoord2f;
1425 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1426 R_Mesh_State_Texture(&m);
1427 qglColorMask(1,1,1,0);
1428 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1430 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1431 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1432 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1434 color[0] = bound(0, color2[0], 1);
1435 color[1] = bound(0, color2[1], 1);
1436 color[2] = bound(0, color2[2], 1);
1437 GL_Color(color[0], color[1], color[2], 1);
1438 R_Mesh_Draw(numverts, numtriangles, elements);
1440 c_rt_lighttris += numtriangles;
1446 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1447 GL_DepthMask(false);
1449 GL_ColorPointer(varray_color4f);
1450 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1451 memset(&m, 0, sizeof(m));
1452 m.tex[0] = R_GetTexture(basetexture);
1453 m.pointer_texcoord[0] = texcoord2f;
1454 if (r_textureunits.integer >= 2)
1457 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1458 m.pointer_texcoord[1] = varray_texcoord2f[1];
1459 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1461 R_Mesh_State_Texture(&m);
1462 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1464 color[0] = bound(0, color2[0], 1);
1465 color[1] = bound(0, color2[1], 1);
1466 color[2] = bound(0, color2[2], 1);
1467 if (r_textureunits.integer >= 2)
1468 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1470 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1471 R_Mesh_Draw(numverts, numtriangles, elements);
1473 c_rt_lighttris += numtriangles;
1478 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)
1481 float color[3], color2[3], colorscale;
1483 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1486 glosstexture = r_shadow_blankglosstexture;
1487 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1489 colorscale = r_shadow_glossintensity.value;
1491 bumptexture = r_shadow_blankbumptexture;
1492 if (glosstexture == r_shadow_blankglosstexture)
1493 colorscale *= r_shadow_gloss2intensity.value;
1494 GL_VertexPointer(vertex3f);
1496 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1498 // 2/0/0/1/2 3D combine blendsquare path
1499 memset(&m, 0, sizeof(m));
1500 m.tex[0] = R_GetTexture(bumptexture);
1501 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1502 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1503 m.pointer_texcoord[0] = texcoord2f;
1504 m.pointer_texcoord[1] = varray_texcoord3f[1];
1505 R_Mesh_State_Texture(&m);
1506 qglColorMask(0,0,0,1);
1507 // this squares the result
1508 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1509 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1510 R_Mesh_Draw(numverts, numtriangles, elements);
1512 c_rt_lighttris += numtriangles;
1514 memset(&m, 0, sizeof(m));
1515 R_Mesh_State_Texture(&m);
1516 // square alpha in framebuffer a few times to make it shiny
1517 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1518 // these comments are a test run through this math for intensity 0.5
1519 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1520 // 0.25 * 0.25 = 0.0625 (this is another pass)
1521 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1522 R_Mesh_Draw(numverts, numtriangles, elements);
1524 c_rt_lighttris += numtriangles;
1525 R_Mesh_Draw(numverts, numtriangles, elements);
1527 c_rt_lighttris += numtriangles;
1529 memset(&m, 0, sizeof(m));
1530 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1531 m.pointer_texcoord[0] = varray_texcoord3f[0];
1532 R_Mesh_State_Texture(&m);
1533 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1534 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1535 R_Mesh_Draw(numverts, numtriangles, elements);
1537 c_rt_lighttris += numtriangles;
1539 memset(&m, 0, sizeof(m));
1540 m.tex[0] = R_GetTexture(glosstexture);
1541 m.texcubemap[1] = R_GetTexture(lightcubemap);
1542 m.pointer_texcoord[0] = texcoord2f;
1543 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1544 R_Mesh_State_Texture(&m);
1545 qglColorMask(1,1,1,0);
1546 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1548 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1549 VectorScale(lightcolor, colorscale, color2);
1550 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1552 color[0] = bound(0, color2[0], 1);
1553 color[1] = bound(0, color2[1], 1);
1554 color[2] = bound(0, color2[2], 1);
1555 GL_Color(color[0], color[1], color[2], 1);
1556 R_Mesh_Draw(numverts, numtriangles, elements);
1558 c_rt_lighttris += numtriangles;
1561 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1563 // 2/0/0/2 3D combine blendsquare path
1564 memset(&m, 0, sizeof(m));
1565 m.tex[0] = R_GetTexture(bumptexture);
1566 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1567 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1568 m.pointer_texcoord[0] = texcoord2f;
1569 m.pointer_texcoord[1] = varray_texcoord3f[1];
1570 R_Mesh_State_Texture(&m);
1571 qglColorMask(0,0,0,1);
1572 // this squares the result
1573 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1574 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1575 R_Mesh_Draw(numverts, numtriangles, elements);
1577 c_rt_lighttris += numtriangles;
1579 memset(&m, 0, sizeof(m));
1580 R_Mesh_State_Texture(&m);
1581 // square alpha in framebuffer a few times to make it shiny
1582 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1583 // these comments are a test run through this math for intensity 0.5
1584 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1585 // 0.25 * 0.25 = 0.0625 (this is another pass)
1586 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1587 R_Mesh_Draw(numverts, numtriangles, elements);
1589 c_rt_lighttris += numtriangles;
1590 R_Mesh_Draw(numverts, numtriangles, elements);
1592 c_rt_lighttris += numtriangles;
1594 memset(&m, 0, sizeof(m));
1595 m.tex[0] = R_GetTexture(glosstexture);
1596 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1597 m.pointer_texcoord[0] = texcoord2f;
1598 m.pointer_texcoord[1] = varray_texcoord3f[1];
1599 R_Mesh_State_Texture(&m);
1600 qglColorMask(1,1,1,0);
1601 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1602 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1603 VectorScale(lightcolor, colorscale, color2);
1604 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1606 color[0] = bound(0, color2[0], 1);
1607 color[1] = bound(0, color2[1], 1);
1608 color[2] = bound(0, color2[2], 1);
1609 GL_Color(color[0], color[1], color[2], 1);
1610 R_Mesh_Draw(numverts, numtriangles, elements);
1612 c_rt_lighttris += numtriangles;
1615 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1617 // 2/0/0/2/2 2D combine blendsquare path
1618 memset(&m, 0, sizeof(m));
1619 m.tex[0] = R_GetTexture(bumptexture);
1620 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1621 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1622 m.pointer_texcoord[0] = texcoord2f;
1623 m.pointer_texcoord[1] = varray_texcoord3f[1];
1624 R_Mesh_State_Texture(&m);
1625 qglColorMask(0,0,0,1);
1626 // this squares the result
1627 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1628 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1629 R_Mesh_Draw(numverts, numtriangles, elements);
1631 c_rt_lighttris += numtriangles;
1633 memset(&m, 0, sizeof(m));
1634 R_Mesh_State_Texture(&m);
1635 // square alpha in framebuffer a few times to make it shiny
1636 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1637 // these comments are a test run through this math for intensity 0.5
1638 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1639 // 0.25 * 0.25 = 0.0625 (this is another pass)
1640 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1641 R_Mesh_Draw(numverts, numtriangles, elements);
1643 c_rt_lighttris += numtriangles;
1644 R_Mesh_Draw(numverts, numtriangles, elements);
1646 c_rt_lighttris += numtriangles;
1648 memset(&m, 0, sizeof(m));
1649 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1650 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1651 m.pointer_texcoord[0] = varray_texcoord2f[0];
1652 m.pointer_texcoord[1] = varray_texcoord2f[1];
1653 R_Mesh_State_Texture(&m);
1654 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1655 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1656 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1657 R_Mesh_Draw(numverts, numtriangles, elements);
1659 c_rt_lighttris += numtriangles;
1661 memset(&m, 0, sizeof(m));
1662 m.tex[0] = R_GetTexture(glosstexture);
1663 m.texcubemap[1] = R_GetTexture(lightcubemap);
1664 m.pointer_texcoord[0] = texcoord2f;
1665 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1666 R_Mesh_State_Texture(&m);
1667 qglColorMask(1,1,1,0);
1668 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1670 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1671 VectorScale(lightcolor, colorscale, color2);
1672 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1674 color[0] = bound(0, color2[0], 1);
1675 color[1] = bound(0, color2[1], 1);
1676 color[2] = bound(0, color2[2], 1);
1677 GL_Color(color[0], color[1], color[2], 1);
1678 R_Mesh_Draw(numverts, numtriangles, elements);
1680 c_rt_lighttris += numtriangles;
1686 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1688 R_Mesh_Matrix(matrix);
1689 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1692 cvar_t r_editlights = {0, "r_editlights", "0"};
1693 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1694 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1695 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1696 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1697 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1698 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1699 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1700 worldlight_t *r_shadow_worldlightchain;
1701 worldlight_t *r_shadow_selectedlight;
1702 vec3_t r_editlights_cursorlocation;
1704 static int lightpvsbytes;
1705 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1707 static int castshadowcount = 1;
1708 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1710 int i, j, k, l, maxverts = 256, *mark, tris, numsurfaces;
1711 float *vertex3f = NULL, mins[3], maxs[3];
1713 shadowmesh_t *mesh, *castmesh;
1717 if (radius < 15 || DotProduct(color, color) < 0.03)
1719 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1723 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1724 VectorCopy(origin, e->origin);
1725 VectorCopy(color, e->light);
1726 e->lightradius = radius;
1728 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1730 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1733 e->castshadows = castshadow;
1735 e->cullradius = e->lightradius;
1736 for (k = 0;k < 3;k++)
1738 mins[k] = e->origin[k] - e->lightradius;
1739 maxs[k] = e->origin[k] + e->lightradius;
1742 e->next = r_shadow_worldlightchain;
1743 r_shadow_worldlightchain = e;
1744 if (cubemapname && cubemapname[0])
1746 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1747 strcpy(e->cubemapname, cubemapname);
1748 // FIXME: add cubemap loading (and don't load a cubemap twice)
1753 VectorCopy(e->origin, e->mins);
1754 VectorCopy(e->origin, e->maxs);
1755 i = CL_PointQ1Contents(e->origin);
1756 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1758 //qbyte *byteleafpvs;
1759 qbyte *bytesurfacepvs;
1761 //byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1762 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1764 Portal_Visibility(cl.worldmodel, e->origin, NULL/*byteleafpvs*/, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1767 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1769 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1771 for (k = 0;k < 3;k++)
1773 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1774 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1780 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1781 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1782 surf->castshadow = castshadowcount;
1784 //Mem_Free(byteleafpvs);
1785 Mem_Free(bytesurfacepvs);
1789 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1790 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1792 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1794 for (k = 0;k < 3;k++)
1796 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1797 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1799 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1801 surf = cl.worldmodel->brushq1.surfaces + *mark;
1802 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1803 surf->castshadow = castshadowcount;
1809 for (k = 0;k < 3;k++)
1811 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1812 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1814 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1817 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1818 if (surf->castshadow == castshadowcount)
1821 e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
1823 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1824 if (surf->castshadow == castshadowcount)
1825 e->surfaces[e->numsurfaces++] = surf;
1830 for (j = 0;j < e->numsurfaces;j++)
1832 surf = e->surfaces[j];
1833 if (surf->flags & SURF_SHADOWCAST)
1835 surf->castshadow = castshadowcount;
1836 if (maxverts < surf->poly_numverts)
1837 maxverts = surf->poly_numverts;
1840 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1841 // make a mesh to cast a shadow volume from
1842 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1843 for (j = 0;j < e->numsurfaces;j++)
1844 if ((surf = e->surfaces[j])->castshadow == castshadowcount)
1845 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surf->mesh.data_vertex3f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1846 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1848 // cast shadow volume from castmesh
1849 for (mesh = castmesh;mesh;mesh = mesh->next)
1851 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1853 if (maxverts < castmesh->numverts * 2)
1855 maxverts = castmesh->numverts * 2;
1860 if (vertex3f == NULL && maxverts > 0)
1861 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1863 // now that we have the buffers big enough, construct and add
1864 // the shadow volume mesh
1865 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)))
1866 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1871 // we're done with castmesh now
1872 Mod_ShadowMesh_Free(castmesh);
1873 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1874 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1875 l += mesh->numtriangles;
1876 Con_Printf("static shadow volume built containing %i triangles\n", l);
1879 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);
1882 void R_Shadow_FreeWorldLight(worldlight_t *light)
1884 worldlight_t **lightpointer;
1885 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1886 if (*lightpointer != light)
1887 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1888 *lightpointer = light->next;
1889 if (light->cubemapname)
1890 Mem_Free(light->cubemapname);
1891 if (light->shadowvolume)
1892 Mod_ShadowMesh_Free(light->shadowvolume);
1893 if (light->surfaces)
1894 Mem_Free(light->surfaces);
1898 void R_Shadow_ClearWorldLights(void)
1900 while (r_shadow_worldlightchain)
1901 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1902 r_shadow_selectedlight = NULL;
1905 void R_Shadow_SelectLight(worldlight_t *light)
1907 if (r_shadow_selectedlight)
1908 r_shadow_selectedlight->selected = false;
1909 r_shadow_selectedlight = light;
1910 if (r_shadow_selectedlight)
1911 r_shadow_selectedlight->selected = true;
1914 rtexture_t *lighttextures[5];
1916 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1918 float scale = r_editlights_cursorgrid.value * 0.5f;
1919 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
1922 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1925 const worldlight_t *light;
1928 if (light->selected)
1929 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1930 if (!light->shadowvolume)
1932 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
1935 void R_Shadow_DrawLightSprites(void)
1939 worldlight_t *light;
1941 for (i = 0;i < 5;i++)
1943 lighttextures[i] = NULL;
1944 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
1945 lighttextures[i] = pic->tex;
1948 for (light = r_shadow_worldlightchain;light;light = light->next)
1949 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
1950 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1953 void R_Shadow_SelectLightInView(void)
1955 float bestrating, rating, temp[3];
1956 worldlight_t *best, *light;
1959 for (light = r_shadow_worldlightchain;light;light = light->next)
1961 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1962 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1965 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1966 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
1968 bestrating = rating;
1973 R_Shadow_SelectLight(best);
1976 void R_Shadow_LoadWorldLights(void)
1978 int n, a, style, shadow;
1979 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1980 float origin[3], radius, color[3];
1981 if (cl.worldmodel == NULL)
1983 Con_Printf("No map loaded.\n");
1986 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
1987 strlcat (name, ".rtlights", sizeof (name));
1988 lightsstring = FS_LoadFile(name, false);
1996 while (*s && *s != '\n')
2002 // check for modifier flags
2008 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);
2014 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);
2017 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2018 radius *= r_editlights_rtlightssizescale.value;
2019 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2024 Con_Printf("invalid rtlights file \"%s\"\n", name);
2025 Mem_Free(lightsstring);
2029 void R_Shadow_SaveWorldLights(void)
2031 worldlight_t *light;
2032 int bufchars, bufmaxchars;
2034 char name[MAX_QPATH];
2036 if (!r_shadow_worldlightchain)
2038 if (cl.worldmodel == NULL)
2040 Con_Printf("No map loaded.\n");
2043 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2044 strlcat (name, ".rtlights", sizeof (name));
2045 bufchars = bufmaxchars = 0;
2047 for (light = r_shadow_worldlightchain;light;light = light->next)
2049 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 : "");
2050 if (bufchars + (int) strlen(line) > bufmaxchars)
2052 bufmaxchars = bufchars + strlen(line) + 2048;
2054 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2058 memcpy(buf, oldbuf, bufchars);
2064 memcpy(buf + bufchars, line, strlen(line));
2065 bufchars += strlen(line);
2069 FS_WriteFile(name, buf, bufchars);
2074 void R_Shadow_LoadLightsFile(void)
2077 char name[MAX_QPATH], *lightsstring, *s, *t;
2078 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2079 if (cl.worldmodel == NULL)
2081 Con_Printf("No map loaded.\n");
2084 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2085 strlcat (name, ".lights", sizeof (name));
2086 lightsstring = FS_LoadFile(name, false);
2094 while (*s && *s != '\n')
2099 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);
2103 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);
2106 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2107 radius = bound(15, radius, 4096);
2108 VectorScale(color, (2.0f / (8388608.0f)), color);
2109 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2114 Con_Printf("invalid lights file \"%s\"\n", name);
2115 Mem_Free(lightsstring);
2119 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2121 int entnum, style, islight;
2122 char key[256], value[1024];
2123 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2126 if (cl.worldmodel == NULL)
2128 Con_Printf("No map loaded.\n");
2131 data = cl.worldmodel->brush.entities;
2134 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2137 origin[0] = origin[1] = origin[2] = 0;
2138 originhack[0] = originhack[1] = originhack[2] = 0;
2139 color[0] = color[1] = color[2] = 1;
2140 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2146 if (!COM_ParseToken(&data, false))
2148 if (com_token[0] == '}')
2149 break; // end of entity
2150 if (com_token[0] == '_')
2151 strcpy(key, com_token + 1);
2153 strcpy(key, com_token);
2154 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2155 key[strlen(key)-1] = 0;
2156 if (!COM_ParseToken(&data, false))
2158 strcpy(value, com_token);
2160 // now that we have the key pair worked out...
2161 if (!strcmp("light", key))
2162 light = atof(value);
2163 else if (!strcmp("origin", key))
2164 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2165 else if (!strcmp("color", key))
2166 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2167 else if (!strcmp("wait", key))
2168 scale = atof(value);
2169 else if (!strcmp("classname", key))
2171 if (!strncmp(value, "light", 5))
2174 if (!strcmp(value, "light_fluoro"))
2179 overridecolor[0] = 1;
2180 overridecolor[1] = 1;
2181 overridecolor[2] = 1;
2183 if (!strcmp(value, "light_fluorospark"))
2188 overridecolor[0] = 1;
2189 overridecolor[1] = 1;
2190 overridecolor[2] = 1;
2192 if (!strcmp(value, "light_globe"))
2197 overridecolor[0] = 1;
2198 overridecolor[1] = 0.8;
2199 overridecolor[2] = 0.4;
2201 if (!strcmp(value, "light_flame_large_yellow"))
2206 overridecolor[0] = 1;
2207 overridecolor[1] = 0.5;
2208 overridecolor[2] = 0.1;
2210 if (!strcmp(value, "light_flame_small_yellow"))
2215 overridecolor[0] = 1;
2216 overridecolor[1] = 0.5;
2217 overridecolor[2] = 0.1;
2219 if (!strcmp(value, "light_torch_small_white"))
2224 overridecolor[0] = 1;
2225 overridecolor[1] = 0.5;
2226 overridecolor[2] = 0.1;
2228 if (!strcmp(value, "light_torch_small_walltorch"))
2233 overridecolor[0] = 1;
2234 overridecolor[1] = 0.5;
2235 overridecolor[2] = 0.1;
2239 else if (!strcmp("style", key))
2240 style = atoi(value);
2242 if (light <= 0 && islight)
2244 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2245 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2246 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2247 VectorCopy(overridecolor, color);
2248 VectorScale(color, light, color);
2249 VectorAdd(origin, originhack, origin);
2251 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2256 void R_Shadow_SetCursorLocationForView(void)
2258 vec_t dist, push, frac;
2259 vec3_t dest, endpos, normal;
2260 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2261 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2264 dist = frac * r_editlights_cursordistance.value;
2265 push = r_editlights_cursorpushback.value;
2269 VectorMA(endpos, push, vpn, endpos);
2270 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2272 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2273 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2274 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2277 void R_Shadow_UpdateWorldLightSelection(void)
2279 if (r_editlights.integer)
2281 R_Shadow_SetCursorLocationForView();
2282 R_Shadow_SelectLightInView();
2283 R_Shadow_DrawLightSprites();
2286 R_Shadow_SelectLight(NULL);
2289 void R_Shadow_EditLights_Clear_f(void)
2291 R_Shadow_ClearWorldLights();
2294 void R_Shadow_EditLights_Reload_f(void)
2296 r_shadow_reloadlights = true;
2299 void R_Shadow_EditLights_Save_f(void)
2302 R_Shadow_SaveWorldLights();
2305 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2307 R_Shadow_ClearWorldLights();
2308 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2311 void R_Shadow_EditLights_ImportLightsFile_f(void)
2313 R_Shadow_ClearWorldLights();
2314 R_Shadow_LoadLightsFile();
2317 void R_Shadow_EditLights_Spawn_f(void)
2320 if (!r_editlights.integer)
2322 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2325 if (Cmd_Argc() != 1)
2327 Con_Printf("r_editlights_spawn does not take parameters\n");
2330 color[0] = color[1] = color[2] = 1;
2331 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2334 void R_Shadow_EditLights_Edit_f(void)
2336 vec3_t origin, color;
2339 char cubemapname[1024];
2340 if (!r_editlights.integer)
2342 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2345 if (!r_shadow_selectedlight)
2347 Con_Printf("No selected light.\n");
2350 VectorCopy(r_shadow_selectedlight->origin, origin);
2351 radius = r_shadow_selectedlight->lightradius;
2352 VectorCopy(r_shadow_selectedlight->light, color);
2353 style = r_shadow_selectedlight->style;
2354 if (r_shadow_selectedlight->cubemapname)
2355 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2358 shadows = r_shadow_selectedlight->castshadows;
2359 if (!strcmp(Cmd_Argv(1), "origin"))
2361 if (Cmd_Argc() != 5)
2363 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2366 origin[0] = atof(Cmd_Argv(2));
2367 origin[1] = atof(Cmd_Argv(3));
2368 origin[2] = atof(Cmd_Argv(4));
2370 else if (!strcmp(Cmd_Argv(1), "originx"))
2372 if (Cmd_Argc() != 3)
2374 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2377 origin[0] = atof(Cmd_Argv(2));
2379 else if (!strcmp(Cmd_Argv(1), "originy"))
2381 if (Cmd_Argc() != 3)
2383 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2386 origin[1] = atof(Cmd_Argv(2));
2388 else if (!strcmp(Cmd_Argv(1), "originz"))
2390 if (Cmd_Argc() != 3)
2392 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2395 origin[2] = atof(Cmd_Argv(2));
2397 else if (!strcmp(Cmd_Argv(1), "move"))
2399 if (Cmd_Argc() != 5)
2401 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2404 origin[0] += atof(Cmd_Argv(2));
2405 origin[1] += atof(Cmd_Argv(3));
2406 origin[2] += atof(Cmd_Argv(4));
2408 else if (!strcmp(Cmd_Argv(1), "movex"))
2410 if (Cmd_Argc() != 3)
2412 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2415 origin[0] += atof(Cmd_Argv(2));
2417 else if (!strcmp(Cmd_Argv(1), "movey"))
2419 if (Cmd_Argc() != 3)
2421 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2424 origin[1] += atof(Cmd_Argv(2));
2426 else if (!strcmp(Cmd_Argv(1), "movez"))
2428 if (Cmd_Argc() != 3)
2430 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2433 origin[2] += atof(Cmd_Argv(2));
2435 else if (!strcmp(Cmd_Argv(1), "color"))
2437 if (Cmd_Argc() != 5)
2439 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2442 color[0] = atof(Cmd_Argv(2));
2443 color[1] = atof(Cmd_Argv(3));
2444 color[2] = atof(Cmd_Argv(4));
2446 else if (!strcmp(Cmd_Argv(1), "radius"))
2448 if (Cmd_Argc() != 3)
2450 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2453 radius = atof(Cmd_Argv(2));
2455 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2457 if (Cmd_Argc() != 3)
2459 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2462 style = atoi(Cmd_Argv(2));
2464 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2468 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2471 if (Cmd_Argc() == 3)
2472 strcpy(cubemapname, Cmd_Argv(2));
2476 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2478 if (Cmd_Argc() != 3)
2480 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2483 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2487 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2488 Con_Printf("Selected light's properties:\n");
2489 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2490 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2491 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2492 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2493 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2494 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2497 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2498 r_shadow_selectedlight = NULL;
2499 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2502 extern int con_vislines;
2503 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2507 if (r_shadow_selectedlight == NULL)
2511 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2512 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;
2513 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2514 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;
2515 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2516 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2517 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;
2520 void R_Shadow_EditLights_ToggleShadow_f(void)
2522 if (!r_editlights.integer)
2524 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2527 if (!r_shadow_selectedlight)
2529 Con_Printf("No selected light.\n");
2532 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);
2533 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2534 r_shadow_selectedlight = NULL;
2537 void R_Shadow_EditLights_Remove_f(void)
2539 if (!r_editlights.integer)
2541 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2544 if (!r_shadow_selectedlight)
2546 Con_Printf("No selected light.\n");
2549 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2550 r_shadow_selectedlight = NULL;
2553 void R_Shadow_EditLights_Init(void)
2555 Cvar_RegisterVariable(&r_editlights);
2556 Cvar_RegisterVariable(&r_editlights_cursordistance);
2557 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2558 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2559 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2560 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2561 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2562 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2563 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2564 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2565 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2566 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2567 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2568 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2569 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2570 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2571 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);