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 int checkcastshadowfromedge(int t, int i)
283 if (t >= trianglerange_start && t < trianglerange_end)
285 if (t < i && !trianglefacinglight[t])
296 te = inelement3i + t * 3;
297 v[0] = invertex3f + te[0] * 3;
298 v[1] = invertex3f + te[1] * 3;
299 v[2] = invertex3f + te[2] * 3;
300 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
309 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)
311 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
313 const int *e, *n, *te;
316 // make sure trianglefacinglight is big enough for this volume
317 if (maxtrianglefacinglight < trianglerange_end)
318 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
320 if (maxvertexupdate < innumvertices)
322 maxvertexupdate = innumvertices;
324 Mem_Free(vertexupdate);
326 Mem_Free(vertexremap);
327 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
328 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
332 if (r_shadow_singlepassvolumegeneration.integer)
334 // one pass approach (identify lit/dark faces and generate sides while doing so)
335 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
337 // calculate triangle facing flag
338 v[0] = invertex3f + e[0] * 3;
339 v[1] = invertex3f + e[1] * 3;
340 v[2] = invertex3f + e[2] * 3;
341 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
343 // make sure the vertices are created
344 for (j = 0;j < 3;j++)
346 if (vertexupdate[e[j]] != vertexupdatenum)
348 vertexupdate[e[j]] = vertexupdatenum;
349 vertexremap[e[j]] = outvertices;
350 VectorCopy(v[j], outvertex3f);
351 VectorSubtract(v[j], relativelightorigin, temp);
352 f = projectdistance / VectorLength(temp);
353 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
358 // output the front and back triangles
359 vr[0] = vertexremap[e[0]];
360 vr[1] = vertexremap[e[1]];
361 vr[2] = vertexremap[e[2]];
362 outelement3i[0] = vr[0];
363 outelement3i[1] = vr[1];
364 outelement3i[2] = vr[2];
365 outelement3i[3] = vr[2] + 1;
366 outelement3i[4] = vr[1] + 1;
367 outelement3i[5] = vr[0] + 1;
370 // output the sides (facing outward from this triangle)
372 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]))))
374 outelement3i[0] = vr[1];
375 outelement3i[1] = vr[0];
376 outelement3i[2] = vr[0] + 1;
377 outelement3i[3] = vr[1];
378 outelement3i[4] = vr[0] + 1;
379 outelement3i[5] = vr[1] + 1;
384 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]))))
386 outelement3i[0] = vr[2];
387 outelement3i[1] = vr[1];
388 outelement3i[2] = vr[1] + 1;
389 outelement3i[3] = vr[2];
390 outelement3i[4] = vr[1] + 1;
391 outelement3i[5] = vr[2] + 1;
396 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]))))
398 outelement3i[0] = vr[0];
399 outelement3i[1] = vr[2];
400 outelement3i[2] = vr[2] + 1;
401 outelement3i[3] = vr[0];
402 outelement3i[4] = vr[2] + 1;
403 outelement3i[5] = vr[0] + 1;
410 // this triangle is not facing the light
411 // output the sides (facing inward to this triangle)
413 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
415 vr[0] = vertexremap[e[0]];
416 vr[1] = vertexremap[e[1]];
417 outelement3i[0] = vr[1];
418 outelement3i[1] = vr[0] + 1;
419 outelement3i[2] = vr[0];
420 outelement3i[3] = vr[1];
421 outelement3i[4] = vr[1] + 1;
422 outelement3i[5] = vr[0] + 1;
427 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
429 vr[1] = vertexremap[e[1]];
430 vr[2] = vertexremap[e[2]];
431 outelement3i[0] = vr[2];
432 outelement3i[1] = vr[1] + 1;
433 outelement3i[2] = vr[1];
434 outelement3i[3] = vr[2];
435 outelement3i[4] = vr[2] + 1;
436 outelement3i[5] = vr[1] + 1;
441 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
443 vr[0] = vertexremap[e[0]];
444 vr[2] = vertexremap[e[2]];
445 outelement3i[0] = vr[0];
446 outelement3i[1] = vr[2] + 1;
447 outelement3i[2] = vr[2];
448 outelement3i[3] = vr[0];
449 outelement3i[4] = vr[0] + 1;
450 outelement3i[5] = vr[2] + 1;
459 // two pass approach (identify lit/dark faces and then generate sides)
460 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
462 // calculate triangle facing flag
463 v[0] = invertex3f + e[0] * 3;
464 v[1] = invertex3f + e[1] * 3;
465 v[2] = invertex3f + e[2] * 3;
466 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
468 trianglefacinglightlist[numfacing++] = i;
469 // make sure the vertices are created
470 for (j = 0;j < 3;j++)
472 if (vertexupdate[e[j]] != vertexupdatenum)
474 vertexupdate[e[j]] = vertexupdatenum;
475 vertexremap[e[j]] = outvertices;
476 VectorSubtract(v[j], relativelightorigin, temp);
477 f = projectdistance / VectorLength(temp);
478 VectorCopy(v[j], outvertex3f);
479 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
484 // output the front and back triangles
485 outelement3i[0] = vertexremap[e[0]];
486 outelement3i[1] = vertexremap[e[1]];
487 outelement3i[2] = vertexremap[e[2]];
488 outelement3i[3] = vertexremap[e[2]] + 1;
489 outelement3i[4] = vertexremap[e[1]] + 1;
490 outelement3i[5] = vertexremap[e[0]] + 1;
495 for (i = 0;i < numfacing;i++)
497 t = trianglefacinglightlist[i];
498 e = inelement3i + t * 3;
499 n = inneighbor3i + t * 3;
500 // output the sides (facing outward from this triangle)
502 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]))))
504 vr[0] = vertexremap[e[0]];
505 vr[1] = vertexremap[e[1]];
506 outelement3i[0] = vr[1];
507 outelement3i[1] = vr[0];
508 outelement3i[2] = vr[0] + 1;
509 outelement3i[3] = vr[1];
510 outelement3i[4] = vr[0] + 1;
511 outelement3i[5] = vr[1] + 1;
516 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]))))
518 vr[1] = vertexremap[e[1]];
519 vr[2] = vertexremap[e[2]];
520 outelement3i[0] = vr[2];
521 outelement3i[1] = vr[1];
522 outelement3i[2] = vr[1] + 1;
523 outelement3i[3] = vr[2];
524 outelement3i[4] = vr[1] + 1;
525 outelement3i[5] = vr[2] + 1;
530 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]))))
532 vr[0] = vertexremap[e[0]];
533 vr[2] = vertexremap[e[2]];
534 outelement3i[0] = vr[0];
535 outelement3i[1] = vr[2];
536 outelement3i[2] = vr[2] + 1;
537 outelement3i[3] = vr[0];
538 outelement3i[4] = vr[2] + 1;
539 outelement3i[5] = vr[0] + 1;
546 *outnumvertices = outvertices;
550 float varray_vertex3f2[65536*3];
552 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
555 if (projectdistance < 0.1)
557 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
563 // make sure shadowelements is big enough for this volume
564 if (maxshadowelements < numtris * 24)
565 R_Shadow_ResizeShadowElements(numtris);
567 // check which triangles are facing the light, and then output
568 // triangle elements and vertices... by clever use of elements we
569 // can construct the whole shadow from the unprojected vertices and
570 // the projected vertices
571 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
573 GL_VertexPointer(varray_vertex3f2);
574 if (r_shadowstage == SHADOWSTAGE_STENCIL)
576 // increment stencil if backface is behind depthbuffer
577 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
578 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
579 R_Mesh_Draw(outverts, tris, shadowelements);
581 c_rt_shadowtris += numtris;
582 // decrement stencil if frontface is behind depthbuffer
583 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
584 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
586 R_Mesh_Draw(outverts, tris, shadowelements);
588 c_rt_shadowtris += numtris;
592 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
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 for (mesh = firstmesh;mesh;mesh = mesh->next)
602 GL_VertexPointer(mesh->vertex3f);
603 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
604 c_rtcached_shadowmeshes++;
605 c_rtcached_shadowtris += mesh->numtriangles;
607 // decrement stencil if frontface is behind depthbuffer
608 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
609 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
611 for (mesh = firstmesh;mesh;mesh = mesh->next)
613 GL_VertexPointer(mesh->vertex3f);
614 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
615 c_rtcached_shadowmeshes++;
616 c_rtcached_shadowtris += mesh->numtriangles;
620 float r_shadow_attenpower, r_shadow_attenscale;
621 static void R_Shadow_MakeTextures(void)
623 int x, y, z, d, side;
624 float v[3], s, t, intensity;
626 R_FreeTexturePool(&r_shadow_texturepool);
627 r_shadow_texturepool = R_AllocTexturePool();
628 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
629 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
631 #define ATTEN2DSIZE 64
632 #define ATTEN3DSIZE 32
633 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
638 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
643 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
648 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
649 if (gl_texturecubemap)
651 for (side = 0;side < 6;side++)
653 for (y = 0;y < NORMSIZE;y++)
655 for (x = 0;x < NORMSIZE;x++)
657 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
658 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
692 intensity = 127.0f / sqrt(DotProduct(v, v));
693 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
694 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
695 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
696 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
700 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
703 r_shadow_normalcubetexture = NULL;
704 for (y = 0;y < ATTEN2DSIZE;y++)
706 for (x = 0;x < ATTEN2DSIZE;x++)
708 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
709 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
711 intensity = 1.0f - sqrt(DotProduct(v, v));
713 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
714 d = bound(0, intensity, 255);
715 data[(y*ATTEN2DSIZE+x)*4+0] = d;
716 data[(y*ATTEN2DSIZE+x)*4+1] = d;
717 data[(y*ATTEN2DSIZE+x)*4+2] = d;
718 data[(y*ATTEN2DSIZE+x)*4+3] = d;
721 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
722 if (r_shadow_texture3d.integer)
724 for (z = 0;z < ATTEN3DSIZE;z++)
726 for (y = 0;y < ATTEN3DSIZE;y++)
728 for (x = 0;x < ATTEN3DSIZE;x++)
730 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
731 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
732 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
733 intensity = 1.0f - sqrt(DotProduct(v, v));
735 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
736 d = bound(0, intensity, 255);
737 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
738 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
739 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
740 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
744 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
749 void R_Shadow_Stage_Begin(void)
753 if (r_shadow_texture3d.integer && !gl_texture3d)
754 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
756 //cl.worldmodel->brushq1.numlights = min(cl.worldmodel->brushq1.numlights, 1);
757 if (!r_shadow_attenuation2dtexture
758 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
759 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
760 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
761 R_Shadow_MakeTextures();
763 memset(&m, 0, sizeof(m));
764 GL_BlendFunc(GL_ONE, GL_ZERO);
767 R_Mesh_State_Texture(&m);
768 GL_Color(0, 0, 0, 1);
769 qglDisable(GL_SCISSOR_TEST);
770 r_shadowstage = SHADOWSTAGE_NONE;
772 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
773 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
774 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
777 void R_Shadow_LoadWorldLightsIfNeeded(void)
779 if (r_shadow_reloadlights && cl.worldmodel)
781 R_Shadow_ClearWorldLights();
782 r_shadow_reloadlights = false;
783 R_Shadow_LoadWorldLights();
784 if (r_shadow_worldlightchain == NULL)
786 R_Shadow_LoadLightsFile();
787 if (r_shadow_worldlightchain == NULL)
788 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
793 void R_Shadow_Stage_ShadowVolumes(void)
796 memset(&m, 0, sizeof(m));
797 R_Mesh_State_Texture(&m);
798 GL_Color(1, 1, 1, 1);
799 qglColorMask(0, 0, 0, 0);
800 GL_BlendFunc(GL_ONE, GL_ZERO);
803 if (r_shadow_polygonoffset.value != 0)
805 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
806 qglEnable(GL_POLYGON_OFFSET_FILL);
809 qglDisable(GL_POLYGON_OFFSET_FILL);
810 qglDepthFunc(GL_LESS);
811 qglEnable(GL_STENCIL_TEST);
812 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
813 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
814 r_shadowstage = SHADOWSTAGE_STENCIL;
815 qglClear(GL_STENCIL_BUFFER_BIT);
817 // LordHavoc note: many shadow volumes reside entirely inside the world
818 // (that is to say they are entirely bounded by their lit surfaces),
819 // which can be optimized by handling things as an inverted light volume,
820 // with the shadow boundaries of the world being simulated by an altered
821 // (129) bias to stencil clearing on such lights
822 // FIXME: generate inverted light volumes for use as shadow volumes and
823 // optimize for them as noted above
826 void R_Shadow_Stage_LightWithoutShadows(void)
829 memset(&m, 0, sizeof(m));
830 R_Mesh_State_Texture(&m);
831 GL_BlendFunc(GL_ONE, GL_ONE);
834 qglDisable(GL_POLYGON_OFFSET_FILL);
835 GL_Color(1, 1, 1, 1);
836 qglColorMask(1, 1, 1, 1);
837 qglDepthFunc(GL_EQUAL);
838 qglDisable(GL_STENCIL_TEST);
839 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
840 qglStencilFunc(GL_EQUAL, 128, 0xFF);
841 r_shadowstage = SHADOWSTAGE_LIGHT;
845 void R_Shadow_Stage_LightWithShadows(void)
848 memset(&m, 0, sizeof(m));
849 R_Mesh_State_Texture(&m);
850 GL_BlendFunc(GL_ONE, GL_ONE);
853 qglDisable(GL_POLYGON_OFFSET_FILL);
854 GL_Color(1, 1, 1, 1);
855 qglColorMask(1, 1, 1, 1);
856 qglDepthFunc(GL_EQUAL);
857 qglEnable(GL_STENCIL_TEST);
858 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
859 // only draw light where this geometry was already rendered AND the
860 // stencil is 128 (values other than this mean shadow)
861 qglStencilFunc(GL_EQUAL, 128, 0xFF);
862 r_shadowstage = SHADOWSTAGE_LIGHT;
866 void R_Shadow_Stage_End(void)
869 memset(&m, 0, sizeof(m));
870 R_Mesh_State_Texture(&m);
871 GL_BlendFunc(GL_ONE, GL_ZERO);
874 qglDisable(GL_POLYGON_OFFSET_FILL);
875 GL_Color(1, 1, 1, 1);
876 qglColorMask(1, 1, 1, 1);
877 qglDisable(GL_SCISSOR_TEST);
878 qglDepthFunc(GL_LEQUAL);
879 qglDisable(GL_STENCIL_TEST);
880 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
881 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
882 r_shadowstage = SHADOWSTAGE_NONE;
886 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
888 int i, ix1, iy1, ix2, iy2;
889 float x1, y1, x2, y2, x, y;
892 if (!r_shadow_scissor.integer)
894 // if view is inside the box, just say yes it's visible
895 if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
896 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
897 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
899 qglDisable(GL_SCISSOR_TEST);
902 VectorSubtract(r_origin, origin, v);
903 if (DotProduct(v, v) < radius * radius)
905 qglDisable(GL_SCISSOR_TEST);
908 // create viewspace bbox
909 for (i = 0;i < 8;i++)
911 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
912 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
913 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
914 v2[0] = DotProduct(v, vright);
915 v2[1] = DotProduct(v, vup);
916 v2[2] = DotProduct(v, vpn);
919 if (smins[0] > v2[0]) smins[0] = v2[0];
920 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
921 if (smins[1] > v2[1]) smins[1] = v2[1];
922 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
923 if (smins[2] > v2[2]) smins[2] = v2[2];
924 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
928 smins[0] = smaxs[0] = v2[0];
929 smins[1] = smaxs[1] = v2[1];
930 smins[2] = smaxs[2] = v2[2];
933 // now we have a bbox in viewspace
934 // clip it to the viewspace version of the sphere
935 v[0] = origin[0] - r_origin[0];
936 v[1] = origin[1] - r_origin[1];
937 v[2] = origin[2] - r_origin[2];
938 v2[0] = DotProduct(v, vright);
939 v2[1] = DotProduct(v, vup);
940 v2[2] = DotProduct(v, vpn);
941 if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
942 if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
943 if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
944 if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
945 if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
946 if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
947 // clip it to the view plane
950 // return true if that culled the box
951 if (smins[2] >= smaxs[2])
953 // ok some of it is infront of the view, transform each corner back to
954 // worldspace and then to screenspace and make screen rect
955 // initialize these variables just to avoid compiler warnings
956 x1 = y1 = x2 = y2 = 0;
957 for (i = 0;i < 8;i++)
959 v2[0] = (i & 1) ? smins[0] : smaxs[0];
960 v2[1] = (i & 2) ? smins[1] : smaxs[1];
961 v2[2] = (i & 4) ? smins[2] : smaxs[2];
962 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
963 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
964 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
966 GL_TransformToScreen(v, v2);
967 //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]);
984 // this code doesn't handle boxes with any points behind view properly
985 x1 = 1000;x2 = -1000;
986 y1 = 1000;y2 = -1000;
987 for (i = 0;i < 8;i++)
989 v[0] = (i & 1) ? mins[0] : maxs[0];
990 v[1] = (i & 2) ? mins[1] : maxs[1];
991 v[2] = (i & 4) ? mins[2] : maxs[2];
993 GL_TransformToScreen(v, v2);
994 //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]);
1011 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1012 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1013 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1014 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1015 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1016 if (ix2 <= ix1 || iy2 <= iy1)
1018 // set up the scissor rectangle
1019 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1020 qglEnable(GL_SCISSOR_TEST);
1026 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1028 int i, ix1, iy1, ix2, iy2;
1029 float x1, y1, x2, y2, x, y, f;
1030 vec3_t smins, smaxs;
1032 if (!r_shadow_scissor.integer)
1034 // if view is inside the box, just say yes it's visible
1035 // LordHavoc: for some odd reason scissor seems broken without stencil
1036 // (?!? seems like a driver bug) so abort if gl_stencil is false
1037 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1039 qglDisable(GL_SCISSOR_TEST);
1042 for (i = 0;i < 3;i++)
1055 f = DotProduct(vpn, r_origin) + 1;
1056 if (DotProduct(vpn, v2) <= f)
1058 // entirely behind nearclip plane
1061 if (DotProduct(vpn, v) >= f)
1063 // entirely infront of nearclip plane
1064 x1 = y1 = x2 = y2 = 0;
1065 for (i = 0;i < 8;i++)
1067 v[0] = (i & 1) ? mins[0] : maxs[0];
1068 v[1] = (i & 2) ? mins[1] : maxs[1];
1069 v[2] = (i & 4) ? mins[2] : maxs[2];
1071 GL_TransformToScreen(v, v2);
1072 //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]);
1091 // clipped by nearclip plane
1092 // this is nasty and crude...
1093 // create viewspace bbox
1094 for (i = 0;i < 8;i++)
1096 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1097 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1098 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1099 v2[0] = DotProduct(v, vright);
1100 v2[1] = DotProduct(v, vup);
1101 v2[2] = DotProduct(v, vpn);
1104 if (smins[0] > v2[0]) smins[0] = v2[0];
1105 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1106 if (smins[1] > v2[1]) smins[1] = v2[1];
1107 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1108 if (smins[2] > v2[2]) smins[2] = v2[2];
1109 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1113 smins[0] = smaxs[0] = v2[0];
1114 smins[1] = smaxs[1] = v2[1];
1115 smins[2] = smaxs[2] = v2[2];
1118 // now we have a bbox in viewspace
1119 // clip it to the view plane
1122 // return true if that culled the box
1123 if (smins[2] >= smaxs[2])
1125 // ok some of it is infront of the view, transform each corner back to
1126 // worldspace and then to screenspace and make screen rect
1127 // initialize these variables just to avoid compiler warnings
1128 x1 = y1 = x2 = y2 = 0;
1129 for (i = 0;i < 8;i++)
1131 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1132 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1133 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1134 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1135 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1136 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1138 GL_TransformToScreen(v, v2);
1139 //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]);
1156 // this code doesn't handle boxes with any points behind view properly
1157 x1 = 1000;x2 = -1000;
1158 y1 = 1000;y2 = -1000;
1159 for (i = 0;i < 8;i++)
1161 v[0] = (i & 1) ? mins[0] : maxs[0];
1162 v[1] = (i & 2) ? mins[1] : maxs[1];
1163 v[2] = (i & 4) ? mins[2] : maxs[2];
1165 GL_TransformToScreen(v, v2);
1166 //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]);
1184 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1185 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1186 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1187 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1188 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1189 if (ix2 <= ix1 || iy2 <= iy1)
1191 // set up the scissor rectangle
1192 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1193 qglEnable(GL_SCISSOR_TEST);
1198 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1200 float *color4f = varray_color4f;
1201 float dist, dot, intensity, v[3], n[3];
1202 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1204 Matrix4x4_Transform(m, vertex3f, v);
1205 if ((dist = DotProduct(v, v)) < 1)
1207 Matrix4x4_Transform3x3(m, normal3f, n);
1208 if ((dot = DotProduct(n, v)) > 0)
1211 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1212 VectorScale(lightcolor, intensity, color4f);
1217 VectorClear(color4f);
1223 VectorClear(color4f);
1229 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1231 float *color4f = varray_color4f;
1232 float dist, dot, intensity, v[3], n[3];
1233 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1235 Matrix4x4_Transform(m, vertex3f, v);
1236 if ((dist = fabs(v[2])) < 1)
1238 Matrix4x4_Transform3x3(m, normal3f, n);
1239 if ((dot = DotProduct(n, v)) > 0)
1241 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1242 VectorScale(lightcolor, intensity, color4f);
1247 VectorClear(color4f);
1253 VectorClear(color4f);
1259 // FIXME: this should be done in a vertex program when possible
1260 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1261 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1265 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1266 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1267 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1274 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1278 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1279 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1286 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)
1290 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1292 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1293 // the cubemap normalizes this for us
1294 out3f[0] = DotProduct(svector3f, lightdir);
1295 out3f[1] = DotProduct(tvector3f, lightdir);
1296 out3f[2] = DotProduct(normal3f, lightdir);
1300 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)
1303 float lightdir[3], eyedir[3], halfdir[3];
1304 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1306 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1307 VectorNormalizeFast(lightdir);
1308 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1309 VectorNormalizeFast(eyedir);
1310 VectorAdd(lightdir, eyedir, halfdir);
1311 // the cubemap normalizes this for us
1312 out3f[0] = DotProduct(svector3f, halfdir);
1313 out3f[1] = DotProduct(tvector3f, halfdir);
1314 out3f[2] = DotProduct(normal3f, halfdir);
1318 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)
1321 float color[3], color2[3];
1323 GL_VertexPointer(vertex3f);
1324 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1327 bumptexture = r_shadow_blankbumptexture;
1329 // colorscale accounts for how much we multiply the brightness during combine
1330 // mult is how many times the final pass of the lighting will be
1331 // performed to get more brightness than otherwise possible
1332 // limit mult to 64 for sanity sake
1333 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1335 // 3/2 3D combine path (Geforce3, Radeon 8500)
1336 memset(&m, 0, sizeof(m));
1337 m.tex[0] = R_GetTexture(bumptexture);
1338 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1339 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1340 m.texcombinergb[0] = GL_REPLACE;
1341 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1342 m.pointer_texcoord[0] = texcoord2f;
1343 m.pointer_texcoord[1] = varray_texcoord3f[1];
1344 m.pointer_texcoord[2] = varray_texcoord3f[2];
1345 R_Mesh_State_Texture(&m);
1346 qglColorMask(0,0,0,1);
1347 GL_BlendFunc(GL_ONE, GL_ZERO);
1348 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1349 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1350 R_Mesh_Draw(numverts, numtriangles, elements);
1352 c_rt_lighttris += numtriangles;
1354 memset(&m, 0, sizeof(m));
1355 m.tex[0] = R_GetTexture(basetexture);
1356 m.texcubemap[1] = R_GetTexture(lightcubemap);
1357 m.pointer_texcoord[0] = texcoord2f;
1358 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1359 R_Mesh_State_Texture(&m);
1360 qglColorMask(1,1,1,0);
1361 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1363 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1364 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1365 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1367 color[0] = bound(0, color2[0], 1);
1368 color[1] = bound(0, color2[1], 1);
1369 color[2] = bound(0, color2[2], 1);
1370 GL_Color(color[0], color[1], color[2], 1);
1371 R_Mesh_Draw(numverts, numtriangles, elements);
1373 c_rt_lighttris += numtriangles;
1376 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1378 // 1/2/2 3D combine path (original Radeon)
1379 memset(&m, 0, sizeof(m));
1380 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1381 m.pointer_texcoord[0] = varray_texcoord3f[0];
1382 R_Mesh_State_Texture(&m);
1383 qglColorMask(0,0,0,1);
1384 GL_BlendFunc(GL_ONE, GL_ZERO);
1385 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1386 R_Mesh_Draw(numverts, numtriangles, elements);
1388 c_rt_lighttris += numtriangles;
1390 memset(&m, 0, sizeof(m));
1391 m.tex[0] = R_GetTexture(bumptexture);
1392 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1393 m.texcombinergb[0] = GL_REPLACE;
1394 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1395 m.pointer_texcoord[0] = texcoord2f;
1396 m.pointer_texcoord[1] = varray_texcoord3f[1];
1397 R_Mesh_State_Texture(&m);
1398 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1399 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1400 R_Mesh_Draw(numverts, numtriangles, elements);
1402 c_rt_lighttris += numtriangles;
1404 memset(&m, 0, sizeof(m));
1405 m.tex[0] = R_GetTexture(basetexture);
1406 m.texcubemap[1] = R_GetTexture(lightcubemap);
1407 m.pointer_texcoord[0] = texcoord2f;
1408 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1409 R_Mesh_State_Texture(&m);
1410 qglColorMask(1,1,1,0);
1411 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1413 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1414 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1415 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1417 color[0] = bound(0, color2[0], 1);
1418 color[1] = bound(0, color2[1], 1);
1419 color[2] = bound(0, color2[2], 1);
1420 GL_Color(color[0], color[1], color[2], 1);
1421 R_Mesh_Draw(numverts, numtriangles, elements);
1423 c_rt_lighttris += numtriangles;
1426 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1428 // 2/2 3D combine path (original Radeon)
1429 memset(&m, 0, sizeof(m));
1430 m.tex[0] = R_GetTexture(bumptexture);
1431 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1432 m.texcombinergb[0] = GL_REPLACE;
1433 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1434 m.pointer_texcoord[0] = texcoord2f;
1435 m.pointer_texcoord[1] = varray_texcoord3f[1];
1436 R_Mesh_State_Texture(&m);
1437 qglColorMask(0,0,0,1);
1438 GL_BlendFunc(GL_ONE, GL_ZERO);
1439 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1440 R_Mesh_Draw(numverts, numtriangles, elements);
1442 c_rt_lighttris += numtriangles;
1444 memset(&m, 0, sizeof(m));
1445 m.tex[0] = R_GetTexture(basetexture);
1446 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1447 m.pointer_texcoord[0] = texcoord2f;
1448 m.pointer_texcoord[1] = varray_texcoord3f[1];
1449 R_Mesh_State_Texture(&m);
1450 qglColorMask(1,1,1,0);
1451 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1452 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1453 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1454 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1456 color[0] = bound(0, color2[0], 1);
1457 color[1] = bound(0, color2[1], 1);
1458 color[2] = bound(0, color2[2], 1);
1459 GL_Color(color[0], color[1], color[2], 1);
1460 R_Mesh_Draw(numverts, numtriangles, elements);
1462 c_rt_lighttris += numtriangles;
1465 else if (r_textureunits.integer >= 4)
1467 // 4/2 2D combine path (Geforce3, Radeon 8500)
1468 memset(&m, 0, sizeof(m));
1469 m.tex[0] = R_GetTexture(bumptexture);
1470 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1471 m.texcombinergb[0] = GL_REPLACE;
1472 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1473 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1474 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1475 m.pointer_texcoord[0] = texcoord2f;
1476 m.pointer_texcoord[1] = varray_texcoord3f[1];
1477 m.pointer_texcoord[2] = varray_texcoord2f[2];
1478 m.pointer_texcoord[3] = varray_texcoord2f[3];
1479 R_Mesh_State_Texture(&m);
1480 qglColorMask(0,0,0,1);
1481 GL_BlendFunc(GL_ONE, GL_ZERO);
1482 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1483 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1484 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1485 R_Mesh_Draw(numverts, numtriangles, elements);
1487 c_rt_lighttris += numtriangles;
1489 memset(&m, 0, sizeof(m));
1490 m.tex[0] = R_GetTexture(basetexture);
1491 m.texcubemap[1] = R_GetTexture(lightcubemap);
1492 m.pointer_texcoord[0] = texcoord2f;
1493 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1494 R_Mesh_State_Texture(&m);
1495 qglColorMask(1,1,1,0);
1496 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1498 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1499 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1500 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1502 color[0] = bound(0, color2[0], 1);
1503 color[1] = bound(0, color2[1], 1);
1504 color[2] = bound(0, color2[2], 1);
1505 GL_Color(color[0], color[1], color[2], 1);
1506 R_Mesh_Draw(numverts, numtriangles, elements);
1508 c_rt_lighttris += numtriangles;
1513 // 2/2/2 2D combine path (any dot3 card)
1514 memset(&m, 0, sizeof(m));
1515 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1516 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1517 m.pointer_texcoord[0] = varray_texcoord2f[0];
1518 m.pointer_texcoord[1] = varray_texcoord2f[1];
1519 R_Mesh_State_Texture(&m);
1520 qglColorMask(0,0,0,1);
1521 GL_BlendFunc(GL_ONE, GL_ZERO);
1522 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1523 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1524 R_Mesh_Draw(numverts, numtriangles, elements);
1526 c_rt_lighttris += numtriangles;
1528 memset(&m, 0, sizeof(m));
1529 m.tex[0] = R_GetTexture(bumptexture);
1530 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1531 m.texcombinergb[0] = GL_REPLACE;
1532 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1533 m.pointer_texcoord[0] = texcoord2f;
1534 m.pointer_texcoord[1] = varray_texcoord3f[1];
1535 R_Mesh_State_Texture(&m);
1536 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1537 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1538 R_Mesh_Draw(numverts, numtriangles, elements);
1540 c_rt_lighttris += numtriangles;
1542 memset(&m, 0, sizeof(m));
1543 m.tex[0] = R_GetTexture(basetexture);
1544 m.texcubemap[1] = R_GetTexture(lightcubemap);
1545 m.pointer_texcoord[0] = texcoord2f;
1546 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1547 R_Mesh_State_Texture(&m);
1548 qglColorMask(1,1,1,0);
1549 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1551 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1552 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1553 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1555 color[0] = bound(0, color2[0], 1);
1556 color[1] = bound(0, color2[1], 1);
1557 color[2] = bound(0, color2[2], 1);
1558 GL_Color(color[0], color[1], color[2], 1);
1559 R_Mesh_Draw(numverts, numtriangles, elements);
1561 c_rt_lighttris += numtriangles;
1567 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1568 GL_DepthMask(false);
1570 GL_ColorPointer(varray_color4f);
1571 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1572 memset(&m, 0, sizeof(m));
1573 m.tex[0] = R_GetTexture(basetexture);
1574 m.pointer_texcoord[0] = texcoord2f;
1575 if (r_textureunits.integer >= 2)
1578 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1579 m.pointer_texcoord[1] = varray_texcoord2f[1];
1580 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1582 R_Mesh_State_Texture(&m);
1583 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1585 color[0] = bound(0, color2[0], 1);
1586 color[1] = bound(0, color2[1], 1);
1587 color[2] = bound(0, color2[2], 1);
1588 if (r_textureunits.integer >= 2)
1589 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1591 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1592 R_Mesh_Draw(numverts, numtriangles, elements);
1594 c_rt_lighttris += numtriangles;
1599 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)
1602 float color[3], color2[3], colorscale;
1604 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1607 glosstexture = r_shadow_blankglosstexture;
1608 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1610 colorscale = r_colorscale * r_shadow_glossintensity.value;
1612 bumptexture = r_shadow_blankbumptexture;
1613 if (glosstexture == r_shadow_blankglosstexture)
1614 colorscale *= r_shadow_gloss2intensity.value;
1615 GL_VertexPointer(vertex3f);
1617 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1619 // 2/0/0/1/2 3D combine blendsquare path
1620 memset(&m, 0, sizeof(m));
1621 m.tex[0] = R_GetTexture(bumptexture);
1622 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1623 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1624 m.pointer_texcoord[0] = texcoord2f;
1625 m.pointer_texcoord[1] = varray_texcoord3f[1];
1626 R_Mesh_State_Texture(&m);
1627 qglColorMask(0,0,0,1);
1628 // this squares the result
1629 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1630 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1631 R_Mesh_Draw(numverts, numtriangles, elements);
1633 c_rt_lighttris += numtriangles;
1635 memset(&m, 0, sizeof(m));
1636 R_Mesh_State_Texture(&m);
1637 // square alpha in framebuffer a few times to make it shiny
1638 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1639 // these comments are a test run through this math for intensity 0.5
1640 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1641 // 0.25 * 0.25 = 0.0625 (this is another pass)
1642 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1643 R_Mesh_Draw(numverts, numtriangles, elements);
1645 c_rt_lighttris += numtriangles;
1646 R_Mesh_Draw(numverts, numtriangles, elements);
1648 c_rt_lighttris += numtriangles;
1650 memset(&m, 0, sizeof(m));
1651 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1652 m.pointer_texcoord[0] = varray_texcoord3f[0];
1653 R_Mesh_State_Texture(&m);
1654 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1655 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1656 R_Mesh_Draw(numverts, numtriangles, elements);
1658 c_rt_lighttris += numtriangles;
1660 memset(&m, 0, sizeof(m));
1661 m.tex[0] = R_GetTexture(glosstexture);
1662 m.texcubemap[1] = R_GetTexture(lightcubemap);
1663 m.pointer_texcoord[0] = texcoord2f;
1664 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1665 R_Mesh_State_Texture(&m);
1666 qglColorMask(1,1,1,0);
1667 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1669 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1670 VectorScale(lightcolor, colorscale, color2);
1671 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1673 color[0] = bound(0, color2[0], 1);
1674 color[1] = bound(0, color2[1], 1);
1675 color[2] = bound(0, color2[2], 1);
1676 GL_Color(color[0], color[1], color[2], 1);
1677 R_Mesh_Draw(numverts, numtriangles, elements);
1679 c_rt_lighttris += numtriangles;
1682 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1684 // 2/0/0/2 3D combine blendsquare path
1685 memset(&m, 0, sizeof(m));
1686 m.tex[0] = R_GetTexture(bumptexture);
1687 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1688 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1689 m.pointer_texcoord[0] = texcoord2f;
1690 m.pointer_texcoord[1] = varray_texcoord3f[1];
1691 R_Mesh_State_Texture(&m);
1692 qglColorMask(0,0,0,1);
1693 // this squares the result
1694 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1695 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1696 R_Mesh_Draw(numverts, numtriangles, elements);
1698 c_rt_lighttris += numtriangles;
1700 memset(&m, 0, sizeof(m));
1701 R_Mesh_State_Texture(&m);
1702 // square alpha in framebuffer a few times to make it shiny
1703 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1704 // these comments are a test run through this math for intensity 0.5
1705 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1706 // 0.25 * 0.25 = 0.0625 (this is another pass)
1707 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1708 R_Mesh_Draw(numverts, numtriangles, elements);
1710 c_rt_lighttris += numtriangles;
1711 R_Mesh_Draw(numverts, numtriangles, elements);
1713 c_rt_lighttris += numtriangles;
1715 memset(&m, 0, sizeof(m));
1716 m.tex[0] = R_GetTexture(glosstexture);
1717 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1718 m.pointer_texcoord[0] = texcoord2f;
1719 m.pointer_texcoord[1] = varray_texcoord3f[1];
1720 R_Mesh_State_Texture(&m);
1721 qglColorMask(1,1,1,0);
1722 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1723 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1724 VectorScale(lightcolor, colorscale, color2);
1725 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1727 color[0] = bound(0, color2[0], 1);
1728 color[1] = bound(0, color2[1], 1);
1729 color[2] = bound(0, color2[2], 1);
1730 GL_Color(color[0], color[1], color[2], 1);
1731 R_Mesh_Draw(numverts, numtriangles, elements);
1733 c_rt_lighttris += numtriangles;
1736 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1738 // 2/0/0/2/2 2D combine blendsquare path
1739 memset(&m, 0, sizeof(m));
1740 m.tex[0] = R_GetTexture(bumptexture);
1741 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1742 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1743 m.pointer_texcoord[0] = texcoord2f;
1744 m.pointer_texcoord[1] = varray_texcoord3f[1];
1745 R_Mesh_State_Texture(&m);
1746 qglColorMask(0,0,0,1);
1747 // this squares the result
1748 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1749 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1750 R_Mesh_Draw(numverts, numtriangles, elements);
1752 c_rt_lighttris += numtriangles;
1754 memset(&m, 0, sizeof(m));
1755 R_Mesh_State_Texture(&m);
1756 // square alpha in framebuffer a few times to make it shiny
1757 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1758 // these comments are a test run through this math for intensity 0.5
1759 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1760 // 0.25 * 0.25 = 0.0625 (this is another pass)
1761 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1762 R_Mesh_Draw(numverts, numtriangles, elements);
1764 c_rt_lighttris += numtriangles;
1765 R_Mesh_Draw(numverts, numtriangles, elements);
1767 c_rt_lighttris += numtriangles;
1769 memset(&m, 0, sizeof(m));
1770 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1771 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1772 m.pointer_texcoord[0] = varray_texcoord2f[0];
1773 m.pointer_texcoord[1] = varray_texcoord2f[1];
1774 R_Mesh_State_Texture(&m);
1775 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1776 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1777 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1778 R_Mesh_Draw(numverts, numtriangles, elements);
1780 c_rt_lighttris += numtriangles;
1782 memset(&m, 0, sizeof(m));
1783 m.tex[0] = R_GetTexture(glosstexture);
1784 m.texcubemap[1] = R_GetTexture(lightcubemap);
1785 m.pointer_texcoord[0] = texcoord2f;
1786 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1787 R_Mesh_State_Texture(&m);
1788 qglColorMask(1,1,1,0);
1789 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1791 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1792 VectorScale(lightcolor, colorscale, color2);
1793 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1795 color[0] = bound(0, color2[0], 1);
1796 color[1] = bound(0, color2[1], 1);
1797 color[2] = bound(0, color2[2], 1);
1798 GL_Color(color[0], color[1], color[2], 1);
1799 R_Mesh_Draw(numverts, numtriangles, elements);
1801 c_rt_lighttris += numtriangles;
1807 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1809 R_Mesh_Matrix(matrix);
1810 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1813 cvar_t r_editlights = {0, "r_editlights", "0"};
1814 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1815 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1816 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1817 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1818 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1819 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1820 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1821 worldlight_t *r_shadow_worldlightchain;
1822 worldlight_t *r_shadow_selectedlight;
1823 vec3_t r_editlights_cursorlocation;
1825 static int castshadowcount = 1;
1826 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1828 int i, j, k, l, maxverts = 256, *mark, tris;
1829 float *vertex3f = NULL;
1831 shadowmesh_t *mesh, *castmesh;
1835 surfmesh_t *surfmesh;
1837 if (radius < 15 || DotProduct(color, color) < 0.03)
1839 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1843 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1844 VectorCopy(origin, e->origin);
1845 VectorCopy(color, e->light);
1846 e->lightradius = radius;
1848 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1850 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1853 e->castshadows = castshadow;
1855 e->cullradius = e->lightradius;
1856 for (k = 0;k < 3;k++)
1858 e->mins[k] = e->origin[k] - e->lightradius;
1859 e->maxs[k] = e->origin[k] + e->lightradius;
1862 e->next = r_shadow_worldlightchain;
1863 r_shadow_worldlightchain = e;
1864 if (cubemapname && cubemapname[0])
1866 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1867 strcpy(e->cubemapname, cubemapname);
1868 // FIXME: add cubemap loading (and don't load a cubemap twice)
1873 i = CL_PointContents(e->origin);
1874 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1877 qbyte *bytesurfacepvs;
1879 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs + 1);
1880 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1882 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1884 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1885 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1886 leaf->worldnodeframe = castshadowcount;
1888 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1889 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1890 surf->castshadow = castshadowcount;
1892 Mem_Free(byteleafpvs);
1893 Mem_Free(bytesurfacepvs);
1897 leaf = cl.worldmodel->brushq1.PointInLeaf(cl.worldmodel, origin);
1898 pvs = cl.worldmodel->brushq1.LeafPVS(cl.worldmodel, leaf);
1899 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1901 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1903 leaf->worldnodeframe = castshadowcount;
1904 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1906 surf = cl.worldmodel->brushq1.surfaces + *mark;
1907 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1908 surf->castshadow = castshadowcount;
1915 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1916 if (leaf->worldnodeframe == castshadowcount)
1919 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1920 if (surf->castshadow == castshadowcount)
1924 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1926 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1928 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1929 if (leaf->worldnodeframe == castshadowcount)
1930 e->leafs[e->numleafs++] = leaf;
1932 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1933 if (surf->castshadow == castshadowcount)
1934 e->surfaces[e->numsurfaces++] = surf;
1936 // find bounding box of lit leafs
1937 VectorCopy(e->origin, e->mins);
1938 VectorCopy(e->origin, e->maxs);
1939 for (j = 0;j < e->numleafs;j++)
1942 for (k = 0;k < 3;k++)
1944 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1945 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1949 for (k = 0;k < 3;k++)
1951 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1952 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1954 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1959 for (j = 0;j < e->numsurfaces;j++)
1961 surf = e->surfaces[j];
1962 if (surf->flags & SURF_SHADOWCAST)
1964 surf->castshadow = castshadowcount;
1965 if (maxverts < surf->poly_numverts)
1966 maxverts = surf->poly_numverts;
1969 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1970 // make a mesh to cast a shadow volume from
1971 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1972 for (j = 0;j < e->numsurfaces;j++)
1973 if (e->surfaces[j]->castshadow == castshadowcount)
1974 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1975 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1976 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1978 // cast shadow volume from castmesh
1979 for (mesh = castmesh;mesh;mesh = mesh->next)
1981 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1983 if (maxverts < castmesh->numverts * 2)
1985 maxverts = castmesh->numverts * 2;
1990 if (vertex3f == NULL && maxverts > 0)
1991 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1993 // now that we have the buffers big enough, construct and add
1994 // the shadow volume mesh
1995 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)))
1996 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
2001 // we're done with castmesh now
2002 Mod_ShadowMesh_Free(castmesh);
2003 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
2004 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
2005 l += mesh->numtriangles;
2006 Con_Printf("static shadow volume built containing %i triangles\n", l);
2009 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
2012 void R_Shadow_FreeWorldLight(worldlight_t *light)
2014 worldlight_t **lightpointer;
2015 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2016 if (*lightpointer != light)
2017 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2018 *lightpointer = light->next;
2019 if (light->cubemapname)
2020 Mem_Free(light->cubemapname);
2021 if (light->shadowvolume)
2022 Mod_ShadowMesh_Free(light->shadowvolume);
2023 if (light->surfaces)
2024 Mem_Free(light->surfaces);
2026 Mem_Free(light->leafs);
2030 void R_Shadow_ClearWorldLights(void)
2032 while (r_shadow_worldlightchain)
2033 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2034 r_shadow_selectedlight = NULL;
2037 void R_Shadow_SelectLight(worldlight_t *light)
2039 if (r_shadow_selectedlight)
2040 r_shadow_selectedlight->selected = false;
2041 r_shadow_selectedlight = light;
2042 if (r_shadow_selectedlight)
2043 r_shadow_selectedlight->selected = true;
2046 rtexture_t *lighttextures[5];
2048 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2050 float scale = r_editlights_cursorgrid.value * 0.5f;
2051 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2054 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2057 const worldlight_t *light;
2060 if (light->selected)
2061 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2062 if (!light->shadowvolume)
2064 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2067 void R_Shadow_DrawLightSprites(void)
2071 worldlight_t *light;
2073 for (i = 0;i < 5;i++)
2075 lighttextures[i] = NULL;
2076 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2077 lighttextures[i] = pic->tex;
2080 for (light = r_shadow_worldlightchain;light;light = light->next)
2081 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2082 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2085 void R_Shadow_SelectLightInView(void)
2087 float bestrating, rating, temp[3];
2088 worldlight_t *best, *light;
2091 for (light = r_shadow_worldlightchain;light;light = light->next)
2093 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2094 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2097 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2098 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2100 bestrating = rating;
2105 R_Shadow_SelectLight(best);
2108 void R_Shadow_LoadWorldLights(void)
2110 int n, a, style, shadow;
2111 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2112 float origin[3], radius, color[3];
2113 if (cl.worldmodel == NULL)
2115 Con_Printf("No map loaded.\n");
2118 FS_StripExtension(cl.worldmodel->name, name);
2119 strcat(name, ".rtlights");
2120 lightsstring = FS_LoadFile(name, false);
2128 while (*s && *s != '\n')
2134 // check for modifier flags
2140 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);
2146 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);
2149 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2150 radius *= r_editlights_rtlightssizescale.value;
2151 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2156 Con_Printf("invalid rtlights file \"%s\"\n", name);
2157 Mem_Free(lightsstring);
2161 void R_Shadow_SaveWorldLights(void)
2163 worldlight_t *light;
2164 int bufchars, bufmaxchars;
2166 char name[MAX_QPATH];
2168 if (!r_shadow_worldlightchain)
2170 if (cl.worldmodel == NULL)
2172 Con_Printf("No map loaded.\n");
2175 FS_StripExtension(cl.worldmodel->name, name);
2176 strcat(name, ".rtlights");
2177 bufchars = bufmaxchars = 0;
2179 for (light = r_shadow_worldlightchain;light;light = light->next)
2181 sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2182 if (bufchars + (int) strlen(line) > bufmaxchars)
2184 bufmaxchars = bufchars + strlen(line) + 2048;
2186 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2190 memcpy(buf, oldbuf, bufchars);
2196 memcpy(buf + bufchars, line, strlen(line));
2197 bufchars += strlen(line);
2201 FS_WriteFile(name, buf, bufchars);
2206 void R_Shadow_LoadLightsFile(void)
2209 char name[MAX_QPATH], *lightsstring, *s, *t;
2210 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2211 if (cl.worldmodel == NULL)
2213 Con_Printf("No map loaded.\n");
2216 FS_StripExtension(cl.worldmodel->name, name);
2217 strcat(name, ".lights");
2218 lightsstring = FS_LoadFile(name, false);
2226 while (*s && *s != '\n')
2231 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);
2235 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);
2238 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2239 radius = bound(15, radius, 4096);
2240 VectorScale(color, (2.0f / (8388608.0f)), color);
2241 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2246 Con_Printf("invalid lights file \"%s\"\n", name);
2247 Mem_Free(lightsstring);
2251 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2253 int entnum, style, islight;
2254 char key[256], value[1024];
2255 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2258 if (cl.worldmodel == NULL)
2260 Con_Printf("No map loaded.\n");
2263 data = cl.worldmodel->brush.entities;
2266 for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2269 origin[0] = origin[1] = origin[2] = 0;
2270 originhack[0] = originhack[1] = originhack[2] = 0;
2271 color[0] = color[1] = color[2] = 1;
2272 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2278 if (!COM_ParseToken(&data))
2280 if (com_token[0] == '}')
2281 break; // end of entity
2282 if (com_token[0] == '_')
2283 strcpy(key, com_token + 1);
2285 strcpy(key, com_token);
2286 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2287 key[strlen(key)-1] = 0;
2288 if (!COM_ParseToken(&data))
2290 strcpy(value, com_token);
2292 // now that we have the key pair worked out...
2293 if (!strcmp("light", key))
2294 light = atof(value);
2295 else if (!strcmp("origin", key))
2296 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2297 else if (!strcmp("color", key))
2298 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2299 else if (!strcmp("wait", key))
2300 scale = atof(value);
2301 else if (!strcmp("classname", key))
2303 if (!strncmp(value, "light", 5))
2306 if (!strcmp(value, "light_fluoro"))
2311 overridecolor[0] = 1;
2312 overridecolor[1] = 1;
2313 overridecolor[2] = 1;
2315 if (!strcmp(value, "light_fluorospark"))
2320 overridecolor[0] = 1;
2321 overridecolor[1] = 1;
2322 overridecolor[2] = 1;
2324 if (!strcmp(value, "light_globe"))
2329 overridecolor[0] = 1;
2330 overridecolor[1] = 0.8;
2331 overridecolor[2] = 0.4;
2333 if (!strcmp(value, "light_flame_large_yellow"))
2338 overridecolor[0] = 1;
2339 overridecolor[1] = 0.5;
2340 overridecolor[2] = 0.1;
2342 if (!strcmp(value, "light_flame_small_yellow"))
2347 overridecolor[0] = 1;
2348 overridecolor[1] = 0.5;
2349 overridecolor[2] = 0.1;
2351 if (!strcmp(value, "light_torch_small_white"))
2356 overridecolor[0] = 1;
2357 overridecolor[1] = 0.5;
2358 overridecolor[2] = 0.1;
2360 if (!strcmp(value, "light_torch_small_walltorch"))
2365 overridecolor[0] = 1;
2366 overridecolor[1] = 0.5;
2367 overridecolor[2] = 0.1;
2371 else if (!strcmp("style", key))
2372 style = atoi(value);
2374 if (light <= 0 && islight)
2376 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2377 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2378 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2379 VectorCopy(overridecolor, color);
2380 VectorScale(color, light, color);
2381 VectorAdd(origin, originhack, origin);
2383 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2388 void R_Shadow_SetCursorLocationForView(void)
2390 vec_t dist, push, frac;
2391 vec3_t dest, endpos, normal;
2392 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2393 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2396 dist = frac * r_editlights_cursordistance.value;
2397 push = r_editlights_cursorpushback.value;
2401 VectorMA(endpos, push, vpn, endpos);
2402 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2404 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2405 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2406 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2409 void R_Shadow_UpdateWorldLightSelection(void)
2411 R_Shadow_SetCursorLocationForView();
2412 if (r_editlights.integer)
2414 R_Shadow_SelectLightInView();
2415 R_Shadow_DrawLightSprites();
2418 R_Shadow_SelectLight(NULL);
2421 void R_Shadow_EditLights_Clear_f(void)
2423 R_Shadow_ClearWorldLights();
2426 void R_Shadow_EditLights_Reload_f(void)
2428 r_shadow_reloadlights = true;
2431 void R_Shadow_EditLights_Save_f(void)
2434 R_Shadow_SaveWorldLights();
2437 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2439 R_Shadow_ClearWorldLights();
2440 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2443 void R_Shadow_EditLights_ImportLightsFile_f(void)
2445 R_Shadow_ClearWorldLights();
2446 R_Shadow_LoadLightsFile();
2449 void R_Shadow_EditLights_Spawn_f(void)
2452 if (!r_editlights.integer)
2454 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2457 if (Cmd_Argc() != 1)
2459 Con_Printf("r_editlights_spawn does not take parameters\n");
2462 color[0] = color[1] = color[2] = 1;
2463 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2466 void R_Shadow_EditLights_Edit_f(void)
2468 vec3_t origin, color;
2471 char cubemapname[1024];
2472 if (!r_editlights.integer)
2474 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2477 if (!r_shadow_selectedlight)
2479 Con_Printf("No selected light.\n");
2482 VectorCopy(r_shadow_selectedlight->origin, origin);
2483 radius = r_shadow_selectedlight->lightradius;
2484 VectorCopy(r_shadow_selectedlight->light, color);
2485 style = r_shadow_selectedlight->style;
2486 if (r_shadow_selectedlight->cubemapname)
2487 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2490 shadows = r_shadow_selectedlight->castshadows;
2491 if (!strcmp(Cmd_Argv(1), "origin"))
2493 if (Cmd_Argc() != 5)
2495 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2498 origin[0] = atof(Cmd_Argv(2));
2499 origin[1] = atof(Cmd_Argv(3));
2500 origin[2] = atof(Cmd_Argv(4));
2502 else if (!strcmp(Cmd_Argv(1), "originx"))
2504 if (Cmd_Argc() != 3)
2506 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2509 origin[0] = atof(Cmd_Argv(2));
2511 else if (!strcmp(Cmd_Argv(1), "originy"))
2513 if (Cmd_Argc() != 3)
2515 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2518 origin[1] = atof(Cmd_Argv(2));
2520 else if (!strcmp(Cmd_Argv(1), "originz"))
2522 if (Cmd_Argc() != 3)
2524 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2527 origin[2] = atof(Cmd_Argv(2));
2529 else if (!strcmp(Cmd_Argv(1), "move"))
2531 if (Cmd_Argc() != 5)
2533 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2536 origin[0] += atof(Cmd_Argv(2));
2537 origin[1] += atof(Cmd_Argv(3));
2538 origin[2] += atof(Cmd_Argv(4));
2540 else if (!strcmp(Cmd_Argv(1), "movex"))
2542 if (Cmd_Argc() != 3)
2544 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2547 origin[0] += atof(Cmd_Argv(2));
2549 else if (!strcmp(Cmd_Argv(1), "movey"))
2551 if (Cmd_Argc() != 3)
2553 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2556 origin[1] += atof(Cmd_Argv(2));
2558 else if (!strcmp(Cmd_Argv(1), "movez"))
2560 if (Cmd_Argc() != 3)
2562 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2565 origin[2] += atof(Cmd_Argv(2));
2567 else if (!strcmp(Cmd_Argv(1), "color"))
2569 if (Cmd_Argc() != 5)
2571 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2574 color[0] = atof(Cmd_Argv(2));
2575 color[1] = atof(Cmd_Argv(3));
2576 color[2] = atof(Cmd_Argv(4));
2578 else if (!strcmp(Cmd_Argv(1), "radius"))
2580 if (Cmd_Argc() != 3)
2582 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2585 radius = atof(Cmd_Argv(2));
2587 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2589 if (Cmd_Argc() != 3)
2591 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2594 style = atoi(Cmd_Argv(2));
2596 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2600 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2603 if (Cmd_Argc() == 3)
2604 strcpy(cubemapname, Cmd_Argv(2));
2608 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2610 if (Cmd_Argc() != 3)
2612 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2615 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2619 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2620 Con_Printf("Selected light's properties:\n");
2621 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2622 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2623 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2624 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2625 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2626 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2629 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2630 r_shadow_selectedlight = NULL;
2631 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2634 extern int con_vislines;
2635 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2639 if (r_shadow_selectedlight == NULL)
2643 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2644 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;
2645 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2646 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;
2647 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2648 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2649 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;
2652 void R_Shadow_EditLights_ToggleShadow_f(void)
2654 if (!r_editlights.integer)
2656 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2659 if (!r_shadow_selectedlight)
2661 Con_Printf("No selected light.\n");
2664 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);
2665 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2666 r_shadow_selectedlight = NULL;
2669 void R_Shadow_EditLights_Remove_f(void)
2671 if (!r_editlights.integer)
2673 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2676 if (!r_shadow_selectedlight)
2678 Con_Printf("No selected light.\n");
2681 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2682 r_shadow_selectedlight = NULL;
2685 void R_Shadow_EditLights_Init(void)
2687 Cvar_RegisterVariable(&r_editlights);
2688 Cvar_RegisterVariable(&r_editlights_cursordistance);
2689 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2690 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2691 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2692 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2693 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2694 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2695 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2696 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2697 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2698 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2699 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2700 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2701 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2702 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2703 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);