3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it).
21 Terminology: Stencil Light Volume (sometimes called Light Volumes)
22 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
23 areas in shadow it contanis the areas in light, this can only be built
24 quickly for certain limited cases (such as portal visibility from a point),
25 but is quite useful for some effects (sunlight coming from sky polygons is
26 one possible example, translucent occluders is another example).
30 Terminology: Optimized Stencil Shadow Volume
31 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
32 no duplicate coverage of areas (no need to shadow an area twice), often this
33 greatly improves performance but is an operation too costly to use on moving
34 lights (however completely optimal Stencil Light Volumes can be constructed
39 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
40 Per pixel evaluation of lighting equations, at a bare minimum this involves
41 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
42 vector and surface normal, using a texture of the surface bumps, called a
43 NormalMap) if supported by hardware; in our case there is support for cards
44 which are incapable of DOT3, the quality is quite poor however. Additionally
45 it is desirable to have specular evaluation per pixel, per vertex
46 normalization of specular halfangle vectors causes noticable distortion but
47 is unavoidable on hardware without GL_ARB_fragment_program.
51 Terminology: Normalization CubeMap
52 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
53 encoded as RGB colors) for any possible direction, this technique allows per
54 pixel calculation of incidence vector for per pixel lighting purposes, which
55 would not otherwise be possible per pixel without GL_ARB_fragment_program.
59 Terminology: 2D Attenuation Texturing
60 A very crude approximation of light attenuation with distance which results
61 in cylindrical light shapes which fade vertically as a streak (some games
62 such as Doom3 allow this to be rotated to be less noticable in specific
63 cases), the technique is simply modulating lighting by two 2D textures (which
64 can be the same) on different axes of projection (XY and Z, typically), this
65 is the best technique available without 3D Attenuation Texturing or
66 GL_ARB_fragment_program technology.
70 Terminology: 3D Attenuation Texturing
71 A slightly crude approximation of light attenuation with distance, its flaws
72 are limited radius and resolution (performance tradeoffs).
76 Terminology: 3D Attenuation-Normalization Texturing
77 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
78 vectors shorter the lighting becomes darker, a very effective optimization of
79 diffuse lighting if 3D Attenuation Textures are already used.
83 Terminology: Light Cubemap Filtering
84 A technique for modeling non-uniform light distribution according to
85 direction, for example projecting a stained glass window image onto a wall,
86 this is done by texturing the lighting with a cubemap.
90 Terminology: Light Projection Filtering
91 A technique for modeling shadowing of light passing through translucent
92 surfaces, allowing stained glass windows and other effects to be done more
93 elegantly than possible with Light Cubemap Filtering by applying an occluder
94 texture to the lighting combined with a stencil light volume to limit the lit
95 area (this allows evaluating multiple translucent occluders in a scene).
99 Terminology: Doom3 Lighting
100 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
101 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
102 the (currently upcoming) game Doom3.
105 #include "quakedef.h"
106 #include "r_shadow.h"
107 #include "cl_collision.h"
110 extern void R_Shadow_EditLights_Init(void);
112 #define SHADOWSTAGE_NONE 0
113 #define SHADOWSTAGE_STENCIL 1
114 #define SHADOWSTAGE_LIGHT 2
115 #define SHADOWSTAGE_ERASESTENCIL 3
117 int r_shadowstage = SHADOWSTAGE_NONE;
118 int r_shadow_reloadlights = false;
120 mempool_t *r_shadow_mempool;
122 int maxshadowelements;
124 int maxtrianglefacinglight;
125 qbyte *trianglefacinglight;
126 int *trianglefacinglightlist;
133 rtexturepool_t *r_shadow_texturepool;
134 rtexture_t *r_shadow_normalcubetexture;
135 rtexture_t *r_shadow_attenuation2dtexture;
136 rtexture_t *r_shadow_attenuation3dtexture;
137 rtexture_t *r_shadow_blankbumptexture;
138 rtexture_t *r_shadow_blankglosstexture;
139 rtexture_t *r_shadow_blankwhitetexture;
141 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
142 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
143 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
144 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
145 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
146 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
147 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
148 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
149 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
150 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
151 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
152 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
153 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
154 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
155 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
156 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
157 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
158 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
159 cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
161 int c_rt_lights, c_rt_clears, c_rt_scissored;
162 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
163 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
165 void R_Shadow_ClearWorldLights(void);
166 void R_Shadow_SaveWorldLights(void);
167 void R_Shadow_LoadWorldLights(void);
168 void R_Shadow_LoadLightsFile(void);
169 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
171 void r_shadow_start(void)
173 // allocate vertex processing arrays
174 r_shadow_mempool = Mem_AllocPool("R_Shadow");
175 maxshadowelements = 0;
176 shadowelements = NULL;
181 maxtrianglefacinglight = 0;
182 trianglefacinglight = NULL;
183 trianglefacinglightlist = NULL;
184 r_shadow_normalcubetexture = NULL;
185 r_shadow_attenuation2dtexture = NULL;
186 r_shadow_attenuation3dtexture = NULL;
187 r_shadow_blankbumptexture = NULL;
188 r_shadow_blankglosstexture = NULL;
189 r_shadow_blankwhitetexture = NULL;
190 r_shadow_texturepool = NULL;
191 R_Shadow_ClearWorldLights();
192 r_shadow_reloadlights = true;
195 void r_shadow_shutdown(void)
197 R_Shadow_ClearWorldLights();
198 r_shadow_reloadlights = true;
199 r_shadow_normalcubetexture = NULL;
200 r_shadow_attenuation2dtexture = NULL;
201 r_shadow_attenuation3dtexture = NULL;
202 r_shadow_blankbumptexture = NULL;
203 r_shadow_blankglosstexture = NULL;
204 r_shadow_blankwhitetexture = NULL;
205 R_FreeTexturePool(&r_shadow_texturepool);
206 maxshadowelements = 0;
207 shadowelements = NULL;
212 maxtrianglefacinglight = 0;
213 trianglefacinglight = NULL;
214 trianglefacinglightlist = NULL;
215 Mem_FreePool(&r_shadow_mempool);
218 void r_shadow_newmap(void)
220 R_Shadow_ClearWorldLights();
221 r_shadow_reloadlights = true;
224 void R_Shadow_Help_f(void)
227 "Documentation on r_shadow system:\n"
229 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
230 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
231 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
232 "r_shadow_realtime_world : use realtime world light rendering\n"
233 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
234 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
235 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
236 "r_shadow_glossintensity : brightness of textured gloss\n"
237 "r_shadow_gloss2intensity : brightness of forced gloss\n"
238 "r_shadow_debuglight : render only this light number (-1 = all)\n"
239 "r_shadow_scissor : use scissor optimization\n"
240 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
241 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
242 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
243 "r_shadow_portallight : use portal visibility for static light precomputation\n"
244 "r_shadow_projectdistance : shadow volume projection distance\n"
245 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
246 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
247 "r_shadow_shadows : dlight shadows (world always has shadows)\n"
249 "r_shadow_help : this help\n"
253 void R_Shadow_Init(void)
255 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
256 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
257 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
258 Cvar_RegisterVariable(&r_shadow_realtime_world);
259 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
260 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
261 Cvar_RegisterVariable(&r_shadow_gloss);
262 Cvar_RegisterVariable(&r_shadow_glossintensity);
263 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
264 Cvar_RegisterVariable(&r_shadow_debuglight);
265 Cvar_RegisterVariable(&r_shadow_scissor);
266 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
267 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
268 Cvar_RegisterVariable(&r_shadow_polygonoffset);
269 Cvar_RegisterVariable(&r_shadow_portallight);
270 Cvar_RegisterVariable(&r_shadow_projectdistance);
271 Cvar_RegisterVariable(&r_shadow_texture3d);
272 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
273 Cvar_RegisterVariable(&r_shadow_shadows);
274 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
275 R_Shadow_EditLights_Init();
276 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
279 void R_Shadow_ResizeTriangleFacingLight(int numtris)
281 // make sure trianglefacinglight is big enough for this volume
282 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
283 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
284 if (maxtrianglefacinglight < numtris)
286 maxtrianglefacinglight = numtris;
287 if (trianglefacinglight)
288 Mem_Free(trianglefacinglight);
289 if (trianglefacinglightlist)
290 Mem_Free(trianglefacinglightlist);
291 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
292 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
296 int *R_Shadow_ResizeShadowElements(int numtris)
298 // make sure shadowelements is big enough for this volume
299 if (maxshadowelements < numtris * 24)
301 maxshadowelements = numtris * 24;
303 Mem_Free(shadowelements);
304 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
306 return shadowelements;
310 // readable version of some code found below
311 //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]))))
312 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
314 float dir0[3], dir1[3], normal[3];
316 // calculate two mostly perpendicular edge directions
317 VectorSubtract(a, b, dir0);
318 VectorSubtract(c, b, dir1);
320 // we have two edge directions, we can calculate a third vector from
321 // them, which is the direction of the surface normal (it's magnitude
323 CrossProduct(dir0, dir1, normal);
325 // compare distance of light along normal, with distance of any point
326 // of the triangle along the same normal (the triangle is planar,
327 // I.E. flat, so all points give the same answer)
328 return DotProduct(p, normal) > DotProduct(a, normal);
330 int checkcastshadowfromedge(int t, int i)
334 if (t >= trianglerange_start && t < trianglerange_end)
336 if (t < i && !trianglefacinglight[t])
347 te = inelement3i + t * 3;
348 v[0] = invertex3f + te[0] * 3;
349 v[1] = invertex3f + te[1] * 3;
350 v[2] = invertex3f + te[2] * 3;
351 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
360 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)
362 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
364 const int *e, *n, *te;
367 // make sure trianglefacinglight is big enough for this volume
368 if (maxtrianglefacinglight < trianglerange_end)
369 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
371 if (maxvertexupdate < innumvertices)
373 maxvertexupdate = innumvertices;
375 Mem_Free(vertexupdate);
377 Mem_Free(vertexremap);
378 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
379 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
383 if (r_shadow_singlepassvolumegeneration.integer)
385 // one pass approach (identify lit/dark faces and generate sides while doing so)
386 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
388 // calculate triangle facing flag
389 v[0] = invertex3f + e[0] * 3;
390 v[1] = invertex3f + e[1] * 3;
391 v[2] = invertex3f + e[2] * 3;
392 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
394 // make sure the vertices are created
395 for (j = 0;j < 3;j++)
397 if (vertexupdate[e[j]] != vertexupdatenum)
399 vertexupdate[e[j]] = vertexupdatenum;
400 vertexremap[e[j]] = outvertices;
401 VectorCopy(v[j], outvertex3f);
402 VectorSubtract(v[j], relativelightorigin, temp);
403 f = projectdistance / VectorLength(temp);
404 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
409 // output the front and back triangles
410 vr[0] = vertexremap[e[0]];
411 vr[1] = vertexremap[e[1]];
412 vr[2] = vertexremap[e[2]];
413 outelement3i[0] = vr[0];
414 outelement3i[1] = vr[1];
415 outelement3i[2] = vr[2];
416 outelement3i[3] = vr[2] + 1;
417 outelement3i[4] = vr[1] + 1;
418 outelement3i[5] = vr[0] + 1;
421 // output the sides (facing outward from this triangle)
423 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]))))
425 outelement3i[0] = vr[1];
426 outelement3i[1] = vr[0];
427 outelement3i[2] = vr[0] + 1;
428 outelement3i[3] = vr[1];
429 outelement3i[4] = vr[0] + 1;
430 outelement3i[5] = vr[1] + 1;
435 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]))))
437 outelement3i[0] = vr[2];
438 outelement3i[1] = vr[1];
439 outelement3i[2] = vr[1] + 1;
440 outelement3i[3] = vr[2];
441 outelement3i[4] = vr[1] + 1;
442 outelement3i[5] = vr[2] + 1;
447 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]))))
449 outelement3i[0] = vr[0];
450 outelement3i[1] = vr[2];
451 outelement3i[2] = vr[2] + 1;
452 outelement3i[3] = vr[0];
453 outelement3i[4] = vr[2] + 1;
454 outelement3i[5] = vr[0] + 1;
461 // this triangle is not facing the light
462 // output the sides (facing inward to this triangle)
464 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
466 vr[0] = vertexremap[e[0]];
467 vr[1] = vertexremap[e[1]];
468 outelement3i[0] = vr[1];
469 outelement3i[1] = vr[0] + 1;
470 outelement3i[2] = vr[0];
471 outelement3i[3] = vr[1];
472 outelement3i[4] = vr[1] + 1;
473 outelement3i[5] = vr[0] + 1;
478 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
480 vr[1] = vertexremap[e[1]];
481 vr[2] = vertexremap[e[2]];
482 outelement3i[0] = vr[2];
483 outelement3i[1] = vr[1] + 1;
484 outelement3i[2] = vr[1];
485 outelement3i[3] = vr[2];
486 outelement3i[4] = vr[2] + 1;
487 outelement3i[5] = vr[1] + 1;
492 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
494 vr[0] = vertexremap[e[0]];
495 vr[2] = vertexremap[e[2]];
496 outelement3i[0] = vr[0];
497 outelement3i[1] = vr[2] + 1;
498 outelement3i[2] = vr[2];
499 outelement3i[3] = vr[0];
500 outelement3i[4] = vr[0] + 1;
501 outelement3i[5] = vr[2] + 1;
510 // two pass approach (identify lit/dark faces and then generate sides)
511 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
513 // calculate triangle facing flag
514 v[0] = invertex3f + e[0] * 3;
515 v[1] = invertex3f + e[1] * 3;
516 v[2] = invertex3f + e[2] * 3;
517 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
519 trianglefacinglightlist[numfacing++] = i;
520 // make sure the vertices are created
521 for (j = 0;j < 3;j++)
523 if (vertexupdate[e[j]] != vertexupdatenum)
525 vertexupdate[e[j]] = vertexupdatenum;
526 vertexremap[e[j]] = outvertices;
527 VectorSubtract(v[j], relativelightorigin, temp);
528 f = projectdistance / VectorLength(temp);
529 VectorCopy(v[j], outvertex3f);
530 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
535 // output the front and back triangles
536 outelement3i[0] = vertexremap[e[0]];
537 outelement3i[1] = vertexremap[e[1]];
538 outelement3i[2] = vertexremap[e[2]];
539 outelement3i[3] = vertexremap[e[2]] + 1;
540 outelement3i[4] = vertexremap[e[1]] + 1;
541 outelement3i[5] = vertexremap[e[0]] + 1;
546 for (i = 0;i < numfacing;i++)
548 t = trianglefacinglightlist[i];
549 e = inelement3i + t * 3;
550 n = inneighbor3i + t * 3;
551 // output the sides (facing outward from this triangle)
553 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]))))
555 vr[0] = vertexremap[e[0]];
556 vr[1] = vertexremap[e[1]];
557 outelement3i[0] = vr[1];
558 outelement3i[1] = vr[0];
559 outelement3i[2] = vr[0] + 1;
560 outelement3i[3] = vr[1];
561 outelement3i[4] = vr[0] + 1;
562 outelement3i[5] = vr[1] + 1;
567 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]))))
569 vr[1] = vertexremap[e[1]];
570 vr[2] = vertexremap[e[2]];
571 outelement3i[0] = vr[2];
572 outelement3i[1] = vr[1];
573 outelement3i[2] = vr[1] + 1;
574 outelement3i[3] = vr[2];
575 outelement3i[4] = vr[1] + 1;
576 outelement3i[5] = vr[2] + 1;
581 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]))))
583 vr[0] = vertexremap[e[0]];
584 vr[2] = vertexremap[e[2]];
585 outelement3i[0] = vr[0];
586 outelement3i[1] = vr[2];
587 outelement3i[2] = vr[2] + 1;
588 outelement3i[3] = vr[0];
589 outelement3i[4] = vr[2] + 1;
590 outelement3i[5] = vr[0] + 1;
597 *outnumvertices = outvertices;
601 float varray_vertex3f2[65536*3];
603 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
606 if (projectdistance < 0.1)
608 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
614 // make sure shadowelements is big enough for this volume
615 if (maxshadowelements < numtris * 24)
616 R_Shadow_ResizeShadowElements(numtris);
618 // check which triangles are facing the light, and then output
619 // triangle elements and vertices... by clever use of elements we
620 // can construct the whole shadow from the unprojected vertices and
621 // the projected vertices
622 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
624 GL_VertexPointer(varray_vertex3f2);
625 if (r_shadowstage == SHADOWSTAGE_STENCIL)
627 // increment stencil if backface is behind depthbuffer
628 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
629 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
630 R_Mesh_Draw(outverts, tris, shadowelements);
632 c_rt_shadowtris += numtris;
633 // decrement stencil if frontface is behind depthbuffer
634 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
635 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
637 R_Mesh_Draw(outverts, tris, shadowelements);
639 c_rt_shadowtris += numtris;
643 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
646 if (r_shadowstage == SHADOWSTAGE_STENCIL)
648 // increment stencil if backface is behind depthbuffer
649 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
650 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
651 for (mesh = firstmesh;mesh;mesh = mesh->next)
653 GL_VertexPointer(mesh->vertex3f);
654 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
655 c_rtcached_shadowmeshes++;
656 c_rtcached_shadowtris += mesh->numtriangles;
658 // decrement stencil if frontface is behind depthbuffer
659 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
660 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
662 for (mesh = firstmesh;mesh;mesh = mesh->next)
664 GL_VertexPointer(mesh->vertex3f);
665 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
666 c_rtcached_shadowmeshes++;
667 c_rtcached_shadowtris += mesh->numtriangles;
671 float r_shadow_attenpower, r_shadow_attenscale;
672 static void R_Shadow_MakeTextures(void)
674 int x, y, z, d, side;
675 float v[3], s, t, intensity;
677 R_FreeTexturePool(&r_shadow_texturepool);
678 r_shadow_texturepool = R_AllocTexturePool();
679 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
680 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
682 #define ATTEN2DSIZE 64
683 #define ATTEN3DSIZE 32
684 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
689 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
694 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
699 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
700 if (gl_texturecubemap)
702 for (side = 0;side < 6;side++)
704 for (y = 0;y < NORMSIZE;y++)
706 for (x = 0;x < NORMSIZE;x++)
708 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
709 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
743 intensity = 127.0f / sqrt(DotProduct(v, v));
744 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
745 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
746 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
747 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
751 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
754 r_shadow_normalcubetexture = NULL;
755 for (y = 0;y < ATTEN2DSIZE;y++)
757 for (x = 0;x < ATTEN2DSIZE;x++)
759 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
760 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
762 intensity = 1.0f - sqrt(DotProduct(v, v));
764 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
765 d = bound(0, intensity, 255);
766 data[(y*ATTEN2DSIZE+x)*4+0] = d;
767 data[(y*ATTEN2DSIZE+x)*4+1] = d;
768 data[(y*ATTEN2DSIZE+x)*4+2] = d;
769 data[(y*ATTEN2DSIZE+x)*4+3] = d;
772 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
773 if (r_shadow_texture3d.integer)
775 for (z = 0;z < ATTEN3DSIZE;z++)
777 for (y = 0;y < ATTEN3DSIZE;y++)
779 for (x = 0;x < ATTEN3DSIZE;x++)
781 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
782 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
783 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
784 intensity = 1.0f - sqrt(DotProduct(v, v));
786 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
787 d = bound(0, intensity, 255);
788 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
789 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
790 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
791 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
795 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
800 void R_Shadow_Stage_Begin(void)
804 if (r_shadow_texture3d.integer && !gl_texture3d)
805 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
807 if (!r_shadow_attenuation2dtexture
808 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
809 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
810 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
811 R_Shadow_MakeTextures();
813 memset(&m, 0, sizeof(m));
814 GL_BlendFunc(GL_ONE, GL_ZERO);
817 R_Mesh_State_Texture(&m);
818 GL_Color(0, 0, 0, 1);
819 qglDisable(GL_SCISSOR_TEST);
820 r_shadowstage = SHADOWSTAGE_NONE;
822 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
823 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
824 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
827 void R_Shadow_LoadWorldLightsIfNeeded(void)
829 if (r_shadow_reloadlights && cl.worldmodel)
831 R_Shadow_ClearWorldLights();
832 r_shadow_reloadlights = false;
833 R_Shadow_LoadWorldLights();
834 if (r_shadow_worldlightchain == NULL)
836 R_Shadow_LoadLightsFile();
837 if (r_shadow_worldlightchain == NULL)
838 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
843 void R_Shadow_Stage_ShadowVolumes(void)
846 memset(&m, 0, sizeof(m));
847 R_Mesh_State_Texture(&m);
848 GL_Color(1, 1, 1, 1);
849 qglColorMask(0, 0, 0, 0);
850 GL_BlendFunc(GL_ONE, GL_ZERO);
853 if (r_shadow_polygonoffset.value != 0)
855 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
856 qglEnable(GL_POLYGON_OFFSET_FILL);
859 qglDisable(GL_POLYGON_OFFSET_FILL);
860 qglDepthFunc(GL_LESS);
861 qglEnable(GL_STENCIL_TEST);
862 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
863 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
864 r_shadowstage = SHADOWSTAGE_STENCIL;
865 qglClear(GL_STENCIL_BUFFER_BIT);
867 // LordHavoc note: many shadow volumes reside entirely inside the world
868 // (that is to say they are entirely bounded by their lit surfaces),
869 // which can be optimized by handling things as an inverted light volume,
870 // with the shadow boundaries of the world being simulated by an altered
871 // (129) bias to stencil clearing on such lights
872 // FIXME: generate inverted light volumes for use as shadow volumes and
873 // optimize for them as noted above
876 void R_Shadow_Stage_LightWithoutShadows(void)
879 memset(&m, 0, sizeof(m));
880 R_Mesh_State_Texture(&m);
881 GL_BlendFunc(GL_ONE, GL_ONE);
884 qglDisable(GL_POLYGON_OFFSET_FILL);
885 GL_Color(1, 1, 1, 1);
886 qglColorMask(1, 1, 1, 1);
887 qglDepthFunc(GL_EQUAL);
888 qglDisable(GL_STENCIL_TEST);
889 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
890 qglStencilFunc(GL_EQUAL, 128, 0xFF);
891 r_shadowstage = SHADOWSTAGE_LIGHT;
895 void R_Shadow_Stage_LightWithShadows(void)
898 memset(&m, 0, sizeof(m));
899 R_Mesh_State_Texture(&m);
900 GL_BlendFunc(GL_ONE, GL_ONE);
903 qglDisable(GL_POLYGON_OFFSET_FILL);
904 GL_Color(1, 1, 1, 1);
905 qglColorMask(1, 1, 1, 1);
906 qglDepthFunc(GL_EQUAL);
907 qglEnable(GL_STENCIL_TEST);
908 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
909 // only draw light where this geometry was already rendered AND the
910 // stencil is 128 (values other than this mean shadow)
911 qglStencilFunc(GL_EQUAL, 128, 0xFF);
912 r_shadowstage = SHADOWSTAGE_LIGHT;
916 void R_Shadow_Stage_End(void)
919 memset(&m, 0, sizeof(m));
920 R_Mesh_State_Texture(&m);
921 GL_BlendFunc(GL_ONE, GL_ZERO);
924 qglDisable(GL_POLYGON_OFFSET_FILL);
925 GL_Color(1, 1, 1, 1);
926 qglColorMask(1, 1, 1, 1);
927 qglDisable(GL_SCISSOR_TEST);
928 qglDepthFunc(GL_LEQUAL);
929 qglDisable(GL_STENCIL_TEST);
930 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
931 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
932 r_shadowstage = SHADOWSTAGE_NONE;
935 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
937 int i, ix1, iy1, ix2, iy2;
938 float x1, y1, x2, y2, x, y, f;
941 if (!r_shadow_scissor.integer)
943 // if view is inside the box, just say yes it's visible
944 // LordHavoc: for some odd reason scissor seems broken without stencil
945 // (?!? seems like a driver bug) so abort if gl_stencil is false
946 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
948 qglDisable(GL_SCISSOR_TEST);
951 for (i = 0;i < 3;i++)
964 f = DotProduct(vpn, r_origin) + 1;
965 if (DotProduct(vpn, v2) <= f)
967 // entirely behind nearclip plane
970 if (DotProduct(vpn, v) >= f)
972 // entirely infront of nearclip plane
973 x1 = y1 = x2 = y2 = 0;
974 for (i = 0;i < 8;i++)
976 v[0] = (i & 1) ? mins[0] : maxs[0];
977 v[1] = (i & 2) ? mins[1] : maxs[1];
978 v[2] = (i & 4) ? mins[2] : maxs[2];
980 GL_TransformToScreen(v, v2);
981 //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]);
1000 // clipped by nearclip plane
1001 // this is nasty and crude...
1002 // create viewspace bbox
1003 for (i = 0;i < 8;i++)
1005 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1006 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1007 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1008 v2[0] = DotProduct(v, vright);
1009 v2[1] = DotProduct(v, vup);
1010 v2[2] = DotProduct(v, vpn);
1013 if (smins[0] > v2[0]) smins[0] = v2[0];
1014 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1015 if (smins[1] > v2[1]) smins[1] = v2[1];
1016 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1017 if (smins[2] > v2[2]) smins[2] = v2[2];
1018 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1022 smins[0] = smaxs[0] = v2[0];
1023 smins[1] = smaxs[1] = v2[1];
1024 smins[2] = smaxs[2] = v2[2];
1027 // now we have a bbox in viewspace
1028 // clip it to the view plane
1031 // return true if that culled the box
1032 if (smins[2] >= smaxs[2])
1034 // ok some of it is infront of the view, transform each corner back to
1035 // worldspace and then to screenspace and make screen rect
1036 // initialize these variables just to avoid compiler warnings
1037 x1 = y1 = x2 = y2 = 0;
1038 for (i = 0;i < 8;i++)
1040 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1041 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1042 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1043 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1044 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1045 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1047 GL_TransformToScreen(v, v2);
1048 //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]);
1065 // this code doesn't handle boxes with any points behind view properly
1066 x1 = 1000;x2 = -1000;
1067 y1 = 1000;y2 = -1000;
1068 for (i = 0;i < 8;i++)
1070 v[0] = (i & 1) ? mins[0] : maxs[0];
1071 v[1] = (i & 2) ? mins[1] : maxs[1];
1072 v[2] = (i & 4) ? mins[2] : maxs[2];
1074 GL_TransformToScreen(v, v2);
1075 //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]);
1093 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1094 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1095 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1096 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1097 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1098 if (ix2 <= ix1 || iy2 <= iy1)
1100 // set up the scissor rectangle
1101 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1102 qglEnable(GL_SCISSOR_TEST);
1107 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1109 float *color4f = varray_color4f;
1110 float dist, dot, intensity, v[3], n[3];
1111 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1113 Matrix4x4_Transform(m, vertex3f, v);
1114 if ((dist = DotProduct(v, v)) < 1)
1116 Matrix4x4_Transform3x3(m, normal3f, n);
1117 if ((dot = DotProduct(n, v)) > 0)
1120 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1121 VectorScale(lightcolor, intensity, color4f);
1126 VectorClear(color4f);
1132 VectorClear(color4f);
1138 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1140 float *color4f = varray_color4f;
1141 float dist, dot, intensity, v[3], n[3];
1142 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1144 Matrix4x4_Transform(m, vertex3f, v);
1145 if ((dist = fabs(v[2])) < 1)
1147 Matrix4x4_Transform3x3(m, normal3f, n);
1148 if ((dot = DotProduct(n, v)) > 0)
1150 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1151 VectorScale(lightcolor, intensity, color4f);
1156 VectorClear(color4f);
1162 VectorClear(color4f);
1168 // FIXME: this should be done in a vertex program when possible
1169 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1170 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1174 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1175 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1176 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1183 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1187 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1188 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1195 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)
1199 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1201 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1202 // the cubemap normalizes this for us
1203 out3f[0] = DotProduct(svector3f, lightdir);
1204 out3f[1] = DotProduct(tvector3f, lightdir);
1205 out3f[2] = DotProduct(normal3f, lightdir);
1209 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)
1212 float lightdir[3], eyedir[3], halfdir[3];
1213 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1215 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1216 VectorNormalizeFast(lightdir);
1217 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1218 VectorNormalizeFast(eyedir);
1219 VectorAdd(lightdir, eyedir, halfdir);
1220 // the cubemap normalizes this for us
1221 out3f[0] = DotProduct(svector3f, halfdir);
1222 out3f[1] = DotProduct(tvector3f, halfdir);
1223 out3f[2] = DotProduct(normal3f, halfdir);
1227 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)
1230 float color[3], color2[3];
1232 GL_VertexPointer(vertex3f);
1233 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1236 bumptexture = r_shadow_blankbumptexture;
1238 // colorscale accounts for how much we multiply the brightness during combine
1239 // mult is how many times the final pass of the lighting will be
1240 // performed to get more brightness than otherwise possible
1241 // limit mult to 64 for sanity sake
1242 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1244 // 3/2 3D combine path (Geforce3, Radeon 8500)
1245 memset(&m, 0, sizeof(m));
1246 m.tex[0] = R_GetTexture(bumptexture);
1247 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1248 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1249 m.texcombinergb[0] = GL_REPLACE;
1250 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1251 m.pointer_texcoord[0] = texcoord2f;
1252 m.pointer_texcoord[1] = varray_texcoord3f[1];
1253 m.pointer_texcoord[2] = varray_texcoord3f[2];
1254 R_Mesh_State_Texture(&m);
1255 qglColorMask(0,0,0,1);
1256 GL_BlendFunc(GL_ONE, GL_ZERO);
1257 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1258 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1259 R_Mesh_Draw(numverts, numtriangles, elements);
1261 c_rt_lighttris += numtriangles;
1263 memset(&m, 0, sizeof(m));
1264 m.tex[0] = R_GetTexture(basetexture);
1265 m.texcubemap[1] = R_GetTexture(lightcubemap);
1266 m.pointer_texcoord[0] = texcoord2f;
1267 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1268 R_Mesh_State_Texture(&m);
1269 qglColorMask(1,1,1,0);
1270 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1272 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1273 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1274 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1276 color[0] = bound(0, color2[0], 1);
1277 color[1] = bound(0, color2[1], 1);
1278 color[2] = bound(0, color2[2], 1);
1279 GL_Color(color[0], color[1], color[2], 1);
1280 R_Mesh_Draw(numverts, numtriangles, elements);
1282 c_rt_lighttris += numtriangles;
1285 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1287 // 1/2/2 3D combine path (original Radeon)
1288 memset(&m, 0, sizeof(m));
1289 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1290 m.pointer_texcoord[0] = varray_texcoord3f[0];
1291 R_Mesh_State_Texture(&m);
1292 qglColorMask(0,0,0,1);
1293 GL_BlendFunc(GL_ONE, GL_ZERO);
1294 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1295 R_Mesh_Draw(numverts, numtriangles, elements);
1297 c_rt_lighttris += numtriangles;
1299 memset(&m, 0, sizeof(m));
1300 m.tex[0] = R_GetTexture(bumptexture);
1301 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1302 m.texcombinergb[0] = GL_REPLACE;
1303 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1304 m.pointer_texcoord[0] = texcoord2f;
1305 m.pointer_texcoord[1] = varray_texcoord3f[1];
1306 R_Mesh_State_Texture(&m);
1307 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1308 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1309 R_Mesh_Draw(numverts, numtriangles, elements);
1311 c_rt_lighttris += numtriangles;
1313 memset(&m, 0, sizeof(m));
1314 m.tex[0] = R_GetTexture(basetexture);
1315 m.texcubemap[1] = R_GetTexture(lightcubemap);
1316 m.pointer_texcoord[0] = texcoord2f;
1317 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1318 R_Mesh_State_Texture(&m);
1319 qglColorMask(1,1,1,0);
1320 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1322 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1323 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1324 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1326 color[0] = bound(0, color2[0], 1);
1327 color[1] = bound(0, color2[1], 1);
1328 color[2] = bound(0, color2[2], 1);
1329 GL_Color(color[0], color[1], color[2], 1);
1330 R_Mesh_Draw(numverts, numtriangles, elements);
1332 c_rt_lighttris += numtriangles;
1335 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1337 // 2/2 3D combine path (original Radeon)
1338 memset(&m, 0, sizeof(m));
1339 m.tex[0] = R_GetTexture(bumptexture);
1340 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1341 m.texcombinergb[0] = GL_REPLACE;
1342 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1343 m.pointer_texcoord[0] = texcoord2f;
1344 m.pointer_texcoord[1] = varray_texcoord3f[1];
1345 R_Mesh_State_Texture(&m);
1346 qglColorMask(0,0,0,1);
1347 GL_BlendFunc(GL_ONE, GL_ZERO);
1348 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1349 R_Mesh_Draw(numverts, numtriangles, elements);
1351 c_rt_lighttris += numtriangles;
1353 memset(&m, 0, sizeof(m));
1354 m.tex[0] = R_GetTexture(basetexture);
1355 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1356 m.pointer_texcoord[0] = texcoord2f;
1357 m.pointer_texcoord[1] = varray_texcoord3f[1];
1358 R_Mesh_State_Texture(&m);
1359 qglColorMask(1,1,1,0);
1360 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1361 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1362 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1363 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1365 color[0] = bound(0, color2[0], 1);
1366 color[1] = bound(0, color2[1], 1);
1367 color[2] = bound(0, color2[2], 1);
1368 GL_Color(color[0], color[1], color[2], 1);
1369 R_Mesh_Draw(numverts, numtriangles, elements);
1371 c_rt_lighttris += numtriangles;
1374 else if (r_textureunits.integer >= 4)
1376 // 4/2 2D combine path (Geforce3, Radeon 8500)
1377 memset(&m, 0, sizeof(m));
1378 m.tex[0] = R_GetTexture(bumptexture);
1379 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1380 m.texcombinergb[0] = GL_REPLACE;
1381 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1382 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1383 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1384 m.pointer_texcoord[0] = texcoord2f;
1385 m.pointer_texcoord[1] = varray_texcoord3f[1];
1386 m.pointer_texcoord[2] = varray_texcoord2f[2];
1387 m.pointer_texcoord[3] = varray_texcoord2f[3];
1388 R_Mesh_State_Texture(&m);
1389 qglColorMask(0,0,0,1);
1390 GL_BlendFunc(GL_ONE, GL_ZERO);
1391 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1392 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1393 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1394 R_Mesh_Draw(numverts, numtriangles, elements);
1396 c_rt_lighttris += numtriangles;
1398 memset(&m, 0, sizeof(m));
1399 m.tex[0] = R_GetTexture(basetexture);
1400 m.texcubemap[1] = R_GetTexture(lightcubemap);
1401 m.pointer_texcoord[0] = texcoord2f;
1402 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1403 R_Mesh_State_Texture(&m);
1404 qglColorMask(1,1,1,0);
1405 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1407 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1408 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1409 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1411 color[0] = bound(0, color2[0], 1);
1412 color[1] = bound(0, color2[1], 1);
1413 color[2] = bound(0, color2[2], 1);
1414 GL_Color(color[0], color[1], color[2], 1);
1415 R_Mesh_Draw(numverts, numtriangles, elements);
1417 c_rt_lighttris += numtriangles;
1422 // 2/2/2 2D combine path (any dot3 card)
1423 memset(&m, 0, sizeof(m));
1424 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1425 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1426 m.pointer_texcoord[0] = varray_texcoord2f[0];
1427 m.pointer_texcoord[1] = varray_texcoord2f[1];
1428 R_Mesh_State_Texture(&m);
1429 qglColorMask(0,0,0,1);
1430 GL_BlendFunc(GL_ONE, GL_ZERO);
1431 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1432 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1433 R_Mesh_Draw(numverts, numtriangles, elements);
1435 c_rt_lighttris += numtriangles;
1437 memset(&m, 0, sizeof(m));
1438 m.tex[0] = R_GetTexture(bumptexture);
1439 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1440 m.texcombinergb[0] = GL_REPLACE;
1441 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1442 m.pointer_texcoord[0] = texcoord2f;
1443 m.pointer_texcoord[1] = varray_texcoord3f[1];
1444 R_Mesh_State_Texture(&m);
1445 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1446 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1447 R_Mesh_Draw(numverts, numtriangles, elements);
1449 c_rt_lighttris += numtriangles;
1451 memset(&m, 0, sizeof(m));
1452 m.tex[0] = R_GetTexture(basetexture);
1453 m.texcubemap[1] = R_GetTexture(lightcubemap);
1454 m.pointer_texcoord[0] = texcoord2f;
1455 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1456 R_Mesh_State_Texture(&m);
1457 qglColorMask(1,1,1,0);
1458 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1460 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1461 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1462 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1464 color[0] = bound(0, color2[0], 1);
1465 color[1] = bound(0, color2[1], 1);
1466 color[2] = bound(0, color2[2], 1);
1467 GL_Color(color[0], color[1], color[2], 1);
1468 R_Mesh_Draw(numverts, numtriangles, elements);
1470 c_rt_lighttris += numtriangles;
1476 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1477 GL_DepthMask(false);
1479 GL_ColorPointer(varray_color4f);
1480 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1481 memset(&m, 0, sizeof(m));
1482 m.tex[0] = R_GetTexture(basetexture);
1483 m.pointer_texcoord[0] = texcoord2f;
1484 if (r_textureunits.integer >= 2)
1487 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1488 m.pointer_texcoord[1] = varray_texcoord2f[1];
1489 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1491 R_Mesh_State_Texture(&m);
1492 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1494 color[0] = bound(0, color2[0], 1);
1495 color[1] = bound(0, color2[1], 1);
1496 color[2] = bound(0, color2[2], 1);
1497 if (r_textureunits.integer >= 2)
1498 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1500 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1501 R_Mesh_Draw(numverts, numtriangles, elements);
1503 c_rt_lighttris += numtriangles;
1508 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)
1511 float color[3], color2[3], colorscale;
1513 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1516 glosstexture = r_shadow_blankglosstexture;
1517 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1519 colorscale = r_shadow_glossintensity.value;
1521 bumptexture = r_shadow_blankbumptexture;
1522 if (glosstexture == r_shadow_blankglosstexture)
1523 colorscale *= r_shadow_gloss2intensity.value;
1524 GL_VertexPointer(vertex3f);
1526 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1528 // 2/0/0/1/2 3D combine blendsquare path
1529 memset(&m, 0, sizeof(m));
1530 m.tex[0] = R_GetTexture(bumptexture);
1531 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1532 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1533 m.pointer_texcoord[0] = texcoord2f;
1534 m.pointer_texcoord[1] = varray_texcoord3f[1];
1535 R_Mesh_State_Texture(&m);
1536 qglColorMask(0,0,0,1);
1537 // this squares the result
1538 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1539 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1540 R_Mesh_Draw(numverts, numtriangles, elements);
1542 c_rt_lighttris += numtriangles;
1544 memset(&m, 0, sizeof(m));
1545 R_Mesh_State_Texture(&m);
1546 // square alpha in framebuffer a few times to make it shiny
1547 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1548 // these comments are a test run through this math for intensity 0.5
1549 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1550 // 0.25 * 0.25 = 0.0625 (this is another pass)
1551 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1552 R_Mesh_Draw(numverts, numtriangles, elements);
1554 c_rt_lighttris += numtriangles;
1555 R_Mesh_Draw(numverts, numtriangles, elements);
1557 c_rt_lighttris += numtriangles;
1559 memset(&m, 0, sizeof(m));
1560 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1561 m.pointer_texcoord[0] = varray_texcoord3f[0];
1562 R_Mesh_State_Texture(&m);
1563 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1564 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1565 R_Mesh_Draw(numverts, numtriangles, elements);
1567 c_rt_lighttris += numtriangles;
1569 memset(&m, 0, sizeof(m));
1570 m.tex[0] = R_GetTexture(glosstexture);
1571 m.texcubemap[1] = R_GetTexture(lightcubemap);
1572 m.pointer_texcoord[0] = texcoord2f;
1573 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1574 R_Mesh_State_Texture(&m);
1575 qglColorMask(1,1,1,0);
1576 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1578 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1579 VectorScale(lightcolor, colorscale, color2);
1580 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1582 color[0] = bound(0, color2[0], 1);
1583 color[1] = bound(0, color2[1], 1);
1584 color[2] = bound(0, color2[2], 1);
1585 GL_Color(color[0], color[1], color[2], 1);
1586 R_Mesh_Draw(numverts, numtriangles, elements);
1588 c_rt_lighttris += numtriangles;
1591 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1593 // 2/0/0/2 3D combine blendsquare path
1594 memset(&m, 0, sizeof(m));
1595 m.tex[0] = R_GetTexture(bumptexture);
1596 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1597 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1598 m.pointer_texcoord[0] = texcoord2f;
1599 m.pointer_texcoord[1] = varray_texcoord3f[1];
1600 R_Mesh_State_Texture(&m);
1601 qglColorMask(0,0,0,1);
1602 // this squares the result
1603 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1604 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1605 R_Mesh_Draw(numverts, numtriangles, elements);
1607 c_rt_lighttris += numtriangles;
1609 memset(&m, 0, sizeof(m));
1610 R_Mesh_State_Texture(&m);
1611 // square alpha in framebuffer a few times to make it shiny
1612 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1613 // these comments are a test run through this math for intensity 0.5
1614 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1615 // 0.25 * 0.25 = 0.0625 (this is another pass)
1616 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1617 R_Mesh_Draw(numverts, numtriangles, elements);
1619 c_rt_lighttris += numtriangles;
1620 R_Mesh_Draw(numverts, numtriangles, elements);
1622 c_rt_lighttris += numtriangles;
1624 memset(&m, 0, sizeof(m));
1625 m.tex[0] = R_GetTexture(glosstexture);
1626 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1627 m.pointer_texcoord[0] = texcoord2f;
1628 m.pointer_texcoord[1] = varray_texcoord3f[1];
1629 R_Mesh_State_Texture(&m);
1630 qglColorMask(1,1,1,0);
1631 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1632 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1633 VectorScale(lightcolor, colorscale, color2);
1634 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1636 color[0] = bound(0, color2[0], 1);
1637 color[1] = bound(0, color2[1], 1);
1638 color[2] = bound(0, color2[2], 1);
1639 GL_Color(color[0], color[1], color[2], 1);
1640 R_Mesh_Draw(numverts, numtriangles, elements);
1642 c_rt_lighttris += numtriangles;
1645 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1647 // 2/0/0/2/2 2D combine blendsquare path
1648 memset(&m, 0, sizeof(m));
1649 m.tex[0] = R_GetTexture(bumptexture);
1650 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1651 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1652 m.pointer_texcoord[0] = texcoord2f;
1653 m.pointer_texcoord[1] = varray_texcoord3f[1];
1654 R_Mesh_State_Texture(&m);
1655 qglColorMask(0,0,0,1);
1656 // this squares the result
1657 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1658 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1659 R_Mesh_Draw(numverts, numtriangles, elements);
1661 c_rt_lighttris += numtriangles;
1663 memset(&m, 0, sizeof(m));
1664 R_Mesh_State_Texture(&m);
1665 // square alpha in framebuffer a few times to make it shiny
1666 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1667 // these comments are a test run through this math for intensity 0.5
1668 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1669 // 0.25 * 0.25 = 0.0625 (this is another pass)
1670 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1671 R_Mesh_Draw(numverts, numtriangles, elements);
1673 c_rt_lighttris += numtriangles;
1674 R_Mesh_Draw(numverts, numtriangles, elements);
1676 c_rt_lighttris += numtriangles;
1678 memset(&m, 0, sizeof(m));
1679 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1680 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1681 m.pointer_texcoord[0] = varray_texcoord2f[0];
1682 m.pointer_texcoord[1] = varray_texcoord2f[1];
1683 R_Mesh_State_Texture(&m);
1684 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1685 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1686 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1687 R_Mesh_Draw(numverts, numtriangles, elements);
1689 c_rt_lighttris += numtriangles;
1691 memset(&m, 0, sizeof(m));
1692 m.tex[0] = R_GetTexture(glosstexture);
1693 m.texcubemap[1] = R_GetTexture(lightcubemap);
1694 m.pointer_texcoord[0] = texcoord2f;
1695 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1696 R_Mesh_State_Texture(&m);
1697 qglColorMask(1,1,1,0);
1698 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1700 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1701 VectorScale(lightcolor, colorscale, color2);
1702 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1704 color[0] = bound(0, color2[0], 1);
1705 color[1] = bound(0, color2[1], 1);
1706 color[2] = bound(0, color2[2], 1);
1707 GL_Color(color[0], color[1], color[2], 1);
1708 R_Mesh_Draw(numverts, numtriangles, elements);
1710 c_rt_lighttris += numtriangles;
1716 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1718 R_Mesh_Matrix(matrix);
1719 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1722 cvar_t r_editlights = {0, "r_editlights", "0"};
1723 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1724 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1725 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1726 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1727 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1728 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1729 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1730 worldlight_t *r_shadow_worldlightchain;
1731 worldlight_t *r_shadow_selectedlight;
1732 vec3_t r_editlights_cursorlocation;
1734 static int lightpvsbytes;
1735 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1737 static int castshadowcount = 1;
1738 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1740 int i, j, k, l, maxverts = 256, *mark, tris, numsurfaces;
1741 float *vertex3f = NULL, mins[3], maxs[3];
1743 shadowmesh_t *mesh, *castmesh;
1747 if (radius < 15 || DotProduct(color, color) < 0.03)
1749 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1753 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1754 VectorCopy(origin, e->origin);
1755 VectorCopy(color, e->light);
1756 e->lightradius = radius;
1758 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1760 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1763 e->castshadows = castshadow;
1765 e->cullradius = e->lightradius;
1766 for (k = 0;k < 3;k++)
1768 mins[k] = e->origin[k] - e->lightradius;
1769 maxs[k] = e->origin[k] + e->lightradius;
1772 e->next = r_shadow_worldlightchain;
1773 r_shadow_worldlightchain = e;
1774 if (cubemapname && cubemapname[0])
1776 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1777 strcpy(e->cubemapname, cubemapname);
1778 // FIXME: add cubemap loading (and don't load a cubemap twice)
1783 VectorCopy(e->origin, e->mins);
1784 VectorCopy(e->origin, e->maxs);
1785 i = CL_PointQ1Contents(e->origin);
1786 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1788 //qbyte *byteleafpvs;
1789 qbyte *bytesurfacepvs;
1791 //byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1792 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1794 Portal_Visibility(cl.worldmodel, e->origin, NULL/*byteleafpvs*/, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1797 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1799 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1801 for (k = 0;k < 3;k++)
1803 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1804 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1810 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1811 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1812 surf->castshadow = castshadowcount;
1814 //Mem_Free(byteleafpvs);
1815 Mem_Free(bytesurfacepvs);
1819 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1820 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1822 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1824 for (k = 0;k < 3;k++)
1826 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1827 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1829 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1831 surf = cl.worldmodel->brushq1.surfaces + *mark;
1832 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1833 surf->castshadow = castshadowcount;
1839 for (k = 0;k < 3;k++)
1841 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1842 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1844 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1847 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1848 if (surf->castshadow == castshadowcount)
1851 e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
1853 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1854 if (surf->castshadow == castshadowcount)
1855 e->surfaces[e->numsurfaces++] = surf;
1860 for (j = 0;j < e->numsurfaces;j++)
1862 surf = e->surfaces[j];
1863 if (surf->flags & SURF_SHADOWCAST)
1865 surf->castshadow = castshadowcount;
1866 if (maxverts < surf->poly_numverts)
1867 maxverts = surf->poly_numverts;
1870 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1871 // make a mesh to cast a shadow volume from
1872 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1873 for (j = 0;j < e->numsurfaces;j++)
1874 if ((surf = e->surfaces[j])->castshadow == castshadowcount)
1875 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surf->mesh.data_vertex3f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1876 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1878 // cast shadow volume from castmesh
1879 for (mesh = castmesh;mesh;mesh = mesh->next)
1881 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1883 if (maxverts < castmesh->numverts * 2)
1885 maxverts = castmesh->numverts * 2;
1890 if (vertex3f == NULL && maxverts > 0)
1891 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1893 // now that we have the buffers big enough, construct and add
1894 // the shadow volume mesh
1895 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)))
1896 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1901 // we're done with castmesh now
1902 Mod_ShadowMesh_Free(castmesh);
1903 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1904 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1905 l += mesh->numtriangles;
1906 Con_Printf("static shadow volume built containing %i triangles\n", l);
1909 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numsurfaces);
1912 void R_Shadow_FreeWorldLight(worldlight_t *light)
1914 worldlight_t **lightpointer;
1915 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1916 if (*lightpointer != light)
1917 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1918 *lightpointer = light->next;
1919 if (light->cubemapname)
1920 Mem_Free(light->cubemapname);
1921 if (light->shadowvolume)
1922 Mod_ShadowMesh_Free(light->shadowvolume);
1923 if (light->surfaces)
1924 Mem_Free(light->surfaces);
1928 void R_Shadow_ClearWorldLights(void)
1930 while (r_shadow_worldlightchain)
1931 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1932 r_shadow_selectedlight = NULL;
1935 void R_Shadow_SelectLight(worldlight_t *light)
1937 if (r_shadow_selectedlight)
1938 r_shadow_selectedlight->selected = false;
1939 r_shadow_selectedlight = light;
1940 if (r_shadow_selectedlight)
1941 r_shadow_selectedlight->selected = true;
1944 rtexture_t *lighttextures[5];
1946 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1948 float scale = r_editlights_cursorgrid.value * 0.5f;
1949 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
1952 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1955 const worldlight_t *light;
1958 if (light->selected)
1959 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1960 if (!light->shadowvolume)
1962 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
1965 void R_Shadow_DrawLightSprites(void)
1969 worldlight_t *light;
1971 for (i = 0;i < 5;i++)
1973 lighttextures[i] = NULL;
1974 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
1975 lighttextures[i] = pic->tex;
1978 for (light = r_shadow_worldlightchain;light;light = light->next)
1979 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
1980 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1983 void R_Shadow_SelectLightInView(void)
1985 float bestrating, rating, temp[3];
1986 worldlight_t *best, *light;
1989 for (light = r_shadow_worldlightchain;light;light = light->next)
1991 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1992 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1995 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1996 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
1998 bestrating = rating;
2003 R_Shadow_SelectLight(best);
2006 void R_Shadow_LoadWorldLights(void)
2008 int n, a, style, shadow;
2009 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2010 float origin[3], radius, color[3];
2011 if (cl.worldmodel == NULL)
2013 Con_Printf("No map loaded.\n");
2016 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2017 strlcat (name, ".rtlights", sizeof (name));
2018 lightsstring = FS_LoadFile(name, false);
2026 while (*s && *s != '\n')
2032 // check for modifier flags
2038 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);
2044 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);
2047 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2048 radius *= r_editlights_rtlightssizescale.value;
2049 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2054 Con_Printf("invalid rtlights file \"%s\"\n", name);
2055 Mem_Free(lightsstring);
2059 void R_Shadow_SaveWorldLights(void)
2061 worldlight_t *light;
2062 int bufchars, bufmaxchars;
2064 char name[MAX_QPATH];
2066 if (!r_shadow_worldlightchain)
2068 if (cl.worldmodel == NULL)
2070 Con_Printf("No map loaded.\n");
2073 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2074 strlcat (name, ".rtlights", sizeof (name));
2075 bufchars = bufmaxchars = 0;
2077 for (light = r_shadow_worldlightchain;light;light = light->next)
2079 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 : "");
2080 if (bufchars + (int) strlen(line) > bufmaxchars)
2082 bufmaxchars = bufchars + strlen(line) + 2048;
2084 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2088 memcpy(buf, oldbuf, bufchars);
2094 memcpy(buf + bufchars, line, strlen(line));
2095 bufchars += strlen(line);
2099 FS_WriteFile(name, buf, bufchars);
2104 void R_Shadow_LoadLightsFile(void)
2107 char name[MAX_QPATH], *lightsstring, *s, *t;
2108 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2109 if (cl.worldmodel == NULL)
2111 Con_Printf("No map loaded.\n");
2114 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2115 strlcat (name, ".lights", sizeof (name));
2116 lightsstring = FS_LoadFile(name, false);
2124 while (*s && *s != '\n')
2129 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);
2133 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);
2136 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2137 radius = bound(15, radius, 4096);
2138 VectorScale(color, (2.0f / (8388608.0f)), color);
2139 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2144 Con_Printf("invalid lights file \"%s\"\n", name);
2145 Mem_Free(lightsstring);
2149 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2151 int entnum, style, islight;
2152 char key[256], value[1024];
2153 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2156 if (cl.worldmodel == NULL)
2158 Con_Printf("No map loaded.\n");
2161 data = cl.worldmodel->brush.entities;
2164 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2167 origin[0] = origin[1] = origin[2] = 0;
2168 originhack[0] = originhack[1] = originhack[2] = 0;
2169 color[0] = color[1] = color[2] = 1;
2170 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2176 if (!COM_ParseToken(&data, false))
2178 if (com_token[0] == '}')
2179 break; // end of entity
2180 if (com_token[0] == '_')
2181 strcpy(key, com_token + 1);
2183 strcpy(key, com_token);
2184 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2185 key[strlen(key)-1] = 0;
2186 if (!COM_ParseToken(&data, false))
2188 strcpy(value, com_token);
2190 // now that we have the key pair worked out...
2191 if (!strcmp("light", key))
2192 light = atof(value);
2193 else if (!strcmp("origin", key))
2194 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2195 else if (!strcmp("color", key))
2196 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2197 else if (!strcmp("wait", key))
2198 scale = atof(value);
2199 else if (!strcmp("classname", key))
2201 if (!strncmp(value, "light", 5))
2204 if (!strcmp(value, "light_fluoro"))
2209 overridecolor[0] = 1;
2210 overridecolor[1] = 1;
2211 overridecolor[2] = 1;
2213 if (!strcmp(value, "light_fluorospark"))
2218 overridecolor[0] = 1;
2219 overridecolor[1] = 1;
2220 overridecolor[2] = 1;
2222 if (!strcmp(value, "light_globe"))
2227 overridecolor[0] = 1;
2228 overridecolor[1] = 0.8;
2229 overridecolor[2] = 0.4;
2231 if (!strcmp(value, "light_flame_large_yellow"))
2236 overridecolor[0] = 1;
2237 overridecolor[1] = 0.5;
2238 overridecolor[2] = 0.1;
2240 if (!strcmp(value, "light_flame_small_yellow"))
2245 overridecolor[0] = 1;
2246 overridecolor[1] = 0.5;
2247 overridecolor[2] = 0.1;
2249 if (!strcmp(value, "light_torch_small_white"))
2254 overridecolor[0] = 1;
2255 overridecolor[1] = 0.5;
2256 overridecolor[2] = 0.1;
2258 if (!strcmp(value, "light_torch_small_walltorch"))
2263 overridecolor[0] = 1;
2264 overridecolor[1] = 0.5;
2265 overridecolor[2] = 0.1;
2269 else if (!strcmp("style", key))
2270 style = atoi(value);
2272 if (light <= 0 && islight)
2274 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2275 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2276 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2277 VectorCopy(overridecolor, color);
2278 VectorScale(color, light, color);
2279 VectorAdd(origin, originhack, origin);
2281 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2286 void R_Shadow_SetCursorLocationForView(void)
2288 vec_t dist, push, frac;
2289 vec3_t dest, endpos, normal;
2290 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2291 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2294 dist = frac * r_editlights_cursordistance.value;
2295 push = r_editlights_cursorpushback.value;
2299 VectorMA(endpos, push, vpn, endpos);
2300 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2302 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2303 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2304 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2307 void R_Shadow_UpdateWorldLightSelection(void)
2309 if (r_editlights.integer)
2311 R_Shadow_SetCursorLocationForView();
2312 R_Shadow_SelectLightInView();
2313 R_Shadow_DrawLightSprites();
2316 R_Shadow_SelectLight(NULL);
2319 void R_Shadow_EditLights_Clear_f(void)
2321 R_Shadow_ClearWorldLights();
2324 void R_Shadow_EditLights_Reload_f(void)
2326 r_shadow_reloadlights = true;
2329 void R_Shadow_EditLights_Save_f(void)
2332 R_Shadow_SaveWorldLights();
2335 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2337 R_Shadow_ClearWorldLights();
2338 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2341 void R_Shadow_EditLights_ImportLightsFile_f(void)
2343 R_Shadow_ClearWorldLights();
2344 R_Shadow_LoadLightsFile();
2347 void R_Shadow_EditLights_Spawn_f(void)
2350 if (!r_editlights.integer)
2352 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2355 if (Cmd_Argc() != 1)
2357 Con_Printf("r_editlights_spawn does not take parameters\n");
2360 color[0] = color[1] = color[2] = 1;
2361 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2364 void R_Shadow_EditLights_Edit_f(void)
2366 vec3_t origin, color;
2369 char cubemapname[1024];
2370 if (!r_editlights.integer)
2372 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2375 if (!r_shadow_selectedlight)
2377 Con_Printf("No selected light.\n");
2380 VectorCopy(r_shadow_selectedlight->origin, origin);
2381 radius = r_shadow_selectedlight->lightradius;
2382 VectorCopy(r_shadow_selectedlight->light, color);
2383 style = r_shadow_selectedlight->style;
2384 if (r_shadow_selectedlight->cubemapname)
2385 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2388 shadows = r_shadow_selectedlight->castshadows;
2389 if (!strcmp(Cmd_Argv(1), "origin"))
2391 if (Cmd_Argc() != 5)
2393 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2396 origin[0] = atof(Cmd_Argv(2));
2397 origin[1] = atof(Cmd_Argv(3));
2398 origin[2] = atof(Cmd_Argv(4));
2400 else if (!strcmp(Cmd_Argv(1), "originx"))
2402 if (Cmd_Argc() != 3)
2404 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2407 origin[0] = atof(Cmd_Argv(2));
2409 else if (!strcmp(Cmd_Argv(1), "originy"))
2411 if (Cmd_Argc() != 3)
2413 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2416 origin[1] = atof(Cmd_Argv(2));
2418 else if (!strcmp(Cmd_Argv(1), "originz"))
2420 if (Cmd_Argc() != 3)
2422 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2425 origin[2] = atof(Cmd_Argv(2));
2427 else if (!strcmp(Cmd_Argv(1), "move"))
2429 if (Cmd_Argc() != 5)
2431 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2434 origin[0] += atof(Cmd_Argv(2));
2435 origin[1] += atof(Cmd_Argv(3));
2436 origin[2] += atof(Cmd_Argv(4));
2438 else if (!strcmp(Cmd_Argv(1), "movex"))
2440 if (Cmd_Argc() != 3)
2442 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2445 origin[0] += atof(Cmd_Argv(2));
2447 else if (!strcmp(Cmd_Argv(1), "movey"))
2449 if (Cmd_Argc() != 3)
2451 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2454 origin[1] += atof(Cmd_Argv(2));
2456 else if (!strcmp(Cmd_Argv(1), "movez"))
2458 if (Cmd_Argc() != 3)
2460 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2463 origin[2] += atof(Cmd_Argv(2));
2465 else if (!strcmp(Cmd_Argv(1), "color"))
2467 if (Cmd_Argc() != 5)
2469 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2472 color[0] = atof(Cmd_Argv(2));
2473 color[1] = atof(Cmd_Argv(3));
2474 color[2] = atof(Cmd_Argv(4));
2476 else if (!strcmp(Cmd_Argv(1), "radius"))
2478 if (Cmd_Argc() != 3)
2480 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2483 radius = atof(Cmd_Argv(2));
2485 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2487 if (Cmd_Argc() != 3)
2489 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2492 style = atoi(Cmd_Argv(2));
2494 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2498 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2501 if (Cmd_Argc() == 3)
2502 strcpy(cubemapname, Cmd_Argv(2));
2506 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2508 if (Cmd_Argc() != 3)
2510 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2513 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2517 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2518 Con_Printf("Selected light's properties:\n");
2519 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2520 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2521 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2522 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2523 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2524 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2527 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2528 r_shadow_selectedlight = NULL;
2529 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2532 extern int con_vislines;
2533 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2537 if (r_shadow_selectedlight == NULL)
2541 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2542 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;
2543 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2544 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;
2545 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2546 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2547 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;
2550 void R_Shadow_EditLights_ToggleShadow_f(void)
2552 if (!r_editlights.integer)
2554 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2557 if (!r_shadow_selectedlight)
2559 Con_Printf("No selected light.\n");
2562 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);
2563 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2564 r_shadow_selectedlight = NULL;
2567 void R_Shadow_EditLights_Remove_f(void)
2569 if (!r_editlights.integer)
2571 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2574 if (!r_shadow_selectedlight)
2576 Con_Printf("No selected light.\n");
2579 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2580 r_shadow_selectedlight = NULL;
2583 void R_Shadow_EditLights_Help_f(void)
2586 "Documentation on r_editlights system:\n"
2588 "r_editlights : enable/disable editing mode\n"
2589 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2590 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2591 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2592 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2593 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2594 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2595 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2597 "r_editlights_help : this help\n"
2598 "r_editlights_clear : remove all lights\n"
2599 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2600 "r_editlights_save : save to .rtlights file\n"
2601 "r_editlights_spawn : create a light with default settings\n"
2602 "r_editlights_edit command : edit selected light - more documentation below\n"
2603 "r_editlights_remove : remove selected light\n"
2604 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2605 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2606 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2608 "origin x y z : set light location\n"
2609 "originx x: set x component of light location\n"
2610 "originy y: set y component of light location\n"
2611 "originz z: set z component of light location\n"
2612 "move x y z : adjust light location\n"
2613 "movex x: adjust x component of light location\n"
2614 "movey y: adjust y component of light location\n"
2615 "movez z: adjust z component of light location\n"
2616 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2617 "radius radius : set radius (size) of light\n"
2618 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2619 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2620 "shadows 1/0 : turn on/off shadows\n"
2621 "<nothing> : print light properties to console\n"
2625 void R_Shadow_EditLights_Init(void)
2627 Cvar_RegisterVariable(&r_editlights);
2628 Cvar_RegisterVariable(&r_editlights_cursordistance);
2629 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2630 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2631 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2632 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2633 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2634 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2635 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2636 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2637 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2638 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2639 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2640 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2641 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2642 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2643 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2644 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);