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), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however. Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
119 extern void R_Shadow_EditLights_Init(void);
121 #define SHADOWSTAGE_NONE 0
122 #define SHADOWSTAGE_STENCIL 1
123 #define SHADOWSTAGE_LIGHT 2
124 #define SHADOWSTAGE_ERASESTENCIL 3
126 int r_shadowstage = SHADOWSTAGE_NONE;
127 int r_shadow_reloadlights = false;
129 mempool_t *r_shadow_mempool;
131 int maxshadowelements;
133 int maxtrianglefacinglight;
134 qbyte *trianglefacinglight;
135 int *trianglefacinglightlist;
142 rtexturepool_t *r_shadow_texturepool;
143 rtexture_t *r_shadow_normalcubetexture;
144 rtexture_t *r_shadow_attenuation2dtexture;
145 rtexture_t *r_shadow_attenuation3dtexture;
146 rtexture_t *r_shadow_blankbumptexture;
147 rtexture_t *r_shadow_blankglosstexture;
148 rtexture_t *r_shadow_blankwhitetexture;
150 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
151 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
152 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
153 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
154 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
155 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
156 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
157 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
158 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
159 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
160 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
161 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
162 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
163 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
164 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
165 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
166 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
167 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
168 cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
169 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
171 int c_rt_lights, c_rt_clears, c_rt_scissored;
172 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
173 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
175 void R_Shadow_ClearWorldLights(void);
176 void R_Shadow_SaveWorldLights(void);
177 void R_Shadow_LoadWorldLights(void);
178 void R_Shadow_LoadLightsFile(void);
179 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
181 void r_shadow_start(void)
183 // allocate vertex processing arrays
184 r_shadow_mempool = Mem_AllocPool("R_Shadow");
185 maxshadowelements = 0;
186 shadowelements = NULL;
191 maxtrianglefacinglight = 0;
192 trianglefacinglight = NULL;
193 trianglefacinglightlist = NULL;
194 r_shadow_normalcubetexture = NULL;
195 r_shadow_attenuation2dtexture = NULL;
196 r_shadow_attenuation3dtexture = NULL;
197 r_shadow_blankbumptexture = NULL;
198 r_shadow_blankglosstexture = NULL;
199 r_shadow_blankwhitetexture = NULL;
200 r_shadow_texturepool = NULL;
201 R_Shadow_ClearWorldLights();
202 r_shadow_reloadlights = true;
205 void r_shadow_shutdown(void)
207 R_Shadow_ClearWorldLights();
208 r_shadow_reloadlights = true;
209 r_shadow_normalcubetexture = NULL;
210 r_shadow_attenuation2dtexture = NULL;
211 r_shadow_attenuation3dtexture = NULL;
212 r_shadow_blankbumptexture = NULL;
213 r_shadow_blankglosstexture = NULL;
214 r_shadow_blankwhitetexture = NULL;
215 R_FreeTexturePool(&r_shadow_texturepool);
216 maxshadowelements = 0;
217 shadowelements = NULL;
222 maxtrianglefacinglight = 0;
223 trianglefacinglight = NULL;
224 trianglefacinglightlist = NULL;
225 Mem_FreePool(&r_shadow_mempool);
228 void r_shadow_newmap(void)
230 R_Shadow_ClearWorldLights();
231 r_shadow_reloadlights = true;
234 void R_Shadow_Help_f(void)
237 "Documentation on r_shadow system:\n"
239 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
240 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
241 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
242 "r_shadow_realtime_world : use realtime world light rendering\n"
243 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
244 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
245 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
246 "r_shadow_glossintensity : brightness of textured gloss\n"
247 "r_shadow_gloss2intensity : brightness of forced gloss\n"
248 "r_shadow_debuglight : render only this light number (-1 = all)\n"
249 "r_shadow_scissor : use scissor optimization\n"
250 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
251 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
252 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
253 "r_shadow_portallight : use portal visibility for static light precomputation\n"
254 "r_shadow_projectdistance : shadow volume projection distance\n"
255 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
256 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
257 "r_shadow_shadows : dlight shadows (world always has shadows)\n"
259 "r_shadow_help : this help\n"
263 void R_Shadow_Init(void)
265 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
266 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
267 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
268 Cvar_RegisterVariable(&r_shadow_realtime_world);
269 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
270 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
271 Cvar_RegisterVariable(&r_shadow_gloss);
272 Cvar_RegisterVariable(&r_shadow_glossintensity);
273 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
274 Cvar_RegisterVariable(&r_shadow_debuglight);
275 Cvar_RegisterVariable(&r_shadow_scissor);
276 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
277 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
278 Cvar_RegisterVariable(&r_shadow_polygonoffset);
279 Cvar_RegisterVariable(&r_shadow_portallight);
280 Cvar_RegisterVariable(&r_shadow_projectdistance);
281 Cvar_RegisterVariable(&r_shadow_texture3d);
282 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
283 Cvar_RegisterVariable(&r_shadow_shadows);
284 Cvar_RegisterVariable(&r_shadow_showtris);
285 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
286 R_Shadow_EditLights_Init();
287 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
290 void R_Shadow_ResizeTriangleFacingLight(int numtris)
292 // make sure trianglefacinglight is big enough for this volume
293 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
294 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
295 if (maxtrianglefacinglight < numtris)
297 maxtrianglefacinglight = numtris;
298 if (trianglefacinglight)
299 Mem_Free(trianglefacinglight);
300 if (trianglefacinglightlist)
301 Mem_Free(trianglefacinglightlist);
302 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
303 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
307 int *R_Shadow_ResizeShadowElements(int numtris)
309 // make sure shadowelements is big enough for this volume
310 if (maxshadowelements < numtris * 24)
312 maxshadowelements = numtris * 24;
314 Mem_Free(shadowelements);
315 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
317 return shadowelements;
321 // readable version of some code found below
322 //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]))))
323 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
325 float dir0[3], dir1[3], normal[3];
327 // calculate two mostly perpendicular edge directions
328 VectorSubtract(a, b, dir0);
329 VectorSubtract(c, b, dir1);
331 // we have two edge directions, we can calculate a third vector from
332 // them, which is the direction of the surface normal (it's magnitude
334 CrossProduct(dir0, dir1, normal);
336 // compare distance of light along normal, with distance of any point
337 // of the triangle along the same normal (the triangle is planar,
338 // I.E. flat, so all points give the same answer)
339 return DotProduct(p, normal) > DotProduct(a, normal);
341 int checkcastshadowfromedge(int t, int i)
345 if (t >= trianglerange_start && t < trianglerange_end)
347 if (t < i && !trianglefacinglight[t])
358 te = inelement3i + t * 3;
359 v[0] = invertex3f + te[0] * 3;
360 v[1] = invertex3f + te[1] * 3;
361 v[2] = invertex3f + te[2] * 3;
362 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
371 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)
373 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
375 const int *e, *n, *te;
378 // make sure trianglefacinglight is big enough for this volume
379 if (maxtrianglefacinglight < trianglerange_end)
380 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
382 if (maxvertexupdate < innumvertices)
384 maxvertexupdate = innumvertices;
386 Mem_Free(vertexupdate);
388 Mem_Free(vertexremap);
389 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
390 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
394 if (r_shadow_singlepassvolumegeneration.integer)
396 // one pass approach (identify lit/dark faces and generate sides while doing so)
397 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
399 // calculate triangle facing flag
400 v[0] = invertex3f + e[0] * 3;
401 v[1] = invertex3f + e[1] * 3;
402 v[2] = invertex3f + e[2] * 3;
403 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
405 // make sure the vertices are created
406 for (j = 0;j < 3;j++)
408 if (vertexupdate[e[j]] != vertexupdatenum)
410 vertexupdate[e[j]] = vertexupdatenum;
411 vertexremap[e[j]] = outvertices;
412 VectorCopy(v[j], outvertex3f);
413 VectorSubtract(v[j], relativelightorigin, temp);
414 f = projectdistance / VectorLength(temp);
415 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
420 // output the front and back triangles
421 vr[0] = vertexremap[e[0]];
422 vr[1] = vertexremap[e[1]];
423 vr[2] = vertexremap[e[2]];
424 outelement3i[0] = vr[0];
425 outelement3i[1] = vr[1];
426 outelement3i[2] = vr[2];
427 outelement3i[3] = vr[2] + 1;
428 outelement3i[4] = vr[1] + 1;
429 outelement3i[5] = vr[0] + 1;
432 // output the sides (facing outward from this triangle)
434 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]))))
436 outelement3i[0] = vr[1];
437 outelement3i[1] = vr[0];
438 outelement3i[2] = vr[0] + 1;
439 outelement3i[3] = vr[1];
440 outelement3i[4] = vr[0] + 1;
441 outelement3i[5] = vr[1] + 1;
446 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]))))
448 outelement3i[0] = vr[2];
449 outelement3i[1] = vr[1];
450 outelement3i[2] = vr[1] + 1;
451 outelement3i[3] = vr[2];
452 outelement3i[4] = vr[1] + 1;
453 outelement3i[5] = vr[2] + 1;
458 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]))))
460 outelement3i[0] = vr[0];
461 outelement3i[1] = vr[2];
462 outelement3i[2] = vr[2] + 1;
463 outelement3i[3] = vr[0];
464 outelement3i[4] = vr[2] + 1;
465 outelement3i[5] = vr[0] + 1;
472 // this triangle is not facing the light
473 // output the sides (facing inward to this triangle)
475 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
477 vr[0] = vertexremap[e[0]];
478 vr[1] = vertexremap[e[1]];
479 outelement3i[0] = vr[1];
480 outelement3i[1] = vr[0] + 1;
481 outelement3i[2] = vr[0];
482 outelement3i[3] = vr[1];
483 outelement3i[4] = vr[1] + 1;
484 outelement3i[5] = vr[0] + 1;
489 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
491 vr[1] = vertexremap[e[1]];
492 vr[2] = vertexremap[e[2]];
493 outelement3i[0] = vr[2];
494 outelement3i[1] = vr[1] + 1;
495 outelement3i[2] = vr[1];
496 outelement3i[3] = vr[2];
497 outelement3i[4] = vr[2] + 1;
498 outelement3i[5] = vr[1] + 1;
503 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
505 vr[0] = vertexremap[e[0]];
506 vr[2] = vertexremap[e[2]];
507 outelement3i[0] = vr[0];
508 outelement3i[1] = vr[2] + 1;
509 outelement3i[2] = vr[2];
510 outelement3i[3] = vr[0];
511 outelement3i[4] = vr[0] + 1;
512 outelement3i[5] = vr[2] + 1;
521 // two pass approach (identify lit/dark faces and then generate sides)
522 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
524 // calculate triangle facing flag
525 v[0] = invertex3f + e[0] * 3;
526 v[1] = invertex3f + e[1] * 3;
527 v[2] = invertex3f + e[2] * 3;
528 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
530 trianglefacinglightlist[numfacing++] = i;
531 // make sure the vertices are created
532 for (j = 0;j < 3;j++)
534 if (vertexupdate[e[j]] != vertexupdatenum)
536 vertexupdate[e[j]] = vertexupdatenum;
537 vertexremap[e[j]] = outvertices;
538 VectorSubtract(v[j], relativelightorigin, temp);
539 f = projectdistance / VectorLength(temp);
540 VectorCopy(v[j], outvertex3f);
541 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
546 // output the front and back triangles
547 outelement3i[0] = vertexremap[e[0]];
548 outelement3i[1] = vertexremap[e[1]];
549 outelement3i[2] = vertexremap[e[2]];
550 outelement3i[3] = vertexremap[e[2]] + 1;
551 outelement3i[4] = vertexremap[e[1]] + 1;
552 outelement3i[5] = vertexremap[e[0]] + 1;
557 for (i = 0;i < numfacing;i++)
559 t = trianglefacinglightlist[i];
560 e = inelement3i + t * 3;
561 n = inneighbor3i + t * 3;
562 // output the sides (facing outward from this triangle)
564 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]))))
566 vr[0] = vertexremap[e[0]];
567 vr[1] = vertexremap[e[1]];
568 outelement3i[0] = vr[1];
569 outelement3i[1] = vr[0];
570 outelement3i[2] = vr[0] + 1;
571 outelement3i[3] = vr[1];
572 outelement3i[4] = vr[0] + 1;
573 outelement3i[5] = vr[1] + 1;
578 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]))))
580 vr[1] = vertexremap[e[1]];
581 vr[2] = vertexremap[e[2]];
582 outelement3i[0] = vr[2];
583 outelement3i[1] = vr[1];
584 outelement3i[2] = vr[1] + 1;
585 outelement3i[3] = vr[2];
586 outelement3i[4] = vr[1] + 1;
587 outelement3i[5] = vr[2] + 1;
592 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]))))
594 vr[0] = vertexremap[e[0]];
595 vr[2] = vertexremap[e[2]];
596 outelement3i[0] = vr[0];
597 outelement3i[1] = vr[2];
598 outelement3i[2] = vr[2] + 1;
599 outelement3i[3] = vr[0];
600 outelement3i[4] = vr[2] + 1;
601 outelement3i[5] = vr[0] + 1;
608 *outnumvertices = outvertices;
612 float varray_vertex3f2[65536*3];
614 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
617 if (projectdistance < 0.1)
619 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
625 // make sure shadowelements is big enough for this volume
626 if (maxshadowelements < numtris * 24)
627 R_Shadow_ResizeShadowElements(numtris);
629 // check which triangles are facing the light, and then output
630 // triangle elements and vertices... by clever use of elements we
631 // can construct the whole shadow from the unprojected vertices and
632 // the projected vertices
633 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
635 GL_VertexPointer(varray_vertex3f2);
636 if (r_shadowstage == SHADOWSTAGE_STENCIL)
638 // decrement stencil if frontface is behind depthbuffer
639 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
640 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
641 R_Mesh_Draw(outverts, tris, shadowelements);
643 c_rt_shadowtris += numtris;
644 // increment stencil if backface is behind depthbuffer
645 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
646 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
648 R_Mesh_Draw(outverts, tris, shadowelements);
650 c_rt_shadowtris += numtris;
654 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
657 if (r_shadowstage == SHADOWSTAGE_STENCIL)
659 // decrement stencil if frontface is behind depthbuffer
660 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
661 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;
669 // increment stencil if backface is behind depthbuffer
670 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
671 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
673 for (mesh = firstmesh;mesh;mesh = mesh->next)
675 GL_VertexPointer(mesh->vertex3f);
676 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
677 c_rtcached_shadowmeshes++;
678 c_rtcached_shadowtris += mesh->numtriangles;
682 float r_shadow_attenpower, r_shadow_attenscale;
683 static void R_Shadow_MakeTextures(void)
685 int x, y, z, d, side;
686 float v[3], s, t, intensity;
688 R_FreeTexturePool(&r_shadow_texturepool);
689 r_shadow_texturepool = R_AllocTexturePool();
690 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
691 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
693 #define ATTEN2DSIZE 64
694 #define ATTEN3DSIZE 32
695 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
700 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
705 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
710 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
711 if (gl_texturecubemap)
713 for (side = 0;side < 6;side++)
715 for (y = 0;y < NORMSIZE;y++)
717 for (x = 0;x < NORMSIZE;x++)
719 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
720 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
754 intensity = 127.0f / sqrt(DotProduct(v, v));
755 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
756 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
757 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
758 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
762 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
765 r_shadow_normalcubetexture = NULL;
766 for (y = 0;y < ATTEN2DSIZE;y++)
768 for (x = 0;x < ATTEN2DSIZE;x++)
770 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
771 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
773 intensity = 1.0f - sqrt(DotProduct(v, v));
775 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
776 d = bound(0, intensity, 255);
777 data[(y*ATTEN2DSIZE+x)*4+0] = d;
778 data[(y*ATTEN2DSIZE+x)*4+1] = d;
779 data[(y*ATTEN2DSIZE+x)*4+2] = d;
780 data[(y*ATTEN2DSIZE+x)*4+3] = d;
783 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
784 if (r_shadow_texture3d.integer)
786 for (z = 0;z < ATTEN3DSIZE;z++)
788 for (y = 0;y < ATTEN3DSIZE;y++)
790 for (x = 0;x < ATTEN3DSIZE;x++)
792 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
793 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
794 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
795 intensity = 1.0f - sqrt(DotProduct(v, v));
797 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
798 d = bound(0, intensity, 255);
799 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
800 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
801 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
802 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
806 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
811 void R_Shadow_Stage_Begin(void)
815 if (r_shadow_texture3d.integer && !gl_texture3d)
816 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
818 if (!r_shadow_attenuation2dtexture
819 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
820 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
821 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
822 R_Shadow_MakeTextures();
824 memset(&m, 0, sizeof(m));
825 GL_BlendFunc(GL_ONE, GL_ZERO);
828 R_Mesh_State_Texture(&m);
829 GL_Color(0, 0, 0, 1);
830 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
831 qglDisable(GL_SCISSOR_TEST);
832 r_shadowstage = SHADOWSTAGE_NONE;
834 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
835 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
836 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
839 void R_Shadow_LoadWorldLightsIfNeeded(void)
841 if (r_shadow_reloadlights && cl.worldmodel)
843 R_Shadow_ClearWorldLights();
844 r_shadow_reloadlights = false;
845 R_Shadow_LoadWorldLights();
846 if (r_shadow_worldlightchain == NULL)
848 R_Shadow_LoadLightsFile();
849 if (r_shadow_worldlightchain == NULL)
850 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
855 void R_Shadow_Stage_ShadowVolumes(void)
858 memset(&m, 0, sizeof(m));
859 R_Mesh_State_Texture(&m);
860 GL_Color(1, 1, 1, 1);
861 qglColorMask(0, 0, 0, 0);
862 GL_BlendFunc(GL_ONE, GL_ZERO);
865 if (r_shadow_polygonoffset.value != 0)
867 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
868 qglEnable(GL_POLYGON_OFFSET_FILL);
871 qglDisable(GL_POLYGON_OFFSET_FILL);
872 qglDepthFunc(GL_LESS);
873 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
874 qglEnable(GL_STENCIL_TEST);
875 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
876 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
877 r_shadowstage = SHADOWSTAGE_STENCIL;
878 qglClear(GL_STENCIL_BUFFER_BIT);
880 // LordHavoc note: many shadow volumes reside entirely inside the world
881 // (that is to say they are entirely bounded by their lit surfaces),
882 // which can be optimized by handling things as an inverted light volume,
883 // with the shadow boundaries of the world being simulated by an altered
884 // (129) bias to stencil clearing on such lights
885 // FIXME: generate inverted light volumes for use as shadow volumes and
886 // optimize for them as noted above
889 void R_Shadow_Stage_LightWithoutShadows(void)
892 memset(&m, 0, sizeof(m));
893 R_Mesh_State_Texture(&m);
894 GL_BlendFunc(GL_ONE, GL_ONE);
897 qglDisable(GL_POLYGON_OFFSET_FILL);
898 GL_Color(1, 1, 1, 1);
899 qglColorMask(1, 1, 1, 1);
900 qglDepthFunc(GL_EQUAL);
901 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
902 qglDisable(GL_STENCIL_TEST);
903 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
904 qglStencilFunc(GL_EQUAL, 128, 0xFF);
905 r_shadowstage = SHADOWSTAGE_LIGHT;
909 void R_Shadow_Stage_LightWithShadows(void)
912 memset(&m, 0, sizeof(m));
913 R_Mesh_State_Texture(&m);
914 GL_BlendFunc(GL_ONE, GL_ONE);
917 qglDisable(GL_POLYGON_OFFSET_FILL);
918 GL_Color(1, 1, 1, 1);
919 qglColorMask(1, 1, 1, 1);
920 qglDepthFunc(GL_EQUAL);
921 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
922 qglEnable(GL_STENCIL_TEST);
923 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
924 // only draw light where this geometry was already rendered AND the
925 // stencil is 128 (values other than this mean shadow)
926 qglStencilFunc(GL_EQUAL, 128, 0xFF);
927 r_shadowstage = SHADOWSTAGE_LIGHT;
931 void R_Shadow_Stage_End(void)
934 memset(&m, 0, sizeof(m));
935 R_Mesh_State_Texture(&m);
936 GL_BlendFunc(GL_ONE, GL_ZERO);
939 qglDisable(GL_POLYGON_OFFSET_FILL);
940 GL_Color(1, 1, 1, 1);
941 qglColorMask(1, 1, 1, 1);
942 qglDisable(GL_SCISSOR_TEST);
943 qglDepthFunc(GL_LEQUAL);
944 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
945 qglDisable(GL_STENCIL_TEST);
946 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
947 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
948 r_shadowstage = SHADOWSTAGE_NONE;
951 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
953 int i, ix1, iy1, ix2, iy2;
954 float x1, y1, x2, y2, x, y, f;
957 if (!r_shadow_scissor.integer)
959 // if view is inside the box, just say yes it's visible
960 // LordHavoc: for some odd reason scissor seems broken without stencil
961 // (?!? seems like a driver bug) so abort if gl_stencil is false
962 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
964 qglDisable(GL_SCISSOR_TEST);
967 for (i = 0;i < 3;i++)
969 if (r_viewforward[i] >= 0)
980 f = DotProduct(r_viewforward, r_vieworigin) + 1;
981 if (DotProduct(r_viewforward, v2) <= f)
983 // entirely behind nearclip plane
986 if (DotProduct(r_viewforward, v) >= f)
988 // entirely infront of nearclip plane
989 x1 = y1 = x2 = y2 = 0;
990 for (i = 0;i < 8;i++)
992 v[0] = (i & 1) ? mins[0] : maxs[0];
993 v[1] = (i & 2) ? mins[1] : maxs[1];
994 v[2] = (i & 4) ? mins[2] : maxs[2];
996 GL_TransformToScreen(v, v2);
997 //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]);
1016 // clipped by nearclip plane
1017 // this is nasty and crude...
1018 // create viewspace bbox
1019 for (i = 0;i < 8;i++)
1021 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1022 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1023 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1024 v2[0] = -DotProduct(v, r_viewleft);
1025 v2[1] = DotProduct(v, r_viewup);
1026 v2[2] = DotProduct(v, r_viewforward);
1029 if (smins[0] > v2[0]) smins[0] = v2[0];
1030 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1031 if (smins[1] > v2[1]) smins[1] = v2[1];
1032 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1033 if (smins[2] > v2[2]) smins[2] = v2[2];
1034 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1038 smins[0] = smaxs[0] = v2[0];
1039 smins[1] = smaxs[1] = v2[1];
1040 smins[2] = smaxs[2] = v2[2];
1043 // now we have a bbox in viewspace
1044 // clip it to the view plane
1047 // return true if that culled the box
1048 if (smins[2] >= smaxs[2])
1050 // ok some of it is infront of the view, transform each corner back to
1051 // worldspace and then to screenspace and make screen rect
1052 // initialize these variables just to avoid compiler warnings
1053 x1 = y1 = x2 = y2 = 0;
1054 for (i = 0;i < 8;i++)
1056 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1057 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1058 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1059 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1060 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1061 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1063 GL_TransformToScreen(v, v2);
1064 //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]);
1081 // this code doesn't handle boxes with any points behind view properly
1082 x1 = 1000;x2 = -1000;
1083 y1 = 1000;y2 = -1000;
1084 for (i = 0;i < 8;i++)
1086 v[0] = (i & 1) ? mins[0] : maxs[0];
1087 v[1] = (i & 2) ? mins[1] : maxs[1];
1088 v[2] = (i & 4) ? mins[2] : maxs[2];
1090 GL_TransformToScreen(v, v2);
1091 //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]);
1109 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1110 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1111 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1112 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1113 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1114 if (ix2 <= ix1 || iy2 <= iy1)
1116 // set up the scissor rectangle
1117 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1118 qglEnable(GL_SCISSOR_TEST);
1123 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1125 float *color4f = varray_color4f;
1126 float dist, dot, intensity, v[3], n[3];
1127 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1129 Matrix4x4_Transform(m, vertex3f, v);
1130 if ((dist = DotProduct(v, v)) < 1)
1132 Matrix4x4_Transform3x3(m, normal3f, n);
1133 if ((dot = DotProduct(n, v)) > 0)
1136 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1137 VectorScale(lightcolor, intensity, color4f);
1142 VectorClear(color4f);
1148 VectorClear(color4f);
1154 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1156 float *color4f = varray_color4f;
1157 float dist, dot, intensity, v[3], n[3];
1158 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1160 Matrix4x4_Transform(m, vertex3f, v);
1161 if ((dist = fabs(v[2])) < 1)
1163 Matrix4x4_Transform3x3(m, normal3f, n);
1164 if ((dot = DotProduct(n, v)) > 0)
1166 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1167 VectorScale(lightcolor, intensity, color4f);
1172 VectorClear(color4f);
1178 VectorClear(color4f);
1184 // FIXME: this should be done in a vertex program when possible
1185 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1186 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1190 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1191 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1192 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1199 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1203 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1204 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1211 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)
1215 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1217 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1218 // the cubemap normalizes this for us
1219 out3f[0] = DotProduct(svector3f, lightdir);
1220 out3f[1] = DotProduct(tvector3f, lightdir);
1221 out3f[2] = DotProduct(normal3f, lightdir);
1225 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)
1228 float lightdir[3], eyedir[3], halfdir[3];
1229 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1231 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1232 VectorNormalizeFast(lightdir);
1233 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1234 VectorNormalizeFast(eyedir);
1235 VectorAdd(lightdir, eyedir, halfdir);
1236 // the cubemap normalizes this for us
1237 out3f[0] = DotProduct(svector3f, halfdir);
1238 out3f[1] = DotProduct(tvector3f, halfdir);
1239 out3f[2] = DotProduct(normal3f, halfdir);
1243 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)
1246 float color[3], color2[3];
1248 GL_VertexPointer(vertex3f);
1249 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1252 bumptexture = r_shadow_blankbumptexture;
1254 // colorscale accounts for how much we multiply the brightness during combine
1255 // mult is how many times the final pass of the lighting will be
1256 // performed to get more brightness than otherwise possible
1257 // limit mult to 64 for sanity sake
1258 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1260 // 3/2 3D combine path (Geforce3, Radeon 8500)
1261 memset(&m, 0, sizeof(m));
1262 m.tex[0] = R_GetTexture(bumptexture);
1263 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1264 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1265 m.texcombinergb[0] = GL_REPLACE;
1266 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1267 m.pointer_texcoord[0] = texcoord2f;
1268 m.pointer_texcoord[1] = varray_texcoord3f[1];
1269 m.pointer_texcoord[2] = varray_texcoord3f[2];
1270 R_Mesh_State_Texture(&m);
1271 qglColorMask(0,0,0,1);
1272 GL_BlendFunc(GL_ONE, GL_ZERO);
1273 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1274 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1275 R_Mesh_Draw(numverts, numtriangles, elements);
1277 c_rt_lighttris += numtriangles;
1279 memset(&m, 0, sizeof(m));
1280 m.tex[0] = R_GetTexture(basetexture);
1281 m.texcubemap[1] = R_GetTexture(lightcubemap);
1282 m.pointer_texcoord[0] = texcoord2f;
1283 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1284 R_Mesh_State_Texture(&m);
1285 qglColorMask(1,1,1,0);
1286 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1288 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1289 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1290 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1292 color[0] = bound(0, color2[0], 1);
1293 color[1] = bound(0, color2[1], 1);
1294 color[2] = bound(0, color2[2], 1);
1295 GL_Color(color[0], color[1], color[2], 1);
1296 R_Mesh_Draw(numverts, numtriangles, elements);
1298 c_rt_lighttris += numtriangles;
1301 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1303 // 1/2/2 3D combine path (original Radeon)
1304 memset(&m, 0, sizeof(m));
1305 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1306 m.pointer_texcoord[0] = varray_texcoord3f[0];
1307 R_Mesh_State_Texture(&m);
1308 qglColorMask(0,0,0,1);
1309 GL_BlendFunc(GL_ONE, GL_ZERO);
1310 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1311 R_Mesh_Draw(numverts, numtriangles, elements);
1313 c_rt_lighttris += numtriangles;
1315 memset(&m, 0, sizeof(m));
1316 m.tex[0] = R_GetTexture(bumptexture);
1317 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1318 m.texcombinergb[0] = GL_REPLACE;
1319 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1320 m.pointer_texcoord[0] = texcoord2f;
1321 m.pointer_texcoord[1] = varray_texcoord3f[1];
1322 R_Mesh_State_Texture(&m);
1323 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1324 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1325 R_Mesh_Draw(numverts, numtriangles, elements);
1327 c_rt_lighttris += numtriangles;
1329 memset(&m, 0, sizeof(m));
1330 m.tex[0] = R_GetTexture(basetexture);
1331 m.texcubemap[1] = R_GetTexture(lightcubemap);
1332 m.pointer_texcoord[0] = texcoord2f;
1333 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1334 R_Mesh_State_Texture(&m);
1335 qglColorMask(1,1,1,0);
1336 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1338 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1339 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1340 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1342 color[0] = bound(0, color2[0], 1);
1343 color[1] = bound(0, color2[1], 1);
1344 color[2] = bound(0, color2[2], 1);
1345 GL_Color(color[0], color[1], color[2], 1);
1346 R_Mesh_Draw(numverts, numtriangles, elements);
1348 c_rt_lighttris += numtriangles;
1351 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1353 // 2/2 3D combine path (original Radeon)
1354 memset(&m, 0, sizeof(m));
1355 m.tex[0] = R_GetTexture(bumptexture);
1356 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1357 m.texcombinergb[0] = GL_REPLACE;
1358 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1359 m.pointer_texcoord[0] = texcoord2f;
1360 m.pointer_texcoord[1] = varray_texcoord3f[1];
1361 R_Mesh_State_Texture(&m);
1362 qglColorMask(0,0,0,1);
1363 GL_BlendFunc(GL_ONE, GL_ZERO);
1364 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1365 R_Mesh_Draw(numverts, numtriangles, elements);
1367 c_rt_lighttris += numtriangles;
1369 memset(&m, 0, sizeof(m));
1370 m.tex[0] = R_GetTexture(basetexture);
1371 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1372 m.pointer_texcoord[0] = texcoord2f;
1373 m.pointer_texcoord[1] = varray_texcoord3f[1];
1374 R_Mesh_State_Texture(&m);
1375 qglColorMask(1,1,1,0);
1376 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1377 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1378 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1379 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1381 color[0] = bound(0, color2[0], 1);
1382 color[1] = bound(0, color2[1], 1);
1383 color[2] = bound(0, color2[2], 1);
1384 GL_Color(color[0], color[1], color[2], 1);
1385 R_Mesh_Draw(numverts, numtriangles, elements);
1387 c_rt_lighttris += numtriangles;
1390 else if (r_textureunits.integer >= 4)
1392 // 4/2 2D combine path (Geforce3, Radeon 8500)
1393 memset(&m, 0, sizeof(m));
1394 m.tex[0] = R_GetTexture(bumptexture);
1395 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1396 m.texcombinergb[0] = GL_REPLACE;
1397 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1398 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1399 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1400 m.pointer_texcoord[0] = texcoord2f;
1401 m.pointer_texcoord[1] = varray_texcoord3f[1];
1402 m.pointer_texcoord[2] = varray_texcoord2f[2];
1403 m.pointer_texcoord[3] = varray_texcoord2f[3];
1404 R_Mesh_State_Texture(&m);
1405 qglColorMask(0,0,0,1);
1406 GL_BlendFunc(GL_ONE, GL_ZERO);
1407 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1408 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1409 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1410 R_Mesh_Draw(numverts, numtriangles, elements);
1412 c_rt_lighttris += numtriangles;
1414 memset(&m, 0, sizeof(m));
1415 m.tex[0] = R_GetTexture(basetexture);
1416 m.texcubemap[1] = R_GetTexture(lightcubemap);
1417 m.pointer_texcoord[0] = texcoord2f;
1418 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1419 R_Mesh_State_Texture(&m);
1420 qglColorMask(1,1,1,0);
1421 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1423 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1424 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1425 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1427 color[0] = bound(0, color2[0], 1);
1428 color[1] = bound(0, color2[1], 1);
1429 color[2] = bound(0, color2[2], 1);
1430 GL_Color(color[0], color[1], color[2], 1);
1431 R_Mesh_Draw(numverts, numtriangles, elements);
1433 c_rt_lighttris += numtriangles;
1438 // 2/2/2 2D combine path (any dot3 card)
1439 memset(&m, 0, sizeof(m));
1440 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1441 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1442 m.pointer_texcoord[0] = varray_texcoord2f[0];
1443 m.pointer_texcoord[1] = varray_texcoord2f[1];
1444 R_Mesh_State_Texture(&m);
1445 qglColorMask(0,0,0,1);
1446 GL_BlendFunc(GL_ONE, GL_ZERO);
1447 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1448 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1449 R_Mesh_Draw(numverts, numtriangles, elements);
1451 c_rt_lighttris += numtriangles;
1453 memset(&m, 0, sizeof(m));
1454 m.tex[0] = R_GetTexture(bumptexture);
1455 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1456 m.texcombinergb[0] = GL_REPLACE;
1457 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1458 m.pointer_texcoord[0] = texcoord2f;
1459 m.pointer_texcoord[1] = varray_texcoord3f[1];
1460 R_Mesh_State_Texture(&m);
1461 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1462 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1463 R_Mesh_Draw(numverts, numtriangles, elements);
1465 c_rt_lighttris += numtriangles;
1467 memset(&m, 0, sizeof(m));
1468 m.tex[0] = R_GetTexture(basetexture);
1469 m.texcubemap[1] = R_GetTexture(lightcubemap);
1470 m.pointer_texcoord[0] = texcoord2f;
1471 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1472 R_Mesh_State_Texture(&m);
1473 qglColorMask(1,1,1,0);
1474 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1476 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1477 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1478 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1480 color[0] = bound(0, color2[0], 1);
1481 color[1] = bound(0, color2[1], 1);
1482 color[2] = bound(0, color2[2], 1);
1483 GL_Color(color[0], color[1], color[2], 1);
1484 R_Mesh_Draw(numverts, numtriangles, elements);
1486 c_rt_lighttris += numtriangles;
1492 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1493 GL_DepthMask(false);
1495 GL_ColorPointer(varray_color4f);
1496 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1497 memset(&m, 0, sizeof(m));
1498 m.tex[0] = R_GetTexture(basetexture);
1499 m.pointer_texcoord[0] = texcoord2f;
1500 if (r_textureunits.integer >= 2)
1503 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1504 m.pointer_texcoord[1] = varray_texcoord2f[1];
1505 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1507 R_Mesh_State_Texture(&m);
1508 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1510 color[0] = bound(0, color2[0], 1);
1511 color[1] = bound(0, color2[1], 1);
1512 color[2] = bound(0, color2[2], 1);
1513 if (r_textureunits.integer >= 2)
1514 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1516 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1517 R_Mesh_Draw(numverts, numtriangles, elements);
1519 c_rt_lighttris += numtriangles;
1524 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)
1527 float color[3], color2[3], colorscale;
1529 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1532 glosstexture = r_shadow_blankglosstexture;
1533 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1535 colorscale = r_shadow_glossintensity.value;
1537 bumptexture = r_shadow_blankbumptexture;
1538 if (glosstexture == r_shadow_blankglosstexture)
1539 colorscale *= r_shadow_gloss2intensity.value;
1540 GL_VertexPointer(vertex3f);
1542 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1544 // 2/0/0/1/2 3D combine blendsquare path
1545 memset(&m, 0, sizeof(m));
1546 m.tex[0] = R_GetTexture(bumptexture);
1547 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1548 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1549 m.pointer_texcoord[0] = texcoord2f;
1550 m.pointer_texcoord[1] = varray_texcoord3f[1];
1551 R_Mesh_State_Texture(&m);
1552 qglColorMask(0,0,0,1);
1553 // this squares the result
1554 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1555 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1556 R_Mesh_Draw(numverts, numtriangles, elements);
1558 c_rt_lighttris += numtriangles;
1560 memset(&m, 0, sizeof(m));
1561 R_Mesh_State_Texture(&m);
1562 // square alpha in framebuffer a few times to make it shiny
1563 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1564 // these comments are a test run through this math for intensity 0.5
1565 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1566 // 0.25 * 0.25 = 0.0625 (this is another pass)
1567 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1568 R_Mesh_Draw(numverts, numtriangles, elements);
1570 c_rt_lighttris += numtriangles;
1571 R_Mesh_Draw(numverts, numtriangles, elements);
1573 c_rt_lighttris += numtriangles;
1575 memset(&m, 0, sizeof(m));
1576 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1577 m.pointer_texcoord[0] = varray_texcoord3f[0];
1578 R_Mesh_State_Texture(&m);
1579 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1580 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1581 R_Mesh_Draw(numverts, numtriangles, elements);
1583 c_rt_lighttris += numtriangles;
1585 memset(&m, 0, sizeof(m));
1586 m.tex[0] = R_GetTexture(glosstexture);
1587 m.texcubemap[1] = R_GetTexture(lightcubemap);
1588 m.pointer_texcoord[0] = texcoord2f;
1589 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1590 R_Mesh_State_Texture(&m);
1591 qglColorMask(1,1,1,0);
1592 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1594 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1595 VectorScale(lightcolor, colorscale, color2);
1596 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1598 color[0] = bound(0, color2[0], 1);
1599 color[1] = bound(0, color2[1], 1);
1600 color[2] = bound(0, color2[2], 1);
1601 GL_Color(color[0], color[1], color[2], 1);
1602 R_Mesh_Draw(numverts, numtriangles, elements);
1604 c_rt_lighttris += numtriangles;
1607 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1609 // 2/0/0/2 3D combine blendsquare path
1610 memset(&m, 0, sizeof(m));
1611 m.tex[0] = R_GetTexture(bumptexture);
1612 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1613 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1614 m.pointer_texcoord[0] = texcoord2f;
1615 m.pointer_texcoord[1] = varray_texcoord3f[1];
1616 R_Mesh_State_Texture(&m);
1617 qglColorMask(0,0,0,1);
1618 // this squares the result
1619 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1620 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1621 R_Mesh_Draw(numverts, numtriangles, elements);
1623 c_rt_lighttris += numtriangles;
1625 memset(&m, 0, sizeof(m));
1626 R_Mesh_State_Texture(&m);
1627 // square alpha in framebuffer a few times to make it shiny
1628 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1629 // these comments are a test run through this math for intensity 0.5
1630 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1631 // 0.25 * 0.25 = 0.0625 (this is another pass)
1632 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1633 R_Mesh_Draw(numverts, numtriangles, elements);
1635 c_rt_lighttris += numtriangles;
1636 R_Mesh_Draw(numverts, numtriangles, elements);
1638 c_rt_lighttris += numtriangles;
1640 memset(&m, 0, sizeof(m));
1641 m.tex[0] = R_GetTexture(glosstexture);
1642 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1643 m.pointer_texcoord[0] = texcoord2f;
1644 m.pointer_texcoord[1] = varray_texcoord3f[1];
1645 R_Mesh_State_Texture(&m);
1646 qglColorMask(1,1,1,0);
1647 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1648 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1649 VectorScale(lightcolor, colorscale, color2);
1650 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1652 color[0] = bound(0, color2[0], 1);
1653 color[1] = bound(0, color2[1], 1);
1654 color[2] = bound(0, color2[2], 1);
1655 GL_Color(color[0], color[1], color[2], 1);
1656 R_Mesh_Draw(numverts, numtriangles, elements);
1658 c_rt_lighttris += numtriangles;
1661 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1663 // 2/0/0/2/2 2D combine blendsquare path
1664 memset(&m, 0, sizeof(m));
1665 m.tex[0] = R_GetTexture(bumptexture);
1666 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1667 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1668 m.pointer_texcoord[0] = texcoord2f;
1669 m.pointer_texcoord[1] = varray_texcoord3f[1];
1670 R_Mesh_State_Texture(&m);
1671 qglColorMask(0,0,0,1);
1672 // this squares the result
1673 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1674 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1675 R_Mesh_Draw(numverts, numtriangles, elements);
1677 c_rt_lighttris += numtriangles;
1679 memset(&m, 0, sizeof(m));
1680 R_Mesh_State_Texture(&m);
1681 // square alpha in framebuffer a few times to make it shiny
1682 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1683 // these comments are a test run through this math for intensity 0.5
1684 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1685 // 0.25 * 0.25 = 0.0625 (this is another pass)
1686 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1687 R_Mesh_Draw(numverts, numtriangles, elements);
1689 c_rt_lighttris += numtriangles;
1690 R_Mesh_Draw(numverts, numtriangles, elements);
1692 c_rt_lighttris += numtriangles;
1694 memset(&m, 0, sizeof(m));
1695 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1696 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1697 m.pointer_texcoord[0] = varray_texcoord2f[0];
1698 m.pointer_texcoord[1] = varray_texcoord2f[1];
1699 R_Mesh_State_Texture(&m);
1700 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1701 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1702 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1703 R_Mesh_Draw(numverts, numtriangles, elements);
1705 c_rt_lighttris += numtriangles;
1707 memset(&m, 0, sizeof(m));
1708 m.tex[0] = R_GetTexture(glosstexture);
1709 m.texcubemap[1] = R_GetTexture(lightcubemap);
1710 m.pointer_texcoord[0] = texcoord2f;
1711 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1712 R_Mesh_State_Texture(&m);
1713 qglColorMask(1,1,1,0);
1714 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1716 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1717 VectorScale(lightcolor, colorscale, color2);
1718 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1720 color[0] = bound(0, color2[0], 1);
1721 color[1] = bound(0, color2[1], 1);
1722 color[2] = bound(0, color2[2], 1);
1723 GL_Color(color[0], color[1], color[2], 1);
1724 R_Mesh_Draw(numverts, numtriangles, elements);
1726 c_rt_lighttris += numtriangles;
1732 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1734 R_Mesh_Matrix(matrix);
1735 if (r_shadow_showtris.integer)
1739 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1740 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1741 qglDisable(GL_DEPTH_TEST);
1742 qglDisable(GL_STENCIL_TEST);
1743 //qglDisable(GL_CULL_FACE);
1744 qglColorMask(1,1,1,1);
1745 memset(&m, 0, sizeof(m));
1746 R_Mesh_State_Texture(&m);
1747 GL_Color(0,0.1,0,1);
1748 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1749 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1751 GL_VertexPointer(mesh->vertex3f);
1752 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1754 //qglEnable(GL_CULL_FACE);
1756 qglEnable(GL_DEPTH_TEST);
1759 qglEnable(GL_STENCIL_TEST);
1760 qglColorMask(0,0,0,0);
1763 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1766 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)
1769 R_Mesh_Matrix(matrix);
1770 if (r_shadow_showtris.integer)
1773 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1774 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1775 qglDisable(GL_DEPTH_TEST);
1776 qglDisable(GL_STENCIL_TEST);
1777 //qglDisable(GL_CULL_FACE);
1778 memset(&m, 0, sizeof(m));
1779 R_Mesh_State_Texture(&m);
1780 GL_Color(0.2,0,0,1);
1781 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1782 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1784 GL_VertexPointer(mesh->vertex3f);
1785 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1787 //qglEnable(GL_CULL_FACE);
1789 qglEnable(GL_DEPTH_TEST);
1791 qglEnable(GL_STENCIL_TEST);
1793 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1795 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);
1796 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);
1800 cvar_t r_editlights = {0, "r_editlights", "0"};
1801 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1802 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1803 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1804 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1805 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1806 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1807 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1808 worldlight_t *r_shadow_worldlightchain;
1809 worldlight_t *r_shadow_selectedlight;
1810 vec3_t r_editlights_cursorlocation;
1812 static int lightpvsbytes;
1813 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1815 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1817 int i, j, k, l, maxverts = 256, tris;
1818 float *vertex3f = NULL, mins[3], maxs[3];
1820 shadowmesh_t *mesh, *castmesh = NULL;
1822 if (radius < 15 || DotProduct(color, color) < 0.03)
1824 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1828 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1829 VectorCopy(origin, e->origin);
1830 VectorCopy(color, e->light);
1831 e->lightradius = radius;
1833 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1835 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1838 e->castshadows = castshadow;
1840 e->cullradius = e->lightradius;
1841 for (k = 0;k < 3;k++)
1843 mins[k] = e->origin[k] - e->lightradius;
1844 maxs[k] = e->origin[k] + e->lightradius;
1847 e->next = r_shadow_worldlightchain;
1848 r_shadow_worldlightchain = e;
1849 if (cubemapname && cubemapname[0])
1851 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1852 strcpy(e->cubemapname, cubemapname);
1853 // FIXME: add cubemap loading (and don't load a cubemap twice)
1855 // FIXME: rewrite this to store ALL geometry into a cache in the light
1857 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1858 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1861 if (cl.worldmodel->brushq3.num_leafs)
1865 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1866 VectorCopy(e->origin, e->mins);
1867 VectorCopy(e->origin, e->maxs);
1868 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1869 face->lighttemp_castshadow = false;
1870 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1872 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1874 for (k = 0;k < 3;k++)
1876 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1877 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1879 for (j = 0;j < leaf->numleaffaces;j++)
1881 face = leaf->firstleafface[j];
1882 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1883 face->lighttemp_castshadow = true;
1888 // add surfaces to shadow casting mesh and light mesh
1889 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1891 if (face->lighttemp_castshadow)
1893 face->lighttemp_castshadow = false;
1894 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1897 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1898 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1899 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1900 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);
1905 else if (cl.worldmodel->brushq1.numleafs)
1909 VectorCopy(e->origin, e->mins);
1910 VectorCopy(e->origin, e->maxs);
1911 i = CL_PointQ1Contents(e->origin);
1913 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1914 surf->lighttemp_castshadow = false;
1916 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1919 qbyte *bytesurfacepvs;
1921 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1922 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1924 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1926 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1928 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1930 for (k = 0;k < 3;k++)
1932 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1933 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1938 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1939 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1940 surf->lighttemp_castshadow = true;
1942 Mem_Free(byteleafpvs);
1943 Mem_Free(bytesurfacepvs);
1947 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1948 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1950 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1952 for (k = 0;k < 3;k++)
1954 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1955 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1957 for (j = 0;j < leaf->nummarksurfaces;j++)
1959 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1960 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1961 surf->lighttemp_castshadow = true;
1967 // add surfaces to shadow casting mesh and light mesh
1968 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1970 if (surf->lighttemp_castshadow)
1972 surf->lighttemp_castshadow = false;
1973 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
1974 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);
1975 if (!(surf->flags & SURF_DRAWSKY))
1976 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);
1982 // limit box to light bounds (in case it grew larger)
1983 for (k = 0;k < 3;k++)
1985 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1986 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1988 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1990 // cast shadow volume from castmesh
1991 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1995 for (mesh = castmesh;mesh;mesh = mesh->next)
1997 R_Shadow_ResizeShadowElements(mesh->numtriangles);
1998 maxverts = max(maxverts, mesh->numverts * 2);
2003 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2004 // now that we have the buffers big enough, construct and add
2005 // the shadow volume mesh
2007 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2008 for (mesh = castmesh;mesh;mesh = mesh->next)
2010 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2011 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)))
2012 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2017 // we're done with castmesh now
2018 Mod_ShadowMesh_Free(castmesh);
2021 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2022 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2025 if (e->meshchain_shadow)
2026 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2027 k += mesh->numtriangles;
2029 if (e->meshchain_light)
2030 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2031 l += mesh->numtriangles;
2032 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);
2035 void R_Shadow_FreeWorldLight(worldlight_t *light)
2037 worldlight_t **lightpointer;
2038 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2039 if (*lightpointer != light)
2040 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2041 *lightpointer = light->next;
2042 if (light->cubemapname)
2043 Mem_Free(light->cubemapname);
2044 if (light->meshchain_shadow)
2045 Mod_ShadowMesh_Free(light->meshchain_shadow);
2046 if (light->meshchain_light)
2047 Mod_ShadowMesh_Free(light->meshchain_light);
2051 void R_Shadow_ClearWorldLights(void)
2053 while (r_shadow_worldlightchain)
2054 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2055 r_shadow_selectedlight = NULL;
2058 void R_Shadow_SelectLight(worldlight_t *light)
2060 if (r_shadow_selectedlight)
2061 r_shadow_selectedlight->selected = false;
2062 r_shadow_selectedlight = light;
2063 if (r_shadow_selectedlight)
2064 r_shadow_selectedlight->selected = true;
2067 rtexture_t *lighttextures[5];
2069 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2071 float scale = r_editlights_cursorgrid.value * 0.5f;
2072 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);
2075 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2078 const worldlight_t *light;
2081 if (light->selected)
2082 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2083 if (!light->meshchain_shadow)
2085 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);
2088 void R_Shadow_DrawLightSprites(void)
2092 worldlight_t *light;
2094 for (i = 0;i < 5;i++)
2096 lighttextures[i] = NULL;
2097 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2098 lighttextures[i] = pic->tex;
2101 for (light = r_shadow_worldlightchain;light;light = light->next)
2102 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2103 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2106 void R_Shadow_SelectLightInView(void)
2108 float bestrating, rating, temp[3];
2109 worldlight_t *best, *light;
2112 for (light = r_shadow_worldlightchain;light;light = light->next)
2114 VectorSubtract(light->origin, r_vieworigin, temp);
2115 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2118 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2119 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2121 bestrating = rating;
2126 R_Shadow_SelectLight(best);
2129 void R_Shadow_LoadWorldLights(void)
2131 int n, a, style, shadow;
2132 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2133 float origin[3], radius, color[3];
2134 if (cl.worldmodel == NULL)
2136 Con_Printf("No map loaded.\n");
2139 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2140 strlcat (name, ".rtlights", sizeof (name));
2141 lightsstring = FS_LoadFile(name, false);
2149 while (*s && *s != '\n')
2155 // check for modifier flags
2161 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);
2167 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);
2170 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2171 radius *= r_editlights_rtlightssizescale.value;
2172 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2177 Con_Printf("invalid rtlights file \"%s\"\n", name);
2178 Mem_Free(lightsstring);
2182 void R_Shadow_SaveWorldLights(void)
2184 worldlight_t *light;
2185 int bufchars, bufmaxchars;
2187 char name[MAX_QPATH];
2189 if (!r_shadow_worldlightchain)
2191 if (cl.worldmodel == NULL)
2193 Con_Printf("No map loaded.\n");
2196 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2197 strlcat (name, ".rtlights", sizeof (name));
2198 bufchars = bufmaxchars = 0;
2200 for (light = r_shadow_worldlightchain;light;light = light->next)
2202 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 : "");
2203 if (bufchars + (int) strlen(line) > bufmaxchars)
2205 bufmaxchars = bufchars + strlen(line) + 2048;
2207 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2211 memcpy(buf, oldbuf, bufchars);
2217 memcpy(buf + bufchars, line, strlen(line));
2218 bufchars += strlen(line);
2222 FS_WriteFile(name, buf, bufchars);
2227 void R_Shadow_LoadLightsFile(void)
2230 char name[MAX_QPATH], *lightsstring, *s, *t;
2231 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2232 if (cl.worldmodel == NULL)
2234 Con_Printf("No map loaded.\n");
2237 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2238 strlcat (name, ".lights", sizeof (name));
2239 lightsstring = FS_LoadFile(name, false);
2247 while (*s && *s != '\n')
2252 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);
2256 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);
2259 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2260 radius = bound(15, radius, 4096);
2261 VectorScale(color, (2.0f / (8388608.0f)), color);
2262 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2267 Con_Printf("invalid lights file \"%s\"\n", name);
2268 Mem_Free(lightsstring);
2272 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2274 int entnum, style, islight;
2275 char key[256], value[1024];
2276 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2279 if (cl.worldmodel == NULL)
2281 Con_Printf("No map loaded.\n");
2284 data = cl.worldmodel->brush.entities;
2287 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2290 origin[0] = origin[1] = origin[2] = 0;
2291 originhack[0] = originhack[1] = originhack[2] = 0;
2292 color[0] = color[1] = color[2] = 1;
2293 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2300 if (!COM_ParseToken(&data, false))
2302 if (com_token[0] == '}')
2303 break; // end of entity
2304 if (com_token[0] == '_')
2305 strcpy(key, com_token + 1);
2307 strcpy(key, com_token);
2308 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2309 key[strlen(key)-1] = 0;
2310 if (!COM_ParseToken(&data, false))
2312 strcpy(value, com_token);
2314 // now that we have the key pair worked out...
2315 if (!strcmp("light", key))
2316 light = atof(value);
2317 else if (!strcmp("origin", key))
2318 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2319 else if (!strcmp("color", key))
2320 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2321 else if (!strcmp("wait", key))
2322 fadescale = atof(value);
2323 else if (!strcmp("classname", key))
2325 if (!strncmp(value, "light", 5))
2328 if (!strcmp(value, "light_fluoro"))
2333 overridecolor[0] = 1;
2334 overridecolor[1] = 1;
2335 overridecolor[2] = 1;
2337 if (!strcmp(value, "light_fluorospark"))
2342 overridecolor[0] = 1;
2343 overridecolor[1] = 1;
2344 overridecolor[2] = 1;
2346 if (!strcmp(value, "light_globe"))
2351 overridecolor[0] = 1;
2352 overridecolor[1] = 0.8;
2353 overridecolor[2] = 0.4;
2355 if (!strcmp(value, "light_flame_large_yellow"))
2360 overridecolor[0] = 1;
2361 overridecolor[1] = 0.5;
2362 overridecolor[2] = 0.1;
2364 if (!strcmp(value, "light_flame_small_yellow"))
2369 overridecolor[0] = 1;
2370 overridecolor[1] = 0.5;
2371 overridecolor[2] = 0.1;
2373 if (!strcmp(value, "light_torch_small_white"))
2378 overridecolor[0] = 1;
2379 overridecolor[1] = 0.5;
2380 overridecolor[2] = 0.1;
2382 if (!strcmp(value, "light_torch_small_walltorch"))
2387 overridecolor[0] = 1;
2388 overridecolor[1] = 0.5;
2389 overridecolor[2] = 0.1;
2393 else if (!strcmp("style", key))
2394 style = atoi(value);
2395 else if (cl.worldmodel->type == mod_brushq3)
2397 if (!strcmp("scale", key))
2398 lightscale = atof(value);
2399 if (!strcmp("fade", key))
2400 fadescale = atof(value);
2403 if (light <= 0 && islight)
2405 if (lightscale <= 0)
2409 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2410 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2411 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2412 VectorCopy(overridecolor, color);
2413 VectorScale(color, light, color);
2414 VectorAdd(origin, originhack, origin);
2416 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2421 void R_Shadow_SetCursorLocationForView(void)
2423 vec_t dist, push, frac;
2424 vec3_t dest, endpos, normal;
2425 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2426 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2429 dist = frac * r_editlights_cursordistance.value;
2430 push = r_editlights_cursorpushback.value;
2434 VectorMA(endpos, push, r_viewforward, endpos);
2435 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2437 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2438 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2439 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2442 void R_Shadow_UpdateWorldLightSelection(void)
2444 if (r_editlights.integer)
2446 R_Shadow_SetCursorLocationForView();
2447 R_Shadow_SelectLightInView();
2448 R_Shadow_DrawLightSprites();
2451 R_Shadow_SelectLight(NULL);
2454 void R_Shadow_EditLights_Clear_f(void)
2456 R_Shadow_ClearWorldLights();
2459 void R_Shadow_EditLights_Reload_f(void)
2461 r_shadow_reloadlights = true;
2464 void R_Shadow_EditLights_Save_f(void)
2467 R_Shadow_SaveWorldLights();
2470 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2472 R_Shadow_ClearWorldLights();
2473 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2476 void R_Shadow_EditLights_ImportLightsFile_f(void)
2478 R_Shadow_ClearWorldLights();
2479 R_Shadow_LoadLightsFile();
2482 void R_Shadow_EditLights_Spawn_f(void)
2485 if (!r_editlights.integer)
2487 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2490 if (Cmd_Argc() != 1)
2492 Con_Printf("r_editlights_spawn does not take parameters\n");
2495 color[0] = color[1] = color[2] = 1;
2496 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2499 void R_Shadow_EditLights_Edit_f(void)
2501 vec3_t origin, color;
2504 char cubemapname[1024];
2505 if (!r_editlights.integer)
2507 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2510 if (!r_shadow_selectedlight)
2512 Con_Printf("No selected light.\n");
2515 VectorCopy(r_shadow_selectedlight->origin, origin);
2516 radius = r_shadow_selectedlight->lightradius;
2517 VectorCopy(r_shadow_selectedlight->light, color);
2518 style = r_shadow_selectedlight->style;
2519 if (r_shadow_selectedlight->cubemapname)
2520 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2523 shadows = r_shadow_selectedlight->castshadows;
2524 if (!strcmp(Cmd_Argv(1), "origin"))
2526 if (Cmd_Argc() != 5)
2528 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2531 origin[0] = atof(Cmd_Argv(2));
2532 origin[1] = atof(Cmd_Argv(3));
2533 origin[2] = atof(Cmd_Argv(4));
2535 else if (!strcmp(Cmd_Argv(1), "originx"))
2537 if (Cmd_Argc() != 3)
2539 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2542 origin[0] = atof(Cmd_Argv(2));
2544 else if (!strcmp(Cmd_Argv(1), "originy"))
2546 if (Cmd_Argc() != 3)
2548 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2551 origin[1] = atof(Cmd_Argv(2));
2553 else if (!strcmp(Cmd_Argv(1), "originz"))
2555 if (Cmd_Argc() != 3)
2557 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2560 origin[2] = atof(Cmd_Argv(2));
2562 else if (!strcmp(Cmd_Argv(1), "move"))
2564 if (Cmd_Argc() != 5)
2566 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2569 origin[0] += atof(Cmd_Argv(2));
2570 origin[1] += atof(Cmd_Argv(3));
2571 origin[2] += atof(Cmd_Argv(4));
2573 else if (!strcmp(Cmd_Argv(1), "movex"))
2575 if (Cmd_Argc() != 3)
2577 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2580 origin[0] += atof(Cmd_Argv(2));
2582 else if (!strcmp(Cmd_Argv(1), "movey"))
2584 if (Cmd_Argc() != 3)
2586 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2589 origin[1] += atof(Cmd_Argv(2));
2591 else if (!strcmp(Cmd_Argv(1), "movez"))
2593 if (Cmd_Argc() != 3)
2595 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2598 origin[2] += atof(Cmd_Argv(2));
2600 else if (!strcmp(Cmd_Argv(1), "color"))
2602 if (Cmd_Argc() != 5)
2604 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2607 color[0] = atof(Cmd_Argv(2));
2608 color[1] = atof(Cmd_Argv(3));
2609 color[2] = atof(Cmd_Argv(4));
2611 else if (!strcmp(Cmd_Argv(1), "radius"))
2613 if (Cmd_Argc() != 3)
2615 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2618 radius = atof(Cmd_Argv(2));
2620 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2622 if (Cmd_Argc() != 3)
2624 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2627 style = atoi(Cmd_Argv(2));
2629 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2633 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2636 if (Cmd_Argc() == 3)
2637 strcpy(cubemapname, Cmd_Argv(2));
2641 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2643 if (Cmd_Argc() != 3)
2645 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2648 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2652 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2653 Con_Printf("Selected light's properties:\n");
2654 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2655 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2656 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2657 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2658 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2659 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2662 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2663 r_shadow_selectedlight = NULL;
2664 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2667 extern int con_vislines;
2668 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2672 if (r_shadow_selectedlight == NULL)
2676 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2677 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;
2678 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2679 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;
2680 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2681 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2682 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;
2685 void R_Shadow_EditLights_ToggleShadow_f(void)
2687 if (!r_editlights.integer)
2689 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2692 if (!r_shadow_selectedlight)
2694 Con_Printf("No selected light.\n");
2697 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);
2698 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2699 r_shadow_selectedlight = NULL;
2702 void R_Shadow_EditLights_Remove_f(void)
2704 if (!r_editlights.integer)
2706 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2709 if (!r_shadow_selectedlight)
2711 Con_Printf("No selected light.\n");
2714 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2715 r_shadow_selectedlight = NULL;
2718 void R_Shadow_EditLights_Help_f(void)
2721 "Documentation on r_editlights system:\n"
2723 "r_editlights : enable/disable editing mode\n"
2724 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2725 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2726 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2727 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2728 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2729 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2730 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2732 "r_editlights_help : this help\n"
2733 "r_editlights_clear : remove all lights\n"
2734 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2735 "r_editlights_save : save to .rtlights file\n"
2736 "r_editlights_spawn : create a light with default settings\n"
2737 "r_editlights_edit command : edit selected light - more documentation below\n"
2738 "r_editlights_remove : remove selected light\n"
2739 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2740 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2741 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2743 "origin x y z : set light location\n"
2744 "originx x: set x component of light location\n"
2745 "originy y: set y component of light location\n"
2746 "originz z: set z component of light location\n"
2747 "move x y z : adjust light location\n"
2748 "movex x: adjust x component of light location\n"
2749 "movey y: adjust y component of light location\n"
2750 "movez z: adjust z component of light location\n"
2751 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2752 "radius radius : set radius (size) of light\n"
2753 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2754 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2755 "shadows 1/0 : turn on/off shadows\n"
2756 "<nothing> : print light properties to console\n"
2760 void R_Shadow_EditLights_Init(void)
2762 Cvar_RegisterVariable(&r_editlights);
2763 Cvar_RegisterVariable(&r_editlights_cursordistance);
2764 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2765 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2766 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2767 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2768 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2769 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2770 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2771 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2772 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2773 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2774 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2775 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2776 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2777 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2778 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2779 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);