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"};
160 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
162 int c_rt_lights, c_rt_clears, c_rt_scissored;
163 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
164 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
166 void R_Shadow_ClearWorldLights(void);
167 void R_Shadow_SaveWorldLights(void);
168 void R_Shadow_LoadWorldLights(void);
169 void R_Shadow_LoadLightsFile(void);
170 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
172 void r_shadow_start(void)
174 // allocate vertex processing arrays
175 r_shadow_mempool = Mem_AllocPool("R_Shadow");
176 maxshadowelements = 0;
177 shadowelements = NULL;
182 maxtrianglefacinglight = 0;
183 trianglefacinglight = NULL;
184 trianglefacinglightlist = NULL;
185 r_shadow_normalcubetexture = NULL;
186 r_shadow_attenuation2dtexture = NULL;
187 r_shadow_attenuation3dtexture = NULL;
188 r_shadow_blankbumptexture = NULL;
189 r_shadow_blankglosstexture = NULL;
190 r_shadow_blankwhitetexture = NULL;
191 r_shadow_texturepool = NULL;
192 R_Shadow_ClearWorldLights();
193 r_shadow_reloadlights = true;
196 void r_shadow_shutdown(void)
198 R_Shadow_ClearWorldLights();
199 r_shadow_reloadlights = true;
200 r_shadow_normalcubetexture = NULL;
201 r_shadow_attenuation2dtexture = NULL;
202 r_shadow_attenuation3dtexture = NULL;
203 r_shadow_blankbumptexture = NULL;
204 r_shadow_blankglosstexture = NULL;
205 r_shadow_blankwhitetexture = NULL;
206 R_FreeTexturePool(&r_shadow_texturepool);
207 maxshadowelements = 0;
208 shadowelements = NULL;
213 maxtrianglefacinglight = 0;
214 trianglefacinglight = NULL;
215 trianglefacinglightlist = NULL;
216 Mem_FreePool(&r_shadow_mempool);
219 void r_shadow_newmap(void)
221 R_Shadow_ClearWorldLights();
222 r_shadow_reloadlights = true;
225 void R_Shadow_Help_f(void)
228 "Documentation on r_shadow system:\n"
230 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
231 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
232 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
233 "r_shadow_realtime_world : use realtime world light rendering\n"
234 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
235 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
236 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
237 "r_shadow_glossintensity : brightness of textured gloss\n"
238 "r_shadow_gloss2intensity : brightness of forced gloss\n"
239 "r_shadow_debuglight : render only this light number (-1 = all)\n"
240 "r_shadow_scissor : use scissor optimization\n"
241 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
242 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
243 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
244 "r_shadow_portallight : use portal visibility for static light precomputation\n"
245 "r_shadow_projectdistance : shadow volume projection distance\n"
246 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
247 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
248 "r_shadow_shadows : dlight shadows (world always has shadows)\n"
250 "r_shadow_help : this help\n"
254 void R_Shadow_Init(void)
256 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
257 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
258 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
259 Cvar_RegisterVariable(&r_shadow_realtime_world);
260 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
261 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
262 Cvar_RegisterVariable(&r_shadow_gloss);
263 Cvar_RegisterVariable(&r_shadow_glossintensity);
264 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
265 Cvar_RegisterVariable(&r_shadow_debuglight);
266 Cvar_RegisterVariable(&r_shadow_scissor);
267 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
268 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
269 Cvar_RegisterVariable(&r_shadow_polygonoffset);
270 Cvar_RegisterVariable(&r_shadow_portallight);
271 Cvar_RegisterVariable(&r_shadow_projectdistance);
272 Cvar_RegisterVariable(&r_shadow_texture3d);
273 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
274 Cvar_RegisterVariable(&r_shadow_shadows);
275 Cvar_RegisterVariable(&r_shadow_showtris);
276 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
277 R_Shadow_EditLights_Init();
278 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
281 void R_Shadow_ResizeTriangleFacingLight(int numtris)
283 // make sure trianglefacinglight is big enough for this volume
284 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
285 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
286 if (maxtrianglefacinglight < numtris)
288 maxtrianglefacinglight = numtris;
289 if (trianglefacinglight)
290 Mem_Free(trianglefacinglight);
291 if (trianglefacinglightlist)
292 Mem_Free(trianglefacinglightlist);
293 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
294 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
298 int *R_Shadow_ResizeShadowElements(int numtris)
300 // make sure shadowelements is big enough for this volume
301 if (maxshadowelements < numtris * 24)
303 maxshadowelements = numtris * 24;
305 Mem_Free(shadowelements);
306 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
308 return shadowelements;
312 // readable version of some code found below
313 //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]))))
314 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
316 float dir0[3], dir1[3], normal[3];
318 // calculate two mostly perpendicular edge directions
319 VectorSubtract(a, b, dir0);
320 VectorSubtract(c, b, dir1);
322 // we have two edge directions, we can calculate a third vector from
323 // them, which is the direction of the surface normal (it's magnitude
325 CrossProduct(dir0, dir1, normal);
327 // compare distance of light along normal, with distance of any point
328 // of the triangle along the same normal (the triangle is planar,
329 // I.E. flat, so all points give the same answer)
330 return DotProduct(p, normal) > DotProduct(a, normal);
332 int checkcastshadowfromedge(int t, int i)
336 if (t >= trianglerange_start && t < trianglerange_end)
338 if (t < i && !trianglefacinglight[t])
349 te = inelement3i + t * 3;
350 v[0] = invertex3f + te[0] * 3;
351 v[1] = invertex3f + te[1] * 3;
352 v[2] = invertex3f + te[2] * 3;
353 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
362 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)
364 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
366 const int *e, *n, *te;
369 // make sure trianglefacinglight is big enough for this volume
370 if (maxtrianglefacinglight < trianglerange_end)
371 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
373 if (maxvertexupdate < innumvertices)
375 maxvertexupdate = innumvertices;
377 Mem_Free(vertexupdate);
379 Mem_Free(vertexremap);
380 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
381 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
385 if (r_shadow_singlepassvolumegeneration.integer)
387 // one pass approach (identify lit/dark faces and generate sides while doing so)
388 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
390 // calculate triangle facing flag
391 v[0] = invertex3f + e[0] * 3;
392 v[1] = invertex3f + e[1] * 3;
393 v[2] = invertex3f + e[2] * 3;
394 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
396 // make sure the vertices are created
397 for (j = 0;j < 3;j++)
399 if (vertexupdate[e[j]] != vertexupdatenum)
401 vertexupdate[e[j]] = vertexupdatenum;
402 vertexremap[e[j]] = outvertices;
403 VectorCopy(v[j], outvertex3f);
404 VectorSubtract(v[j], relativelightorigin, temp);
405 f = projectdistance / VectorLength(temp);
406 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
411 // output the front and back triangles
412 vr[0] = vertexremap[e[0]];
413 vr[1] = vertexremap[e[1]];
414 vr[2] = vertexremap[e[2]];
415 outelement3i[0] = vr[0];
416 outelement3i[1] = vr[1];
417 outelement3i[2] = vr[2];
418 outelement3i[3] = vr[2] + 1;
419 outelement3i[4] = vr[1] + 1;
420 outelement3i[5] = vr[0] + 1;
423 // output the sides (facing outward from this triangle)
425 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]))))
427 outelement3i[0] = vr[1];
428 outelement3i[1] = vr[0];
429 outelement3i[2] = vr[0] + 1;
430 outelement3i[3] = vr[1];
431 outelement3i[4] = vr[0] + 1;
432 outelement3i[5] = vr[1] + 1;
437 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]))))
439 outelement3i[0] = vr[2];
440 outelement3i[1] = vr[1];
441 outelement3i[2] = vr[1] + 1;
442 outelement3i[3] = vr[2];
443 outelement3i[4] = vr[1] + 1;
444 outelement3i[5] = vr[2] + 1;
449 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]))))
451 outelement3i[0] = vr[0];
452 outelement3i[1] = vr[2];
453 outelement3i[2] = vr[2] + 1;
454 outelement3i[3] = vr[0];
455 outelement3i[4] = vr[2] + 1;
456 outelement3i[5] = vr[0] + 1;
463 // this triangle is not facing the light
464 // output the sides (facing inward to this triangle)
466 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
468 vr[0] = vertexremap[e[0]];
469 vr[1] = vertexremap[e[1]];
470 outelement3i[0] = vr[1];
471 outelement3i[1] = vr[0] + 1;
472 outelement3i[2] = vr[0];
473 outelement3i[3] = vr[1];
474 outelement3i[4] = vr[1] + 1;
475 outelement3i[5] = vr[0] + 1;
480 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
482 vr[1] = vertexremap[e[1]];
483 vr[2] = vertexremap[e[2]];
484 outelement3i[0] = vr[2];
485 outelement3i[1] = vr[1] + 1;
486 outelement3i[2] = vr[1];
487 outelement3i[3] = vr[2];
488 outelement3i[4] = vr[2] + 1;
489 outelement3i[5] = vr[1] + 1;
494 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
496 vr[0] = vertexremap[e[0]];
497 vr[2] = vertexremap[e[2]];
498 outelement3i[0] = vr[0];
499 outelement3i[1] = vr[2] + 1;
500 outelement3i[2] = vr[2];
501 outelement3i[3] = vr[0];
502 outelement3i[4] = vr[0] + 1;
503 outelement3i[5] = vr[2] + 1;
512 // two pass approach (identify lit/dark faces and then generate sides)
513 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
515 // calculate triangle facing flag
516 v[0] = invertex3f + e[0] * 3;
517 v[1] = invertex3f + e[1] * 3;
518 v[2] = invertex3f + e[2] * 3;
519 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
521 trianglefacinglightlist[numfacing++] = i;
522 // make sure the vertices are created
523 for (j = 0;j < 3;j++)
525 if (vertexupdate[e[j]] != vertexupdatenum)
527 vertexupdate[e[j]] = vertexupdatenum;
528 vertexremap[e[j]] = outvertices;
529 VectorSubtract(v[j], relativelightorigin, temp);
530 f = projectdistance / VectorLength(temp);
531 VectorCopy(v[j], outvertex3f);
532 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
537 // output the front and back triangles
538 outelement3i[0] = vertexremap[e[0]];
539 outelement3i[1] = vertexremap[e[1]];
540 outelement3i[2] = vertexremap[e[2]];
541 outelement3i[3] = vertexremap[e[2]] + 1;
542 outelement3i[4] = vertexremap[e[1]] + 1;
543 outelement3i[5] = vertexremap[e[0]] + 1;
548 for (i = 0;i < numfacing;i++)
550 t = trianglefacinglightlist[i];
551 e = inelement3i + t * 3;
552 n = inneighbor3i + t * 3;
553 // output the sides (facing outward from this triangle)
555 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]))))
557 vr[0] = vertexremap[e[0]];
558 vr[1] = vertexremap[e[1]];
559 outelement3i[0] = vr[1];
560 outelement3i[1] = vr[0];
561 outelement3i[2] = vr[0] + 1;
562 outelement3i[3] = vr[1];
563 outelement3i[4] = vr[0] + 1;
564 outelement3i[5] = vr[1] + 1;
569 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]))))
571 vr[1] = vertexremap[e[1]];
572 vr[2] = vertexremap[e[2]];
573 outelement3i[0] = vr[2];
574 outelement3i[1] = vr[1];
575 outelement3i[2] = vr[1] + 1;
576 outelement3i[3] = vr[2];
577 outelement3i[4] = vr[1] + 1;
578 outelement3i[5] = vr[2] + 1;
583 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]))))
585 vr[0] = vertexremap[e[0]];
586 vr[2] = vertexremap[e[2]];
587 outelement3i[0] = vr[0];
588 outelement3i[1] = vr[2];
589 outelement3i[2] = vr[2] + 1;
590 outelement3i[3] = vr[0];
591 outelement3i[4] = vr[2] + 1;
592 outelement3i[5] = vr[0] + 1;
599 *outnumvertices = outvertices;
603 float varray_vertex3f2[65536*3];
605 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
608 if (projectdistance < 0.1)
610 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
616 // make sure shadowelements is big enough for this volume
617 if (maxshadowelements < numtris * 24)
618 R_Shadow_ResizeShadowElements(numtris);
620 // check which triangles are facing the light, and then output
621 // triangle elements and vertices... by clever use of elements we
622 // can construct the whole shadow from the unprojected vertices and
623 // the projected vertices
624 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
626 GL_VertexPointer(varray_vertex3f2);
627 if (r_shadowstage == SHADOWSTAGE_STENCIL)
629 // increment stencil if backface is behind depthbuffer
630 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
631 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
632 R_Mesh_Draw(outverts, tris, shadowelements);
634 c_rt_shadowtris += numtris;
635 // decrement stencil if frontface is behind depthbuffer
636 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
637 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
639 R_Mesh_Draw(outverts, tris, shadowelements);
641 c_rt_shadowtris += numtris;
645 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
648 if (r_shadowstage == SHADOWSTAGE_STENCIL)
650 // increment stencil if backface is behind depthbuffer
651 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
652 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
653 for (mesh = firstmesh;mesh;mesh = mesh->next)
655 GL_VertexPointer(mesh->vertex3f);
656 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
657 c_rtcached_shadowmeshes++;
658 c_rtcached_shadowtris += mesh->numtriangles;
660 // decrement stencil if frontface is behind depthbuffer
661 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
662 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
664 for (mesh = firstmesh;mesh;mesh = mesh->next)
666 GL_VertexPointer(mesh->vertex3f);
667 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
668 c_rtcached_shadowmeshes++;
669 c_rtcached_shadowtris += mesh->numtriangles;
673 float r_shadow_attenpower, r_shadow_attenscale;
674 static void R_Shadow_MakeTextures(void)
676 int x, y, z, d, side;
677 float v[3], s, t, intensity;
679 R_FreeTexturePool(&r_shadow_texturepool);
680 r_shadow_texturepool = R_AllocTexturePool();
681 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
682 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
684 #define ATTEN2DSIZE 64
685 #define ATTEN3DSIZE 32
686 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
691 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
696 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
701 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
702 if (gl_texturecubemap)
704 for (side = 0;side < 6;side++)
706 for (y = 0;y < NORMSIZE;y++)
708 for (x = 0;x < NORMSIZE;x++)
710 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
711 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
745 intensity = 127.0f / sqrt(DotProduct(v, v));
746 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
747 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
748 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
749 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
753 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
756 r_shadow_normalcubetexture = NULL;
757 for (y = 0;y < ATTEN2DSIZE;y++)
759 for (x = 0;x < ATTEN2DSIZE;x++)
761 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
762 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
764 intensity = 1.0f - sqrt(DotProduct(v, v));
766 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
767 d = bound(0, intensity, 255);
768 data[(y*ATTEN2DSIZE+x)*4+0] = d;
769 data[(y*ATTEN2DSIZE+x)*4+1] = d;
770 data[(y*ATTEN2DSIZE+x)*4+2] = d;
771 data[(y*ATTEN2DSIZE+x)*4+3] = d;
774 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
775 if (r_shadow_texture3d.integer)
777 for (z = 0;z < ATTEN3DSIZE;z++)
779 for (y = 0;y < ATTEN3DSIZE;y++)
781 for (x = 0;x < ATTEN3DSIZE;x++)
783 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
784 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
785 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
786 intensity = 1.0f - sqrt(DotProduct(v, v));
788 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
789 d = bound(0, intensity, 255);
790 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
791 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
792 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
793 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
797 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
802 void R_Shadow_Stage_Begin(void)
806 if (r_shadow_texture3d.integer && !gl_texture3d)
807 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
809 if (!r_shadow_attenuation2dtexture
810 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
811 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
812 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
813 R_Shadow_MakeTextures();
815 memset(&m, 0, sizeof(m));
816 GL_BlendFunc(GL_ONE, GL_ZERO);
819 R_Mesh_State_Texture(&m);
820 GL_Color(0, 0, 0, 1);
821 qglDisable(GL_SCISSOR_TEST);
822 r_shadowstage = SHADOWSTAGE_NONE;
824 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
825 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
826 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
829 void R_Shadow_LoadWorldLightsIfNeeded(void)
831 if (r_shadow_reloadlights && cl.worldmodel)
833 R_Shadow_ClearWorldLights();
834 r_shadow_reloadlights = false;
835 R_Shadow_LoadWorldLights();
836 if (r_shadow_worldlightchain == NULL)
838 R_Shadow_LoadLightsFile();
839 if (r_shadow_worldlightchain == NULL)
840 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
845 void R_Shadow_Stage_ShadowVolumes(void)
848 memset(&m, 0, sizeof(m));
849 R_Mesh_State_Texture(&m);
850 GL_Color(1, 1, 1, 1);
851 qglColorMask(0, 0, 0, 0);
852 GL_BlendFunc(GL_ONE, GL_ZERO);
855 if (r_shadow_polygonoffset.value != 0)
857 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
858 qglEnable(GL_POLYGON_OFFSET_FILL);
861 qglDisable(GL_POLYGON_OFFSET_FILL);
862 qglDepthFunc(GL_LESS);
863 qglEnable(GL_STENCIL_TEST);
864 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
865 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
866 r_shadowstage = SHADOWSTAGE_STENCIL;
867 qglClear(GL_STENCIL_BUFFER_BIT);
869 // LordHavoc note: many shadow volumes reside entirely inside the world
870 // (that is to say they are entirely bounded by their lit surfaces),
871 // which can be optimized by handling things as an inverted light volume,
872 // with the shadow boundaries of the world being simulated by an altered
873 // (129) bias to stencil clearing on such lights
874 // FIXME: generate inverted light volumes for use as shadow volumes and
875 // optimize for them as noted above
878 void R_Shadow_Stage_LightWithoutShadows(void)
881 memset(&m, 0, sizeof(m));
882 R_Mesh_State_Texture(&m);
883 GL_BlendFunc(GL_ONE, GL_ONE);
886 qglDisable(GL_POLYGON_OFFSET_FILL);
887 GL_Color(1, 1, 1, 1);
888 qglColorMask(1, 1, 1, 1);
889 qglDepthFunc(GL_EQUAL);
890 qglDisable(GL_STENCIL_TEST);
891 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
892 qglStencilFunc(GL_EQUAL, 128, 0xFF);
893 r_shadowstage = SHADOWSTAGE_LIGHT;
897 void R_Shadow_Stage_LightWithShadows(void)
900 memset(&m, 0, sizeof(m));
901 R_Mesh_State_Texture(&m);
902 GL_BlendFunc(GL_ONE, GL_ONE);
905 qglDisable(GL_POLYGON_OFFSET_FILL);
906 GL_Color(1, 1, 1, 1);
907 qglColorMask(1, 1, 1, 1);
908 qglDepthFunc(GL_EQUAL);
909 qglEnable(GL_STENCIL_TEST);
910 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
911 // only draw light where this geometry was already rendered AND the
912 // stencil is 128 (values other than this mean shadow)
913 qglStencilFunc(GL_EQUAL, 128, 0xFF);
914 r_shadowstage = SHADOWSTAGE_LIGHT;
918 void R_Shadow_Stage_End(void)
921 memset(&m, 0, sizeof(m));
922 R_Mesh_State_Texture(&m);
923 GL_BlendFunc(GL_ONE, GL_ZERO);
926 qglDisable(GL_POLYGON_OFFSET_FILL);
927 GL_Color(1, 1, 1, 1);
928 qglColorMask(1, 1, 1, 1);
929 qglDisable(GL_SCISSOR_TEST);
930 qglDepthFunc(GL_LEQUAL);
931 qglDisable(GL_STENCIL_TEST);
932 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
933 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
934 r_shadowstage = SHADOWSTAGE_NONE;
937 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
939 int i, ix1, iy1, ix2, iy2;
940 float x1, y1, x2, y2, x, y, f;
943 if (!r_shadow_scissor.integer)
945 // if view is inside the box, just say yes it's visible
946 // LordHavoc: for some odd reason scissor seems broken without stencil
947 // (?!? seems like a driver bug) so abort if gl_stencil is false
948 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
950 qglDisable(GL_SCISSOR_TEST);
953 for (i = 0;i < 3;i++)
955 if (r_viewforward[i] >= 0)
966 f = DotProduct(r_viewforward, r_vieworigin) + 1;
967 if (DotProduct(r_viewforward, v2) <= f)
969 // entirely behind nearclip plane
972 if (DotProduct(r_viewforward, v) >= f)
974 // entirely infront of nearclip plane
975 x1 = y1 = x2 = y2 = 0;
976 for (i = 0;i < 8;i++)
978 v[0] = (i & 1) ? mins[0] : maxs[0];
979 v[1] = (i & 2) ? mins[1] : maxs[1];
980 v[2] = (i & 4) ? mins[2] : maxs[2];
982 GL_TransformToScreen(v, v2);
983 //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]);
1002 // clipped by nearclip plane
1003 // this is nasty and crude...
1004 // create viewspace bbox
1005 for (i = 0;i < 8;i++)
1007 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1008 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1009 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1010 v2[0] = -DotProduct(v, r_viewleft);
1011 v2[1] = DotProduct(v, r_viewup);
1012 v2[2] = DotProduct(v, r_viewforward);
1015 if (smins[0] > v2[0]) smins[0] = v2[0];
1016 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1017 if (smins[1] > v2[1]) smins[1] = v2[1];
1018 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1019 if (smins[2] > v2[2]) smins[2] = v2[2];
1020 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1024 smins[0] = smaxs[0] = v2[0];
1025 smins[1] = smaxs[1] = v2[1];
1026 smins[2] = smaxs[2] = v2[2];
1029 // now we have a bbox in viewspace
1030 // clip it to the view plane
1033 // return true if that culled the box
1034 if (smins[2] >= smaxs[2])
1036 // ok some of it is infront of the view, transform each corner back to
1037 // worldspace and then to screenspace and make screen rect
1038 // initialize these variables just to avoid compiler warnings
1039 x1 = y1 = x2 = y2 = 0;
1040 for (i = 0;i < 8;i++)
1042 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1043 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1044 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1045 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1046 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1047 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1049 GL_TransformToScreen(v, v2);
1050 //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]);
1067 // this code doesn't handle boxes with any points behind view properly
1068 x1 = 1000;x2 = -1000;
1069 y1 = 1000;y2 = -1000;
1070 for (i = 0;i < 8;i++)
1072 v[0] = (i & 1) ? mins[0] : maxs[0];
1073 v[1] = (i & 2) ? mins[1] : maxs[1];
1074 v[2] = (i & 4) ? mins[2] : maxs[2];
1076 GL_TransformToScreen(v, v2);
1077 //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]);
1095 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1096 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1097 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1098 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1099 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1100 if (ix2 <= ix1 || iy2 <= iy1)
1102 // set up the scissor rectangle
1103 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1104 qglEnable(GL_SCISSOR_TEST);
1109 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1111 float *color4f = varray_color4f;
1112 float dist, dot, intensity, v[3], n[3];
1113 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1115 Matrix4x4_Transform(m, vertex3f, v);
1116 if ((dist = DotProduct(v, v)) < 1)
1118 Matrix4x4_Transform3x3(m, normal3f, n);
1119 if ((dot = DotProduct(n, v)) > 0)
1122 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1123 VectorScale(lightcolor, intensity, color4f);
1128 VectorClear(color4f);
1134 VectorClear(color4f);
1140 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1142 float *color4f = varray_color4f;
1143 float dist, dot, intensity, v[3], n[3];
1144 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1146 Matrix4x4_Transform(m, vertex3f, v);
1147 if ((dist = fabs(v[2])) < 1)
1149 Matrix4x4_Transform3x3(m, normal3f, n);
1150 if ((dot = DotProduct(n, v)) > 0)
1152 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1153 VectorScale(lightcolor, intensity, color4f);
1158 VectorClear(color4f);
1164 VectorClear(color4f);
1170 // FIXME: this should be done in a vertex program when possible
1171 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1172 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1176 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1177 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1178 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1185 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1189 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1190 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1197 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)
1201 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1203 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1204 // the cubemap normalizes this for us
1205 out3f[0] = DotProduct(svector3f, lightdir);
1206 out3f[1] = DotProduct(tvector3f, lightdir);
1207 out3f[2] = DotProduct(normal3f, lightdir);
1211 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)
1214 float lightdir[3], eyedir[3], halfdir[3];
1215 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1217 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1218 VectorNormalizeFast(lightdir);
1219 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1220 VectorNormalizeFast(eyedir);
1221 VectorAdd(lightdir, eyedir, halfdir);
1222 // the cubemap normalizes this for us
1223 out3f[0] = DotProduct(svector3f, halfdir);
1224 out3f[1] = DotProduct(tvector3f, halfdir);
1225 out3f[2] = DotProduct(normal3f, halfdir);
1229 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)
1232 float color[3], color2[3];
1234 GL_VertexPointer(vertex3f);
1235 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1238 bumptexture = r_shadow_blankbumptexture;
1240 // colorscale accounts for how much we multiply the brightness during combine
1241 // mult is how many times the final pass of the lighting will be
1242 // performed to get more brightness than otherwise possible
1243 // limit mult to 64 for sanity sake
1244 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1246 // 3/2 3D combine path (Geforce3, Radeon 8500)
1247 memset(&m, 0, sizeof(m));
1248 m.tex[0] = R_GetTexture(bumptexture);
1249 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1250 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1251 m.texcombinergb[0] = GL_REPLACE;
1252 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1253 m.pointer_texcoord[0] = texcoord2f;
1254 m.pointer_texcoord[1] = varray_texcoord3f[1];
1255 m.pointer_texcoord[2] = varray_texcoord3f[2];
1256 R_Mesh_State_Texture(&m);
1257 qglColorMask(0,0,0,1);
1258 GL_BlendFunc(GL_ONE, GL_ZERO);
1259 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1260 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1261 R_Mesh_Draw(numverts, numtriangles, elements);
1263 c_rt_lighttris += numtriangles;
1265 memset(&m, 0, sizeof(m));
1266 m.tex[0] = R_GetTexture(basetexture);
1267 m.texcubemap[1] = R_GetTexture(lightcubemap);
1268 m.pointer_texcoord[0] = texcoord2f;
1269 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1270 R_Mesh_State_Texture(&m);
1271 qglColorMask(1,1,1,0);
1272 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1274 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1275 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1276 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1278 color[0] = bound(0, color2[0], 1);
1279 color[1] = bound(0, color2[1], 1);
1280 color[2] = bound(0, color2[2], 1);
1281 GL_Color(color[0], color[1], color[2], 1);
1282 R_Mesh_Draw(numverts, numtriangles, elements);
1284 c_rt_lighttris += numtriangles;
1287 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1289 // 1/2/2 3D combine path (original Radeon)
1290 memset(&m, 0, sizeof(m));
1291 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1292 m.pointer_texcoord[0] = varray_texcoord3f[0];
1293 R_Mesh_State_Texture(&m);
1294 qglColorMask(0,0,0,1);
1295 GL_BlendFunc(GL_ONE, GL_ZERO);
1296 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1297 R_Mesh_Draw(numverts, numtriangles, elements);
1299 c_rt_lighttris += numtriangles;
1301 memset(&m, 0, sizeof(m));
1302 m.tex[0] = R_GetTexture(bumptexture);
1303 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1304 m.texcombinergb[0] = GL_REPLACE;
1305 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1306 m.pointer_texcoord[0] = texcoord2f;
1307 m.pointer_texcoord[1] = varray_texcoord3f[1];
1308 R_Mesh_State_Texture(&m);
1309 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1310 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1311 R_Mesh_Draw(numverts, numtriangles, elements);
1313 c_rt_lighttris += numtriangles;
1315 memset(&m, 0, sizeof(m));
1316 m.tex[0] = R_GetTexture(basetexture);
1317 m.texcubemap[1] = R_GetTexture(lightcubemap);
1318 m.pointer_texcoord[0] = texcoord2f;
1319 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1320 R_Mesh_State_Texture(&m);
1321 qglColorMask(1,1,1,0);
1322 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1324 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1325 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1326 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1328 color[0] = bound(0, color2[0], 1);
1329 color[1] = bound(0, color2[1], 1);
1330 color[2] = bound(0, color2[2], 1);
1331 GL_Color(color[0], color[1], color[2], 1);
1332 R_Mesh_Draw(numverts, numtriangles, elements);
1334 c_rt_lighttris += numtriangles;
1337 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1339 // 2/2 3D combine path (original Radeon)
1340 memset(&m, 0, sizeof(m));
1341 m.tex[0] = R_GetTexture(bumptexture);
1342 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1343 m.texcombinergb[0] = GL_REPLACE;
1344 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1345 m.pointer_texcoord[0] = texcoord2f;
1346 m.pointer_texcoord[1] = varray_texcoord3f[1];
1347 R_Mesh_State_Texture(&m);
1348 qglColorMask(0,0,0,1);
1349 GL_BlendFunc(GL_ONE, GL_ZERO);
1350 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1351 R_Mesh_Draw(numverts, numtriangles, elements);
1353 c_rt_lighttris += numtriangles;
1355 memset(&m, 0, sizeof(m));
1356 m.tex[0] = R_GetTexture(basetexture);
1357 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1358 m.pointer_texcoord[0] = texcoord2f;
1359 m.pointer_texcoord[1] = varray_texcoord3f[1];
1360 R_Mesh_State_Texture(&m);
1361 qglColorMask(1,1,1,0);
1362 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1363 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1364 VectorScale(lightcolor, 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_textureunits.integer >= 4)
1378 // 4/2 2D combine path (Geforce3, Radeon 8500)
1379 memset(&m, 0, sizeof(m));
1380 m.tex[0] = R_GetTexture(bumptexture);
1381 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1382 m.texcombinergb[0] = GL_REPLACE;
1383 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1384 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1385 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1386 m.pointer_texcoord[0] = texcoord2f;
1387 m.pointer_texcoord[1] = varray_texcoord3f[1];
1388 m.pointer_texcoord[2] = varray_texcoord2f[2];
1389 m.pointer_texcoord[3] = varray_texcoord2f[3];
1390 R_Mesh_State_Texture(&m);
1391 qglColorMask(0,0,0,1);
1392 GL_BlendFunc(GL_ONE, GL_ZERO);
1393 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1394 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1395 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1396 R_Mesh_Draw(numverts, numtriangles, elements);
1398 c_rt_lighttris += numtriangles;
1400 memset(&m, 0, sizeof(m));
1401 m.tex[0] = R_GetTexture(basetexture);
1402 m.texcubemap[1] = R_GetTexture(lightcubemap);
1403 m.pointer_texcoord[0] = texcoord2f;
1404 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1405 R_Mesh_State_Texture(&m);
1406 qglColorMask(1,1,1,0);
1407 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1409 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1410 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1411 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1413 color[0] = bound(0, color2[0], 1);
1414 color[1] = bound(0, color2[1], 1);
1415 color[2] = bound(0, color2[2], 1);
1416 GL_Color(color[0], color[1], color[2], 1);
1417 R_Mesh_Draw(numverts, numtriangles, elements);
1419 c_rt_lighttris += numtriangles;
1424 // 2/2/2 2D combine path (any dot3 card)
1425 memset(&m, 0, sizeof(m));
1426 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1427 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1428 m.pointer_texcoord[0] = varray_texcoord2f[0];
1429 m.pointer_texcoord[1] = varray_texcoord2f[1];
1430 R_Mesh_State_Texture(&m);
1431 qglColorMask(0,0,0,1);
1432 GL_BlendFunc(GL_ONE, GL_ZERO);
1433 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1434 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1435 R_Mesh_Draw(numverts, numtriangles, elements);
1437 c_rt_lighttris += numtriangles;
1439 memset(&m, 0, sizeof(m));
1440 m.tex[0] = R_GetTexture(bumptexture);
1441 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1442 m.texcombinergb[0] = GL_REPLACE;
1443 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1444 m.pointer_texcoord[0] = texcoord2f;
1445 m.pointer_texcoord[1] = varray_texcoord3f[1];
1446 R_Mesh_State_Texture(&m);
1447 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1448 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1449 R_Mesh_Draw(numverts, numtriangles, elements);
1451 c_rt_lighttris += numtriangles;
1453 memset(&m, 0, sizeof(m));
1454 m.tex[0] = R_GetTexture(basetexture);
1455 m.texcubemap[1] = R_GetTexture(lightcubemap);
1456 m.pointer_texcoord[0] = texcoord2f;
1457 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1458 R_Mesh_State_Texture(&m);
1459 qglColorMask(1,1,1,0);
1460 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1462 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1463 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1464 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1466 color[0] = bound(0, color2[0], 1);
1467 color[1] = bound(0, color2[1], 1);
1468 color[2] = bound(0, color2[2], 1);
1469 GL_Color(color[0], color[1], color[2], 1);
1470 R_Mesh_Draw(numverts, numtriangles, elements);
1472 c_rt_lighttris += numtriangles;
1478 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1479 GL_DepthMask(false);
1481 GL_ColorPointer(varray_color4f);
1482 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1483 memset(&m, 0, sizeof(m));
1484 m.tex[0] = R_GetTexture(basetexture);
1485 m.pointer_texcoord[0] = texcoord2f;
1486 if (r_textureunits.integer >= 2)
1489 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1490 m.pointer_texcoord[1] = varray_texcoord2f[1];
1491 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1493 R_Mesh_State_Texture(&m);
1494 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1496 color[0] = bound(0, color2[0], 1);
1497 color[1] = bound(0, color2[1], 1);
1498 color[2] = bound(0, color2[2], 1);
1499 if (r_textureunits.integer >= 2)
1500 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1502 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1503 R_Mesh_Draw(numverts, numtriangles, elements);
1505 c_rt_lighttris += numtriangles;
1510 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)
1513 float color[3], color2[3], colorscale;
1515 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1518 glosstexture = r_shadow_blankglosstexture;
1519 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1521 colorscale = r_shadow_glossintensity.value;
1523 bumptexture = r_shadow_blankbumptexture;
1524 if (glosstexture == r_shadow_blankglosstexture)
1525 colorscale *= r_shadow_gloss2intensity.value;
1526 GL_VertexPointer(vertex3f);
1528 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1530 // 2/0/0/1/2 3D combine blendsquare path
1531 memset(&m, 0, sizeof(m));
1532 m.tex[0] = R_GetTexture(bumptexture);
1533 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1534 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1535 m.pointer_texcoord[0] = texcoord2f;
1536 m.pointer_texcoord[1] = varray_texcoord3f[1];
1537 R_Mesh_State_Texture(&m);
1538 qglColorMask(0,0,0,1);
1539 // this squares the result
1540 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1541 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1542 R_Mesh_Draw(numverts, numtriangles, elements);
1544 c_rt_lighttris += numtriangles;
1546 memset(&m, 0, sizeof(m));
1547 R_Mesh_State_Texture(&m);
1548 // square alpha in framebuffer a few times to make it shiny
1549 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1550 // these comments are a test run through this math for intensity 0.5
1551 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1552 // 0.25 * 0.25 = 0.0625 (this is another pass)
1553 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1554 R_Mesh_Draw(numverts, numtriangles, elements);
1556 c_rt_lighttris += numtriangles;
1557 R_Mesh_Draw(numverts, numtriangles, elements);
1559 c_rt_lighttris += numtriangles;
1561 memset(&m, 0, sizeof(m));
1562 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1563 m.pointer_texcoord[0] = varray_texcoord3f[0];
1564 R_Mesh_State_Texture(&m);
1565 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1566 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1567 R_Mesh_Draw(numverts, numtriangles, elements);
1569 c_rt_lighttris += numtriangles;
1571 memset(&m, 0, sizeof(m));
1572 m.tex[0] = R_GetTexture(glosstexture);
1573 m.texcubemap[1] = R_GetTexture(lightcubemap);
1574 m.pointer_texcoord[0] = texcoord2f;
1575 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1576 R_Mesh_State_Texture(&m);
1577 qglColorMask(1,1,1,0);
1578 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1580 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1581 VectorScale(lightcolor, colorscale, color2);
1582 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1584 color[0] = bound(0, color2[0], 1);
1585 color[1] = bound(0, color2[1], 1);
1586 color[2] = bound(0, color2[2], 1);
1587 GL_Color(color[0], color[1], color[2], 1);
1588 R_Mesh_Draw(numverts, numtriangles, elements);
1590 c_rt_lighttris += numtriangles;
1593 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1595 // 2/0/0/2 3D combine blendsquare path
1596 memset(&m, 0, sizeof(m));
1597 m.tex[0] = R_GetTexture(bumptexture);
1598 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1599 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1600 m.pointer_texcoord[0] = texcoord2f;
1601 m.pointer_texcoord[1] = varray_texcoord3f[1];
1602 R_Mesh_State_Texture(&m);
1603 qglColorMask(0,0,0,1);
1604 // this squares the result
1605 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1606 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1607 R_Mesh_Draw(numverts, numtriangles, elements);
1609 c_rt_lighttris += numtriangles;
1611 memset(&m, 0, sizeof(m));
1612 R_Mesh_State_Texture(&m);
1613 // square alpha in framebuffer a few times to make it shiny
1614 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1615 // these comments are a test run through this math for intensity 0.5
1616 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1617 // 0.25 * 0.25 = 0.0625 (this is another pass)
1618 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1619 R_Mesh_Draw(numverts, numtriangles, elements);
1621 c_rt_lighttris += numtriangles;
1622 R_Mesh_Draw(numverts, numtriangles, elements);
1624 c_rt_lighttris += numtriangles;
1626 memset(&m, 0, sizeof(m));
1627 m.tex[0] = R_GetTexture(glosstexture);
1628 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1629 m.pointer_texcoord[0] = texcoord2f;
1630 m.pointer_texcoord[1] = varray_texcoord3f[1];
1631 R_Mesh_State_Texture(&m);
1632 qglColorMask(1,1,1,0);
1633 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1634 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1635 VectorScale(lightcolor, colorscale, color2);
1636 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1638 color[0] = bound(0, color2[0], 1);
1639 color[1] = bound(0, color2[1], 1);
1640 color[2] = bound(0, color2[2], 1);
1641 GL_Color(color[0], color[1], color[2], 1);
1642 R_Mesh_Draw(numverts, numtriangles, elements);
1644 c_rt_lighttris += numtriangles;
1647 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1649 // 2/0/0/2/2 2D combine blendsquare path
1650 memset(&m, 0, sizeof(m));
1651 m.tex[0] = R_GetTexture(bumptexture);
1652 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1653 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1654 m.pointer_texcoord[0] = texcoord2f;
1655 m.pointer_texcoord[1] = varray_texcoord3f[1];
1656 R_Mesh_State_Texture(&m);
1657 qglColorMask(0,0,0,1);
1658 // this squares the result
1659 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1660 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1661 R_Mesh_Draw(numverts, numtriangles, elements);
1663 c_rt_lighttris += numtriangles;
1665 memset(&m, 0, sizeof(m));
1666 R_Mesh_State_Texture(&m);
1667 // square alpha in framebuffer a few times to make it shiny
1668 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1669 // these comments are a test run through this math for intensity 0.5
1670 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1671 // 0.25 * 0.25 = 0.0625 (this is another pass)
1672 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1673 R_Mesh_Draw(numverts, numtriangles, elements);
1675 c_rt_lighttris += numtriangles;
1676 R_Mesh_Draw(numverts, numtriangles, elements);
1678 c_rt_lighttris += numtriangles;
1680 memset(&m, 0, sizeof(m));
1681 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1682 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1683 m.pointer_texcoord[0] = varray_texcoord2f[0];
1684 m.pointer_texcoord[1] = varray_texcoord2f[1];
1685 R_Mesh_State_Texture(&m);
1686 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1687 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1688 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1689 R_Mesh_Draw(numverts, numtriangles, elements);
1691 c_rt_lighttris += numtriangles;
1693 memset(&m, 0, sizeof(m));
1694 m.tex[0] = R_GetTexture(glosstexture);
1695 m.texcubemap[1] = R_GetTexture(lightcubemap);
1696 m.pointer_texcoord[0] = texcoord2f;
1697 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1698 R_Mesh_State_Texture(&m);
1699 qglColorMask(1,1,1,0);
1700 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1702 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1703 VectorScale(lightcolor, colorscale, color2);
1704 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1706 color[0] = bound(0, color2[0], 1);
1707 color[1] = bound(0, color2[1], 1);
1708 color[2] = bound(0, color2[2], 1);
1709 GL_Color(color[0], color[1], color[2], 1);
1710 R_Mesh_Draw(numverts, numtriangles, elements);
1712 c_rt_lighttris += numtriangles;
1718 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1720 R_Mesh_Matrix(matrix);
1721 if (r_shadow_showtris.integer)
1725 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1726 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1727 qglDisable(GL_DEPTH_TEST);
1728 qglDisable(GL_STENCIL_TEST);
1729 //qglDisable(GL_CULL_FACE);
1730 qglColorMask(1,1,1,1);
1731 memset(&m, 0, sizeof(m));
1732 R_Mesh_State_Texture(&m);
1733 GL_Color(0,0.1,0,1);
1734 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1735 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1737 GL_VertexPointer(mesh->vertex3f);
1738 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1740 //qglEnable(GL_CULL_FACE);
1742 qglEnable(GL_DEPTH_TEST);
1745 qglEnable(GL_STENCIL_TEST);
1746 qglColorMask(0,0,0,0);
1749 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1752 void R_Shadow_DrawStaticWorldLight_Light(worldlight_t *light, matrix4x4_t *matrix, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz)
1755 R_Mesh_Matrix(matrix);
1756 if (r_shadow_showtris.integer)
1759 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1760 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1761 qglDisable(GL_DEPTH_TEST);
1762 qglDisable(GL_STENCIL_TEST);
1763 //qglDisable(GL_CULL_FACE);
1764 memset(&m, 0, sizeof(m));
1765 R_Mesh_State_Texture(&m);
1766 GL_Color(0.2,0,0,1);
1767 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1768 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1770 GL_VertexPointer(mesh->vertex3f);
1771 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1773 //qglEnable(GL_CULL_FACE);
1775 qglEnable(GL_DEPTH_TEST);
1777 qglEnable(GL_STENCIL_TEST);
1779 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1781 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, NULL);
1782 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltofilter, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, NULL);
1786 cvar_t r_editlights = {0, "r_editlights", "0"};
1787 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1788 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1789 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1790 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1791 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1792 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1793 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1794 worldlight_t *r_shadow_worldlightchain;
1795 worldlight_t *r_shadow_selectedlight;
1796 vec3_t r_editlights_cursorlocation;
1798 static int lightpvsbytes;
1799 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1801 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1803 int i, j, k, l, maxverts = 256, tris;
1804 float *vertex3f = NULL, mins[3], maxs[3];
1806 shadowmesh_t *mesh, *castmesh = NULL;
1808 if (radius < 15 || DotProduct(color, color) < 0.03)
1810 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1814 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1815 VectorCopy(origin, e->origin);
1816 VectorCopy(color, e->light);
1817 e->lightradius = radius;
1819 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1821 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1824 e->castshadows = castshadow;
1826 e->cullradius = e->lightradius;
1827 for (k = 0;k < 3;k++)
1829 mins[k] = e->origin[k] - e->lightradius;
1830 maxs[k] = e->origin[k] + e->lightradius;
1833 e->next = r_shadow_worldlightchain;
1834 r_shadow_worldlightchain = e;
1835 if (cubemapname && cubemapname[0])
1837 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1838 strcpy(e->cubemapname, cubemapname);
1839 // FIXME: add cubemap loading (and don't load a cubemap twice)
1841 // FIXME: rewrite this to store ALL geometry into a cache in the light
1843 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1844 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1847 if (cl.worldmodel->brushq3.num_leafs)
1851 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1852 VectorCopy(e->origin, e->mins);
1853 VectorCopy(e->origin, e->maxs);
1854 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1855 face->lighttemp_castshadow = false;
1856 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1858 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1860 for (k = 0;k < 3;k++)
1862 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1863 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1865 for (j = 0;j < leaf->numleaffaces;j++)
1867 face = leaf->firstleafface[j];
1868 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1869 face->lighttemp_castshadow = true;
1874 // add surfaces to shadow casting mesh and light mesh
1875 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1877 if (face->lighttemp_castshadow)
1879 face->lighttemp_castshadow = false;
1880 if (!(face->texture->renderflags & (Q3MTEXTURERENDERFLAGS_NODRAW | Q3MTEXTURERENDERFLAGS_SKY)))
1883 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1884 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1885 if (!(face->texture->renderflags & (Q3MTEXTURERENDERFLAGS_SKY)))
1886 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1891 else if (cl.worldmodel->brushq1.numleafs)
1895 VectorCopy(e->origin, e->mins);
1896 VectorCopy(e->origin, e->maxs);
1897 i = CL_PointQ1Contents(e->origin);
1899 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1900 surf->lighttemp_castshadow = false;
1902 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1905 qbyte *bytesurfacepvs;
1907 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1908 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1910 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1912 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1914 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1916 for (k = 0;k < 3;k++)
1918 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1919 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1924 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1925 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1926 surf->lighttemp_castshadow = true;
1928 Mem_Free(byteleafpvs);
1929 Mem_Free(bytesurfacepvs);
1933 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1934 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1936 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1938 for (k = 0;k < 3;k++)
1940 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1941 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1943 for (j = 0;j < leaf->nummarksurfaces;j++)
1945 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1946 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1947 surf->lighttemp_castshadow = true;
1953 // add surfaces to shadow casting mesh and light mesh
1954 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1956 if (surf->lighttemp_castshadow)
1958 surf->lighttemp_castshadow = false;
1959 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1960 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
1961 if (!(surf->flags & SURF_DRAWSKY))
1962 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1968 // limit box to light bounds (in case it grew larger)
1969 for (k = 0;k < 3;k++)
1971 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1972 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1974 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1976 // cast shadow volume from castmesh
1977 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1981 for (mesh = castmesh;mesh;mesh = mesh->next)
1983 R_Shadow_ResizeShadowElements(mesh->numtriangles);
1984 maxverts = max(maxverts, mesh->numverts * 2);
1989 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1990 // now that we have the buffers big enough, construct and add
1991 // the shadow volume mesh
1993 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1994 for (mesh = castmesh;mesh;mesh = mesh->next)
1996 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1997 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)))
1998 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2003 // we're done with castmesh now
2004 Mod_ShadowMesh_Free(castmesh);
2007 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2008 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2011 if (e->meshchain_shadow)
2012 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2013 k += mesh->numtriangles;
2015 if (e->meshchain_light)
2016 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2017 l += mesh->numtriangles;
2018 Con_Printf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], k, l);
2021 void R_Shadow_FreeWorldLight(worldlight_t *light)
2023 worldlight_t **lightpointer;
2024 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2025 if (*lightpointer != light)
2026 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2027 *lightpointer = light->next;
2028 if (light->cubemapname)
2029 Mem_Free(light->cubemapname);
2030 if (light->meshchain_shadow)
2031 Mod_ShadowMesh_Free(light->meshchain_shadow);
2032 if (light->meshchain_light)
2033 Mod_ShadowMesh_Free(light->meshchain_light);
2037 void R_Shadow_ClearWorldLights(void)
2039 while (r_shadow_worldlightchain)
2040 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2041 r_shadow_selectedlight = NULL;
2044 void R_Shadow_SelectLight(worldlight_t *light)
2046 if (r_shadow_selectedlight)
2047 r_shadow_selectedlight->selected = false;
2048 r_shadow_selectedlight = light;
2049 if (r_shadow_selectedlight)
2050 r_shadow_selectedlight->selected = true;
2053 rtexture_t *lighttextures[5];
2055 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2057 float scale = r_editlights_cursorgrid.value * 0.5f;
2058 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2061 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2064 const worldlight_t *light;
2067 if (light->selected)
2068 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2069 if (!light->meshchain_shadow)
2071 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2074 void R_Shadow_DrawLightSprites(void)
2078 worldlight_t *light;
2080 for (i = 0;i < 5;i++)
2082 lighttextures[i] = NULL;
2083 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2084 lighttextures[i] = pic->tex;
2087 for (light = r_shadow_worldlightchain;light;light = light->next)
2088 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2089 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2092 void R_Shadow_SelectLightInView(void)
2094 float bestrating, rating, temp[3];
2095 worldlight_t *best, *light;
2098 for (light = r_shadow_worldlightchain;light;light = light->next)
2100 VectorSubtract(light->origin, r_vieworigin, temp);
2101 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2104 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2105 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2107 bestrating = rating;
2112 R_Shadow_SelectLight(best);
2115 void R_Shadow_LoadWorldLights(void)
2117 int n, a, style, shadow;
2118 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2119 float origin[3], radius, color[3];
2120 if (cl.worldmodel == NULL)
2122 Con_Printf("No map loaded.\n");
2125 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2126 strlcat (name, ".rtlights", sizeof (name));
2127 lightsstring = FS_LoadFile(name, false);
2135 while (*s && *s != '\n')
2141 // check for modifier flags
2147 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);
2153 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);
2156 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2157 radius *= r_editlights_rtlightssizescale.value;
2158 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2163 Con_Printf("invalid rtlights file \"%s\"\n", name);
2164 Mem_Free(lightsstring);
2168 void R_Shadow_SaveWorldLights(void)
2170 worldlight_t *light;
2171 int bufchars, bufmaxchars;
2173 char name[MAX_QPATH];
2175 if (!r_shadow_worldlightchain)
2177 if (cl.worldmodel == NULL)
2179 Con_Printf("No map loaded.\n");
2182 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2183 strlcat (name, ".rtlights", sizeof (name));
2184 bufchars = bufmaxchars = 0;
2186 for (light = r_shadow_worldlightchain;light;light = light->next)
2188 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 : "");
2189 if (bufchars + (int) strlen(line) > bufmaxchars)
2191 bufmaxchars = bufchars + strlen(line) + 2048;
2193 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2197 memcpy(buf, oldbuf, bufchars);
2203 memcpy(buf + bufchars, line, strlen(line));
2204 bufchars += strlen(line);
2208 FS_WriteFile(name, buf, bufchars);
2213 void R_Shadow_LoadLightsFile(void)
2216 char name[MAX_QPATH], *lightsstring, *s, *t;
2217 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2218 if (cl.worldmodel == NULL)
2220 Con_Printf("No map loaded.\n");
2223 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2224 strlcat (name, ".lights", sizeof (name));
2225 lightsstring = FS_LoadFile(name, false);
2233 while (*s && *s != '\n')
2238 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);
2242 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);
2245 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2246 radius = bound(15, radius, 4096);
2247 VectorScale(color, (2.0f / (8388608.0f)), color);
2248 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2253 Con_Printf("invalid lights file \"%s\"\n", name);
2254 Mem_Free(lightsstring);
2258 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2260 int entnum, style, islight;
2261 char key[256], value[1024];
2262 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2265 if (cl.worldmodel == NULL)
2267 Con_Printf("No map loaded.\n");
2270 data = cl.worldmodel->brush.entities;
2273 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2276 origin[0] = origin[1] = origin[2] = 0;
2277 originhack[0] = originhack[1] = originhack[2] = 0;
2278 color[0] = color[1] = color[2] = 1;
2279 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2285 if (!COM_ParseToken(&data, false))
2287 if (com_token[0] == '}')
2288 break; // end of entity
2289 if (com_token[0] == '_')
2290 strcpy(key, com_token + 1);
2292 strcpy(key, com_token);
2293 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2294 key[strlen(key)-1] = 0;
2295 if (!COM_ParseToken(&data, false))
2297 strcpy(value, com_token);
2299 // now that we have the key pair worked out...
2300 if (!strcmp("light", key))
2301 light = atof(value);
2302 else if (!strcmp("origin", key))
2303 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2304 else if (!strcmp("color", key))
2305 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2306 else if (!strcmp("wait", key))
2307 scale = atof(value);
2308 else if (!strcmp("classname", key))
2310 if (!strncmp(value, "light", 5))
2313 if (!strcmp(value, "light_fluoro"))
2318 overridecolor[0] = 1;
2319 overridecolor[1] = 1;
2320 overridecolor[2] = 1;
2322 if (!strcmp(value, "light_fluorospark"))
2327 overridecolor[0] = 1;
2328 overridecolor[1] = 1;
2329 overridecolor[2] = 1;
2331 if (!strcmp(value, "light_globe"))
2336 overridecolor[0] = 1;
2337 overridecolor[1] = 0.8;
2338 overridecolor[2] = 0.4;
2340 if (!strcmp(value, "light_flame_large_yellow"))
2345 overridecolor[0] = 1;
2346 overridecolor[1] = 0.5;
2347 overridecolor[2] = 0.1;
2349 if (!strcmp(value, "light_flame_small_yellow"))
2354 overridecolor[0] = 1;
2355 overridecolor[1] = 0.5;
2356 overridecolor[2] = 0.1;
2358 if (!strcmp(value, "light_torch_small_white"))
2363 overridecolor[0] = 1;
2364 overridecolor[1] = 0.5;
2365 overridecolor[2] = 0.1;
2367 if (!strcmp(value, "light_torch_small_walltorch"))
2372 overridecolor[0] = 1;
2373 overridecolor[1] = 0.5;
2374 overridecolor[2] = 0.1;
2378 else if (!strcmp("style", key))
2379 style = atoi(value);
2381 if (light <= 0 && islight)
2383 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2384 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2385 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2386 VectorCopy(overridecolor, color);
2387 VectorScale(color, light, color);
2388 VectorAdd(origin, originhack, origin);
2390 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2395 void R_Shadow_SetCursorLocationForView(void)
2397 vec_t dist, push, frac;
2398 vec3_t dest, endpos, normal;
2399 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2400 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2403 dist = frac * r_editlights_cursordistance.value;
2404 push = r_editlights_cursorpushback.value;
2408 VectorMA(endpos, push, r_viewforward, endpos);
2409 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2411 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2412 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2413 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2416 void R_Shadow_UpdateWorldLightSelection(void)
2418 if (r_editlights.integer)
2420 R_Shadow_SetCursorLocationForView();
2421 R_Shadow_SelectLightInView();
2422 R_Shadow_DrawLightSprites();
2425 R_Shadow_SelectLight(NULL);
2428 void R_Shadow_EditLights_Clear_f(void)
2430 R_Shadow_ClearWorldLights();
2433 void R_Shadow_EditLights_Reload_f(void)
2435 r_shadow_reloadlights = true;
2438 void R_Shadow_EditLights_Save_f(void)
2441 R_Shadow_SaveWorldLights();
2444 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2446 R_Shadow_ClearWorldLights();
2447 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2450 void R_Shadow_EditLights_ImportLightsFile_f(void)
2452 R_Shadow_ClearWorldLights();
2453 R_Shadow_LoadLightsFile();
2456 void R_Shadow_EditLights_Spawn_f(void)
2459 if (!r_editlights.integer)
2461 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2464 if (Cmd_Argc() != 1)
2466 Con_Printf("r_editlights_spawn does not take parameters\n");
2469 color[0] = color[1] = color[2] = 1;
2470 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2473 void R_Shadow_EditLights_Edit_f(void)
2475 vec3_t origin, color;
2478 char cubemapname[1024];
2479 if (!r_editlights.integer)
2481 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2484 if (!r_shadow_selectedlight)
2486 Con_Printf("No selected light.\n");
2489 VectorCopy(r_shadow_selectedlight->origin, origin);
2490 radius = r_shadow_selectedlight->lightradius;
2491 VectorCopy(r_shadow_selectedlight->light, color);
2492 style = r_shadow_selectedlight->style;
2493 if (r_shadow_selectedlight->cubemapname)
2494 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2497 shadows = r_shadow_selectedlight->castshadows;
2498 if (!strcmp(Cmd_Argv(1), "origin"))
2500 if (Cmd_Argc() != 5)
2502 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2505 origin[0] = atof(Cmd_Argv(2));
2506 origin[1] = atof(Cmd_Argv(3));
2507 origin[2] = atof(Cmd_Argv(4));
2509 else if (!strcmp(Cmd_Argv(1), "originx"))
2511 if (Cmd_Argc() != 3)
2513 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2516 origin[0] = atof(Cmd_Argv(2));
2518 else if (!strcmp(Cmd_Argv(1), "originy"))
2520 if (Cmd_Argc() != 3)
2522 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2525 origin[1] = atof(Cmd_Argv(2));
2527 else if (!strcmp(Cmd_Argv(1), "originz"))
2529 if (Cmd_Argc() != 3)
2531 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2534 origin[2] = atof(Cmd_Argv(2));
2536 else if (!strcmp(Cmd_Argv(1), "move"))
2538 if (Cmd_Argc() != 5)
2540 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2543 origin[0] += atof(Cmd_Argv(2));
2544 origin[1] += atof(Cmd_Argv(3));
2545 origin[2] += atof(Cmd_Argv(4));
2547 else if (!strcmp(Cmd_Argv(1), "movex"))
2549 if (Cmd_Argc() != 3)
2551 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2554 origin[0] += atof(Cmd_Argv(2));
2556 else if (!strcmp(Cmd_Argv(1), "movey"))
2558 if (Cmd_Argc() != 3)
2560 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2563 origin[1] += atof(Cmd_Argv(2));
2565 else if (!strcmp(Cmd_Argv(1), "movez"))
2567 if (Cmd_Argc() != 3)
2569 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2572 origin[2] += atof(Cmd_Argv(2));
2574 else if (!strcmp(Cmd_Argv(1), "color"))
2576 if (Cmd_Argc() != 5)
2578 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2581 color[0] = atof(Cmd_Argv(2));
2582 color[1] = atof(Cmd_Argv(3));
2583 color[2] = atof(Cmd_Argv(4));
2585 else if (!strcmp(Cmd_Argv(1), "radius"))
2587 if (Cmd_Argc() != 3)
2589 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2592 radius = atof(Cmd_Argv(2));
2594 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2596 if (Cmd_Argc() != 3)
2598 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2601 style = atoi(Cmd_Argv(2));
2603 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2607 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2610 if (Cmd_Argc() == 3)
2611 strcpy(cubemapname, Cmd_Argv(2));
2615 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2617 if (Cmd_Argc() != 3)
2619 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2622 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2626 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2627 Con_Printf("Selected light's properties:\n");
2628 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2629 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2630 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2631 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2632 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2633 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2636 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2637 r_shadow_selectedlight = NULL;
2638 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2641 extern int con_vislines;
2642 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2646 if (r_shadow_selectedlight == NULL)
2650 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2651 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;
2652 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2653 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;
2654 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2655 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2656 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;
2659 void R_Shadow_EditLights_ToggleShadow_f(void)
2661 if (!r_editlights.integer)
2663 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2666 if (!r_shadow_selectedlight)
2668 Con_Printf("No selected light.\n");
2671 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);
2672 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2673 r_shadow_selectedlight = NULL;
2676 void R_Shadow_EditLights_Remove_f(void)
2678 if (!r_editlights.integer)
2680 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2683 if (!r_shadow_selectedlight)
2685 Con_Printf("No selected light.\n");
2688 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2689 r_shadow_selectedlight = NULL;
2692 void R_Shadow_EditLights_Help_f(void)
2695 "Documentation on r_editlights system:\n"
2697 "r_editlights : enable/disable editing mode\n"
2698 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2699 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2700 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2701 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2702 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2703 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2704 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2706 "r_editlights_help : this help\n"
2707 "r_editlights_clear : remove all lights\n"
2708 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2709 "r_editlights_save : save to .rtlights file\n"
2710 "r_editlights_spawn : create a light with default settings\n"
2711 "r_editlights_edit command : edit selected light - more documentation below\n"
2712 "r_editlights_remove : remove selected light\n"
2713 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2714 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2715 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2717 "origin x y z : set light location\n"
2718 "originx x: set x component of light location\n"
2719 "originy y: set y component of light location\n"
2720 "originz z: set z component of light location\n"
2721 "move x y z : adjust light location\n"
2722 "movex x: adjust x component of light location\n"
2723 "movey y: adjust y component of light location\n"
2724 "movez z: adjust z component of light location\n"
2725 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2726 "radius radius : set radius (size) of light\n"
2727 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2728 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2729 "shadows 1/0 : turn on/off shadows\n"
2730 "<nothing> : print light properties to console\n"
2734 void R_Shadow_EditLights_Init(void)
2736 Cvar_RegisterVariable(&r_editlights);
2737 Cvar_RegisterVariable(&r_editlights_cursordistance);
2738 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2739 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2740 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2741 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2742 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2743 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2744 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2745 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2746 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2747 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2748 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2749 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2750 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2751 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2752 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2753 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);