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"
120 extern void R_Shadow_EditLights_Init(void);
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_ERASESTENCIL 3
127 int r_shadowstage = SHADOWSTAGE_NONE;
128 int r_shadow_reloadlights = false;
130 mempool_t *r_shadow_mempool;
132 int maxshadowelements;
134 int maxtrianglefacinglight;
135 qbyte *trianglefacinglight;
136 int *trianglefacinglightlist;
143 rtexturepool_t *r_shadow_texturepool;
144 rtexture_t *r_shadow_normalcubetexture;
145 rtexture_t *r_shadow_attenuation2dtexture;
146 rtexture_t *r_shadow_attenuation3dtexture;
147 rtexture_t *r_shadow_blankbumptexture;
148 rtexture_t *r_shadow_blankglosstexture;
149 rtexture_t *r_shadow_blankwhitetexture;
151 // used only for light filters (cubemaps)
152 rtexturepool_t *r_shadow_filters_texturepool;
154 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
155 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
156 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
157 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
158 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
159 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
160 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
161 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
162 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
163 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
164 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
165 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
166 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
167 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
168 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
169 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
170 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
171 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
172 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
173 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
174 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
175 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
176 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
178 int c_rt_lights, c_rt_clears, c_rt_scissored;
179 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
180 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
182 void R_Shadow_ClearWorldLights(void);
183 void R_Shadow_SaveWorldLights(void);
184 void R_Shadow_LoadWorldLights(void);
185 void R_Shadow_LoadLightsFile(void);
186 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
188 void r_shadow_start(void)
190 // allocate vertex processing arrays
191 r_shadow_mempool = Mem_AllocPool("R_Shadow");
192 maxshadowelements = 0;
193 shadowelements = NULL;
198 maxtrianglefacinglight = 0;
199 trianglefacinglight = NULL;
200 trianglefacinglightlist = NULL;
201 r_shadow_normalcubetexture = NULL;
202 r_shadow_attenuation2dtexture = NULL;
203 r_shadow_attenuation3dtexture = NULL;
204 r_shadow_blankbumptexture = NULL;
205 r_shadow_blankglosstexture = NULL;
206 r_shadow_blankwhitetexture = NULL;
207 r_shadow_texturepool = NULL;
208 r_shadow_filters_texturepool = NULL;
209 R_Shadow_ClearWorldLights();
210 r_shadow_reloadlights = true;
213 void r_shadow_shutdown(void)
215 R_Shadow_ClearWorldLights();
216 r_shadow_reloadlights = true;
217 r_shadow_normalcubetexture = NULL;
218 r_shadow_attenuation2dtexture = NULL;
219 r_shadow_attenuation3dtexture = NULL;
220 r_shadow_blankbumptexture = NULL;
221 r_shadow_blankglosstexture = NULL;
222 r_shadow_blankwhitetexture = NULL;
223 R_FreeTexturePool(&r_shadow_texturepool);
224 R_FreeTexturePool(&r_shadow_filters_texturepool);
225 maxshadowelements = 0;
226 shadowelements = NULL;
231 maxtrianglefacinglight = 0;
232 trianglefacinglight = NULL;
233 trianglefacinglightlist = NULL;
234 Mem_FreePool(&r_shadow_mempool);
237 void r_shadow_newmap(void)
239 R_Shadow_ClearWorldLights();
240 r_shadow_reloadlights = true;
243 void R_Shadow_Help_f(void)
246 "Documentation on r_shadow system:\n"
248 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
249 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
250 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
251 "r_shadow_realtime_world : use realtime world light rendering\n"
252 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
253 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
254 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
255 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
256 "r_shadow_glossintensity : brightness of textured gloss\n"
257 "r_shadow_gloss2intensity : brightness of forced gloss\n"
258 "r_shadow_debuglight : render only this light number (-1 = all)\n"
259 "r_shadow_scissor : use scissor optimization\n"
260 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
261 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
262 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
263 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
264 "r_shadow_portallight : use portal visibility for static light precomputation\n"
265 "r_shadow_projectdistance : shadow volume projection distance\n"
266 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
267 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
268 "r_shadow_worldshadows : enable world shadows\n"
269 "r_shadow_dlightshadows : enable dlight shadows\n"
271 "r_shadow_help : this help\n"
275 void R_Shadow_Init(void)
277 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
278 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
279 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
280 Cvar_RegisterVariable(&r_shadow_realtime_world);
281 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
282 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
283 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
284 Cvar_RegisterVariable(&r_shadow_gloss);
285 Cvar_RegisterVariable(&r_shadow_glossintensity);
286 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
287 Cvar_RegisterVariable(&r_shadow_debuglight);
288 Cvar_RegisterVariable(&r_shadow_scissor);
289 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
290 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
291 Cvar_RegisterVariable(&r_shadow_polygonfactor);
292 Cvar_RegisterVariable(&r_shadow_polygonoffset);
293 Cvar_RegisterVariable(&r_shadow_portallight);
294 Cvar_RegisterVariable(&r_shadow_projectdistance);
295 Cvar_RegisterVariable(&r_shadow_texture3d);
296 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
297 Cvar_RegisterVariable(&r_shadow_worldshadows);
298 Cvar_RegisterVariable(&r_shadow_dlightshadows);
299 Cvar_RegisterVariable(&r_shadow_showtris);
300 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
301 R_Shadow_EditLights_Init();
302 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
305 void R_Shadow_ResizeTriangleFacingLight(int numtris)
307 // make sure trianglefacinglight is big enough for this volume
308 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
309 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
310 if (maxtrianglefacinglight < numtris)
312 maxtrianglefacinglight = numtris;
313 if (trianglefacinglight)
314 Mem_Free(trianglefacinglight);
315 if (trianglefacinglightlist)
316 Mem_Free(trianglefacinglightlist);
317 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
318 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
322 int *R_Shadow_ResizeShadowElements(int numtris)
324 // make sure shadowelements is big enough for this volume
325 if (maxshadowelements < numtris * 24)
327 maxshadowelements = numtris * 24;
329 Mem_Free(shadowelements);
330 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
332 return shadowelements;
336 // readable version of some code found below
337 //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]))))
338 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
340 float dir0[3], dir1[3], normal[3];
342 // calculate two mostly perpendicular edge directions
343 VectorSubtract(a, b, dir0);
344 VectorSubtract(c, b, dir1);
346 // we have two edge directions, we can calculate a third vector from
347 // them, which is the direction of the surface normal (it's magnitude
349 CrossProduct(dir0, dir1, normal);
351 // compare distance of light along normal, with distance of any point
352 // of the triangle along the same normal (the triangle is planar,
353 // I.E. flat, so all points give the same answer)
354 return DotProduct(p, normal) > DotProduct(a, normal);
356 int checkcastshadowfromedge(int t, int i)
360 if (t >= trianglerange_start && t < trianglerange_end)
362 if (t < i && !trianglefacinglight[t])
373 te = inelement3i + t * 3;
374 v[0] = invertex3f + te[0] * 3;
375 v[1] = invertex3f + te[1] * 3;
376 v[2] = invertex3f + te[2] * 3;
377 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
386 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)
388 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
390 const int *e, *n, *te;
393 // make sure trianglefacinglight is big enough for this volume
394 if (maxtrianglefacinglight < trianglerange_end)
395 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
397 if (maxvertexupdate < innumvertices)
399 maxvertexupdate = innumvertices;
401 Mem_Free(vertexupdate);
403 Mem_Free(vertexremap);
404 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
405 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
409 if (r_shadow_singlepassvolumegeneration.integer)
411 // one pass approach (identify lit/dark faces and generate sides while doing so)
412 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
414 // calculate triangle facing flag
415 v[0] = invertex3f + e[0] * 3;
416 v[1] = invertex3f + e[1] * 3;
417 v[2] = invertex3f + e[2] * 3;
418 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
420 // make sure the vertices are created
421 for (j = 0;j < 3;j++)
423 if (vertexupdate[e[j]] != vertexupdatenum)
425 vertexupdate[e[j]] = vertexupdatenum;
426 vertexremap[e[j]] = outvertices;
427 VectorCopy(v[j], outvertex3f);
428 VectorSubtract(v[j], relativelightorigin, temp);
429 f = projectdistance / VectorLength(temp);
430 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
435 // output the front and back triangles
436 vr[0] = vertexremap[e[0]];
437 vr[1] = vertexremap[e[1]];
438 vr[2] = vertexremap[e[2]];
439 outelement3i[0] = vr[0];
440 outelement3i[1] = vr[1];
441 outelement3i[2] = vr[2];
442 outelement3i[3] = vr[2] + 1;
443 outelement3i[4] = vr[1] + 1;
444 outelement3i[5] = vr[0] + 1;
447 // output the sides (facing outward from this triangle)
449 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
451 outelement3i[0] = vr[1];
452 outelement3i[1] = vr[0];
453 outelement3i[2] = vr[0] + 1;
454 outelement3i[3] = vr[1];
455 outelement3i[4] = vr[0] + 1;
456 outelement3i[5] = vr[1] + 1;
461 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]))))
463 outelement3i[0] = vr[2];
464 outelement3i[1] = vr[1];
465 outelement3i[2] = vr[1] + 1;
466 outelement3i[3] = vr[2];
467 outelement3i[4] = vr[1] + 1;
468 outelement3i[5] = vr[2] + 1;
473 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]))))
475 outelement3i[0] = vr[0];
476 outelement3i[1] = vr[2];
477 outelement3i[2] = vr[2] + 1;
478 outelement3i[3] = vr[0];
479 outelement3i[4] = vr[2] + 1;
480 outelement3i[5] = vr[0] + 1;
487 // this triangle is not facing the light
488 // output the sides (facing inward to this triangle)
490 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
492 vr[0] = vertexremap[e[0]];
493 vr[1] = vertexremap[e[1]];
494 outelement3i[0] = vr[1];
495 outelement3i[1] = vr[0] + 1;
496 outelement3i[2] = vr[0];
497 outelement3i[3] = vr[1];
498 outelement3i[4] = vr[1] + 1;
499 outelement3i[5] = vr[0] + 1;
504 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
506 vr[1] = vertexremap[e[1]];
507 vr[2] = vertexremap[e[2]];
508 outelement3i[0] = vr[2];
509 outelement3i[1] = vr[1] + 1;
510 outelement3i[2] = vr[1];
511 outelement3i[3] = vr[2];
512 outelement3i[4] = vr[2] + 1;
513 outelement3i[5] = vr[1] + 1;
518 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
520 vr[0] = vertexremap[e[0]];
521 vr[2] = vertexremap[e[2]];
522 outelement3i[0] = vr[0];
523 outelement3i[1] = vr[2] + 1;
524 outelement3i[2] = vr[2];
525 outelement3i[3] = vr[0];
526 outelement3i[4] = vr[0] + 1;
527 outelement3i[5] = vr[2] + 1;
536 // two pass approach (identify lit/dark faces and then generate sides)
537 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
539 // calculate triangle facing flag
540 v[0] = invertex3f + e[0] * 3;
541 v[1] = invertex3f + e[1] * 3;
542 v[2] = invertex3f + e[2] * 3;
543 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
545 trianglefacinglightlist[numfacing++] = i;
546 // make sure the vertices are created
547 for (j = 0;j < 3;j++)
549 if (vertexupdate[e[j]] != vertexupdatenum)
551 vertexupdate[e[j]] = vertexupdatenum;
552 vertexremap[e[j]] = outvertices;
553 VectorSubtract(v[j], relativelightorigin, temp);
554 f = projectdistance / VectorLength(temp);
555 VectorCopy(v[j], outvertex3f);
556 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
561 // output the front and back triangles
562 outelement3i[0] = vertexremap[e[0]];
563 outelement3i[1] = vertexremap[e[1]];
564 outelement3i[2] = vertexremap[e[2]];
565 outelement3i[3] = vertexremap[e[2]] + 1;
566 outelement3i[4] = vertexremap[e[1]] + 1;
567 outelement3i[5] = vertexremap[e[0]] + 1;
572 for (i = 0;i < numfacing;i++)
574 t = trianglefacinglightlist[i];
575 e = inelement3i + t * 3;
576 n = inneighbor3i + t * 3;
577 // output the sides (facing outward from this triangle)
579 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]))))
581 vr[0] = vertexremap[e[0]];
582 vr[1] = vertexremap[e[1]];
583 outelement3i[0] = vr[1];
584 outelement3i[1] = vr[0];
585 outelement3i[2] = vr[0] + 1;
586 outelement3i[3] = vr[1];
587 outelement3i[4] = vr[0] + 1;
588 outelement3i[5] = vr[1] + 1;
593 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]))))
595 vr[1] = vertexremap[e[1]];
596 vr[2] = vertexremap[e[2]];
597 outelement3i[0] = vr[2];
598 outelement3i[1] = vr[1];
599 outelement3i[2] = vr[1] + 1;
600 outelement3i[3] = vr[2];
601 outelement3i[4] = vr[1] + 1;
602 outelement3i[5] = vr[2] + 1;
607 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]))))
609 vr[0] = vertexremap[e[0]];
610 vr[2] = vertexremap[e[2]];
611 outelement3i[0] = vr[0];
612 outelement3i[1] = vr[2];
613 outelement3i[2] = vr[2] + 1;
614 outelement3i[3] = vr[0];
615 outelement3i[4] = vr[2] + 1;
616 outelement3i[5] = vr[0] + 1;
623 *outnumvertices = outvertices;
627 float varray_vertex3f2[65536*3];
629 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
632 if (projectdistance < 0.1)
634 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
640 // make sure shadowelements is big enough for this volume
641 if (maxshadowelements < numtris * 24)
642 R_Shadow_ResizeShadowElements(numtris);
644 // check which triangles are facing the light, and then output
645 // triangle elements and vertices... by clever use of elements we
646 // can construct the whole shadow from the unprojected vertices and
647 // the projected vertices
648 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
650 GL_VertexPointer(varray_vertex3f2);
651 if (r_shadowstage == SHADOWSTAGE_STENCIL)
653 // decrement stencil if frontface is behind depthbuffer
654 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
655 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
656 R_Mesh_Draw(outverts, tris, shadowelements);
658 c_rt_shadowtris += numtris;
659 // increment stencil if backface is behind depthbuffer
660 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
661 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
663 R_Mesh_Draw(outverts, tris, shadowelements);
665 c_rt_shadowtris += numtris;
669 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
672 if (r_shadowstage == SHADOWSTAGE_STENCIL)
674 // decrement stencil if frontface is behind depthbuffer
675 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
676 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
677 for (mesh = firstmesh;mesh;mesh = mesh->next)
679 GL_VertexPointer(mesh->vertex3f);
680 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
681 c_rtcached_shadowmeshes++;
682 c_rtcached_shadowtris += mesh->numtriangles;
684 // increment stencil if backface is behind depthbuffer
685 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
686 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
688 for (mesh = firstmesh;mesh;mesh = mesh->next)
690 GL_VertexPointer(mesh->vertex3f);
691 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
692 c_rtcached_shadowmeshes++;
693 c_rtcached_shadowtris += mesh->numtriangles;
697 float r_shadow_attenpower, r_shadow_attenscale;
698 static void R_Shadow_MakeTextures(void)
700 int x, y, z, d, side;
701 float v[3], s, t, intensity;
703 R_FreeTexturePool(&r_shadow_texturepool);
704 r_shadow_texturepool = R_AllocTexturePool();
705 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
706 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
708 #define ATTEN2DSIZE 64
709 #define ATTEN3DSIZE 32
710 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
715 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
720 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
725 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
726 if (gl_texturecubemap)
728 for (side = 0;side < 6;side++)
730 for (y = 0;y < NORMSIZE;y++)
732 for (x = 0;x < NORMSIZE;x++)
734 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
735 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
769 intensity = 127.0f / sqrt(DotProduct(v, v));
770 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
771 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
772 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
773 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
777 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
780 r_shadow_normalcubetexture = NULL;
781 for (y = 0;y < ATTEN2DSIZE;y++)
783 for (x = 0;x < ATTEN2DSIZE;x++)
785 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
786 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
788 intensity = 1.0f - sqrt(DotProduct(v, v));
790 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
791 d = bound(0, intensity, 255);
792 data[(y*ATTEN2DSIZE+x)*4+0] = d;
793 data[(y*ATTEN2DSIZE+x)*4+1] = d;
794 data[(y*ATTEN2DSIZE+x)*4+2] = d;
795 data[(y*ATTEN2DSIZE+x)*4+3] = d;
798 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
799 if (r_shadow_texture3d.integer)
801 for (z = 0;z < ATTEN3DSIZE;z++)
803 for (y = 0;y < ATTEN3DSIZE;y++)
805 for (x = 0;x < ATTEN3DSIZE;x++)
807 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
808 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
809 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
810 intensity = 1.0f - sqrt(DotProduct(v, v));
812 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
813 d = bound(0, intensity, 255);
814 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
815 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
816 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
817 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
821 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
826 void R_Shadow_Stage_Begin(void)
830 if (r_shadow_texture3d.integer && !gl_texture3d)
831 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
833 if (!r_shadow_attenuation2dtexture
834 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
835 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
836 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
837 R_Shadow_MakeTextures();
839 memset(&m, 0, sizeof(m));
840 GL_BlendFunc(GL_ONE, GL_ZERO);
843 R_Mesh_State_Texture(&m);
844 GL_Color(0, 0, 0, 1);
845 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
846 qglDisable(GL_SCISSOR_TEST);
847 r_shadowstage = SHADOWSTAGE_NONE;
849 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
850 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
851 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
854 void R_Shadow_LoadWorldLightsIfNeeded(void)
856 if (r_shadow_reloadlights && cl.worldmodel)
858 R_Shadow_ClearWorldLights();
859 r_shadow_reloadlights = false;
860 R_Shadow_LoadWorldLights();
861 if (r_shadow_worldlightchain == NULL)
863 R_Shadow_LoadLightsFile();
864 if (r_shadow_worldlightchain == NULL)
865 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
870 void R_Shadow_Stage_ShadowVolumes(void)
873 memset(&m, 0, sizeof(m));
874 R_Mesh_State_Texture(&m);
875 GL_Color(1, 1, 1, 1);
876 qglColorMask(0, 0, 0, 0);
877 GL_BlendFunc(GL_ONE, GL_ZERO);
880 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
881 //if (r_shadow_polygonoffset.value != 0)
883 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
884 // qglEnable(GL_POLYGON_OFFSET_FILL);
887 // qglDisable(GL_POLYGON_OFFSET_FILL);
888 qglDepthFunc(GL_LESS);
889 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
890 qglEnable(GL_STENCIL_TEST);
891 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
892 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
893 r_shadowstage = SHADOWSTAGE_STENCIL;
894 qglClear(GL_STENCIL_BUFFER_BIT);
896 // LordHavoc note: many shadow volumes reside entirely inside the world
897 // (that is to say they are entirely bounded by their lit surfaces),
898 // which can be optimized by handling things as an inverted light volume,
899 // with the shadow boundaries of the world being simulated by an altered
900 // (129) bias to stencil clearing on such lights
901 // FIXME: generate inverted light volumes for use as shadow volumes and
902 // optimize for them as noted above
905 void R_Shadow_Stage_LightWithoutShadows(void)
908 memset(&m, 0, sizeof(m));
909 R_Mesh_State_Texture(&m);
910 GL_BlendFunc(GL_ONE, GL_ONE);
913 qglPolygonOffset(0, 0);
914 //qglDisable(GL_POLYGON_OFFSET_FILL);
915 GL_Color(1, 1, 1, 1);
916 qglColorMask(1, 1, 1, 1);
917 qglDepthFunc(GL_EQUAL);
918 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
919 qglDisable(GL_STENCIL_TEST);
920 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
921 qglStencilFunc(GL_EQUAL, 128, 0xFF);
922 r_shadowstage = SHADOWSTAGE_LIGHT;
926 void R_Shadow_Stage_LightWithShadows(void)
929 memset(&m, 0, sizeof(m));
930 R_Mesh_State_Texture(&m);
931 GL_BlendFunc(GL_ONE, GL_ONE);
934 qglPolygonOffset(0, 0);
935 //qglDisable(GL_POLYGON_OFFSET_FILL);
936 GL_Color(1, 1, 1, 1);
937 qglColorMask(1, 1, 1, 1);
938 qglDepthFunc(GL_EQUAL);
939 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
940 qglEnable(GL_STENCIL_TEST);
941 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
942 // only draw light where this geometry was already rendered AND the
943 // stencil is 128 (values other than this mean shadow)
944 qglStencilFunc(GL_EQUAL, 128, 0xFF);
945 r_shadowstage = SHADOWSTAGE_LIGHT;
949 void R_Shadow_Stage_End(void)
952 memset(&m, 0, sizeof(m));
953 R_Mesh_State_Texture(&m);
954 GL_BlendFunc(GL_ONE, GL_ZERO);
957 qglPolygonOffset(0, 0);
958 //qglDisable(GL_POLYGON_OFFSET_FILL);
959 GL_Color(1, 1, 1, 1);
960 qglColorMask(1, 1, 1, 1);
961 qglDisable(GL_SCISSOR_TEST);
962 qglDepthFunc(GL_LEQUAL);
963 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
964 qglDisable(GL_STENCIL_TEST);
965 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
966 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
967 r_shadowstage = SHADOWSTAGE_NONE;
970 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
972 int i, ix1, iy1, ix2, iy2;
973 float x1, y1, x2, y2, x, y, f;
976 if (!r_shadow_scissor.integer)
978 // if view is inside the box, just say yes it's visible
979 // LordHavoc: for some odd reason scissor seems broken without stencil
980 // (?!? seems like a driver bug) so abort if gl_stencil is false
981 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
983 qglDisable(GL_SCISSOR_TEST);
986 for (i = 0;i < 3;i++)
988 if (r_viewforward[i] >= 0)
999 f = DotProduct(r_viewforward, r_vieworigin) + 1;
1000 if (DotProduct(r_viewforward, v2) <= f)
1002 // entirely behind nearclip plane
1005 if (DotProduct(r_viewforward, v) >= f)
1007 // entirely infront of nearclip plane
1008 x1 = y1 = x2 = y2 = 0;
1009 for (i = 0;i < 8;i++)
1011 v[0] = (i & 1) ? mins[0] : maxs[0];
1012 v[1] = (i & 2) ? mins[1] : maxs[1];
1013 v[2] = (i & 4) ? mins[2] : maxs[2];
1015 GL_TransformToScreen(v, v2);
1016 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1035 // clipped by nearclip plane
1036 // this is nasty and crude...
1037 // create viewspace bbox
1038 for (i = 0;i < 8;i++)
1040 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1041 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1042 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1043 v2[0] = -DotProduct(v, r_viewleft);
1044 v2[1] = DotProduct(v, r_viewup);
1045 v2[2] = DotProduct(v, r_viewforward);
1048 if (smins[0] > v2[0]) smins[0] = v2[0];
1049 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1050 if (smins[1] > v2[1]) smins[1] = v2[1];
1051 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1052 if (smins[2] > v2[2]) smins[2] = v2[2];
1053 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1057 smins[0] = smaxs[0] = v2[0];
1058 smins[1] = smaxs[1] = v2[1];
1059 smins[2] = smaxs[2] = v2[2];
1062 // now we have a bbox in viewspace
1063 // clip it to the view plane
1066 // return true if that culled the box
1067 if (smins[2] >= smaxs[2])
1069 // ok some of it is infront of the view, transform each corner back to
1070 // worldspace and then to screenspace and make screen rect
1071 // initialize these variables just to avoid compiler warnings
1072 x1 = y1 = x2 = y2 = 0;
1073 for (i = 0;i < 8;i++)
1075 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1076 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1077 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1078 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1079 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1080 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1082 GL_TransformToScreen(v, v2);
1083 //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]);
1100 // this code doesn't handle boxes with any points behind view properly
1101 x1 = 1000;x2 = -1000;
1102 y1 = 1000;y2 = -1000;
1103 for (i = 0;i < 8;i++)
1105 v[0] = (i & 1) ? mins[0] : maxs[0];
1106 v[1] = (i & 2) ? mins[1] : maxs[1];
1107 v[2] = (i & 4) ? mins[2] : maxs[2];
1109 GL_TransformToScreen(v, v2);
1110 //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]);
1128 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1129 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1130 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1131 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1132 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1133 if (ix2 <= ix1 || iy2 <= iy1)
1135 // set up the scissor rectangle
1136 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1137 qglEnable(GL_SCISSOR_TEST);
1142 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1144 float *color4f = varray_color4f;
1145 float dist, dot, intensity, v[3], n[3];
1146 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1148 Matrix4x4_Transform(m, vertex3f, v);
1149 if ((dist = DotProduct(v, v)) < 1)
1151 Matrix4x4_Transform3x3(m, normal3f, n);
1152 if ((dot = DotProduct(n, v)) > 0)
1155 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1156 VectorScale(lightcolor, intensity, color4f);
1161 VectorClear(color4f);
1167 VectorClear(color4f);
1173 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1175 float *color4f = varray_color4f;
1176 float dist, dot, intensity, v[3], n[3];
1177 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1179 Matrix4x4_Transform(m, vertex3f, v);
1180 if ((dist = fabs(v[2])) < 1)
1182 Matrix4x4_Transform3x3(m, normal3f, n);
1183 if ((dot = DotProduct(n, v)) > 0)
1185 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1186 VectorScale(lightcolor, intensity, color4f);
1191 VectorClear(color4f);
1197 VectorClear(color4f);
1203 // FIXME: this should be done in a vertex program when possible
1204 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1205 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1209 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1210 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1211 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1218 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1222 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1223 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1230 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)
1234 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1236 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1237 // the cubemap normalizes this for us
1238 out3f[0] = DotProduct(svector3f, lightdir);
1239 out3f[1] = DotProduct(tvector3f, lightdir);
1240 out3f[2] = DotProduct(normal3f, lightdir);
1244 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)
1247 float lightdir[3], eyedir[3], halfdir[3];
1248 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1250 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1251 VectorNormalizeFast(lightdir);
1252 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1253 VectorNormalizeFast(eyedir);
1254 VectorAdd(lightdir, eyedir, halfdir);
1255 // the cubemap normalizes this for us
1256 out3f[0] = DotProduct(svector3f, halfdir);
1257 out3f[1] = DotProduct(tvector3f, halfdir);
1258 out3f[2] = DotProduct(normal3f, halfdir);
1262 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)
1265 float color[3], color2[3];
1267 GL_VertexPointer(vertex3f);
1268 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1271 bumptexture = r_shadow_blankbumptexture;
1273 // colorscale accounts for how much we multiply the brightness during combine
1274 // mult is how many times the final pass of the lighting will be
1275 // performed to get more brightness than otherwise possible
1276 // limit mult to 64 for sanity sake
1277 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1279 // 3/2 3D combine path (Geforce3, Radeon 8500)
1280 memset(&m, 0, sizeof(m));
1281 m.tex[0] = R_GetTexture(bumptexture);
1282 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1283 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1284 m.texcombinergb[0] = GL_REPLACE;
1285 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1286 m.pointer_texcoord[0] = texcoord2f;
1287 m.pointer_texcoord[1] = varray_texcoord3f[1];
1288 m.pointer_texcoord[2] = varray_texcoord3f[2];
1289 R_Mesh_State_Texture(&m);
1290 qglColorMask(0,0,0,1);
1291 GL_BlendFunc(GL_ONE, GL_ZERO);
1292 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1293 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1294 R_Mesh_Draw(numverts, numtriangles, elements);
1296 c_rt_lighttris += numtriangles;
1298 memset(&m, 0, sizeof(m));
1299 m.tex[0] = R_GetTexture(basetexture);
1300 m.pointer_texcoord[0] = texcoord2f;
1303 m.texcubemap[1] = R_GetTexture(lightcubemap);
1304 m.pointer_texcoord[1] = varray_texcoord3f[1];
1305 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1307 R_Mesh_State_Texture(&m);
1308 qglColorMask(1,1,1,0);
1309 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1310 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1311 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1313 color[0] = bound(0, color2[0], 1);
1314 color[1] = bound(0, color2[1], 1);
1315 color[2] = bound(0, color2[2], 1);
1316 GL_Color(color[0], color[1], color[2], 1);
1317 R_Mesh_Draw(numverts, numtriangles, elements);
1319 c_rt_lighttris += numtriangles;
1322 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1324 // 1/2/2 3D combine path (original Radeon)
1325 memset(&m, 0, sizeof(m));
1326 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1327 m.pointer_texcoord[0] = varray_texcoord3f[0];
1328 R_Mesh_State_Texture(&m);
1329 qglColorMask(0,0,0,1);
1330 GL_BlendFunc(GL_ONE, GL_ZERO);
1331 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1332 R_Mesh_Draw(numverts, numtriangles, elements);
1334 c_rt_lighttris += numtriangles;
1336 memset(&m, 0, sizeof(m));
1337 m.tex[0] = R_GetTexture(bumptexture);
1338 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1339 m.texcombinergb[0] = GL_REPLACE;
1340 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1341 m.pointer_texcoord[0] = texcoord2f;
1342 m.pointer_texcoord[1] = varray_texcoord3f[1];
1343 R_Mesh_State_Texture(&m);
1344 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1345 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1346 R_Mesh_Draw(numverts, numtriangles, elements);
1348 c_rt_lighttris += numtriangles;
1350 memset(&m, 0, sizeof(m));
1351 m.tex[0] = R_GetTexture(basetexture);
1352 m.pointer_texcoord[0] = texcoord2f;
1355 m.texcubemap[1] = R_GetTexture(lightcubemap);
1356 m.pointer_texcoord[1] = varray_texcoord3f[1];
1357 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1359 R_Mesh_State_Texture(&m);
1360 qglColorMask(1,1,1,0);
1361 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1362 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1363 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1365 color[0] = bound(0, color2[0], 1);
1366 color[1] = bound(0, color2[1], 1);
1367 color[2] = bound(0, color2[2], 1);
1368 GL_Color(color[0], color[1], color[2], 1);
1369 R_Mesh_Draw(numverts, numtriangles, elements);
1371 c_rt_lighttris += numtriangles;
1374 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1376 // 2/2 3D combine path (original Radeon)
1377 memset(&m, 0, sizeof(m));
1378 m.tex[0] = R_GetTexture(bumptexture);
1379 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1380 m.texcombinergb[0] = GL_REPLACE;
1381 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1382 m.pointer_texcoord[0] = texcoord2f;
1383 m.pointer_texcoord[1] = varray_texcoord3f[1];
1384 R_Mesh_State_Texture(&m);
1385 qglColorMask(0,0,0,1);
1386 GL_BlendFunc(GL_ONE, GL_ZERO);
1387 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1388 R_Mesh_Draw(numverts, numtriangles, elements);
1390 c_rt_lighttris += numtriangles;
1392 memset(&m, 0, sizeof(m));
1393 m.tex[0] = R_GetTexture(basetexture);
1394 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1395 m.pointer_texcoord[0] = texcoord2f;
1396 m.pointer_texcoord[1] = varray_texcoord3f[1];
1397 R_Mesh_State_Texture(&m);
1398 qglColorMask(1,1,1,0);
1399 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1400 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1401 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1402 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1404 color[0] = bound(0, color2[0], 1);
1405 color[1] = bound(0, color2[1], 1);
1406 color[2] = bound(0, color2[2], 1);
1407 GL_Color(color[0], color[1], color[2], 1);
1408 R_Mesh_Draw(numverts, numtriangles, elements);
1410 c_rt_lighttris += numtriangles;
1413 else if (r_textureunits.integer >= 4)
1415 // 4/2 2D combine path (Geforce3, Radeon 8500)
1416 memset(&m, 0, sizeof(m));
1417 m.tex[0] = R_GetTexture(bumptexture);
1418 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1419 m.texcombinergb[0] = GL_REPLACE;
1420 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1421 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1422 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1423 m.pointer_texcoord[0] = texcoord2f;
1424 m.pointer_texcoord[1] = varray_texcoord3f[1];
1425 m.pointer_texcoord[2] = varray_texcoord2f[2];
1426 m.pointer_texcoord[3] = varray_texcoord2f[3];
1427 R_Mesh_State_Texture(&m);
1428 qglColorMask(0,0,0,1);
1429 GL_BlendFunc(GL_ONE, GL_ZERO);
1430 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1431 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1432 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1433 R_Mesh_Draw(numverts, numtriangles, elements);
1435 c_rt_lighttris += numtriangles;
1437 memset(&m, 0, sizeof(m));
1438 m.tex[0] = R_GetTexture(basetexture);
1439 m.pointer_texcoord[0] = texcoord2f;
1442 m.texcubemap[1] = R_GetTexture(lightcubemap);
1443 m.pointer_texcoord[1] = varray_texcoord3f[1];
1444 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1446 R_Mesh_State_Texture(&m);
1447 qglColorMask(1,1,1,0);
1448 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1449 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1450 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1452 color[0] = bound(0, color2[0], 1);
1453 color[1] = bound(0, color2[1], 1);
1454 color[2] = bound(0, color2[2], 1);
1455 GL_Color(color[0], color[1], color[2], 1);
1456 R_Mesh_Draw(numverts, numtriangles, elements);
1458 c_rt_lighttris += numtriangles;
1463 // 2/2/2 2D combine path (any dot3 card)
1464 memset(&m, 0, sizeof(m));
1465 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1466 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1467 m.pointer_texcoord[0] = varray_texcoord2f[0];
1468 m.pointer_texcoord[1] = varray_texcoord2f[1];
1469 R_Mesh_State_Texture(&m);
1470 qglColorMask(0,0,0,1);
1471 GL_BlendFunc(GL_ONE, GL_ZERO);
1472 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1473 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1474 R_Mesh_Draw(numverts, numtriangles, elements);
1476 c_rt_lighttris += numtriangles;
1478 memset(&m, 0, sizeof(m));
1479 m.tex[0] = R_GetTexture(bumptexture);
1480 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1481 m.texcombinergb[0] = GL_REPLACE;
1482 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1483 m.pointer_texcoord[0] = texcoord2f;
1484 m.pointer_texcoord[1] = varray_texcoord3f[1];
1485 R_Mesh_State_Texture(&m);
1486 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1487 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1488 R_Mesh_Draw(numverts, numtriangles, elements);
1490 c_rt_lighttris += numtriangles;
1492 memset(&m, 0, sizeof(m));
1493 m.tex[0] = R_GetTexture(basetexture);
1494 m.pointer_texcoord[0] = texcoord2f;
1497 m.texcubemap[1] = R_GetTexture(lightcubemap);
1498 m.pointer_texcoord[1] = varray_texcoord3f[1];
1499 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1501 R_Mesh_State_Texture(&m);
1502 qglColorMask(1,1,1,0);
1503 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1504 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1505 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1507 color[0] = bound(0, color2[0], 1);
1508 color[1] = bound(0, color2[1], 1);
1509 color[2] = bound(0, color2[2], 1);
1510 GL_Color(color[0], color[1], color[2], 1);
1511 R_Mesh_Draw(numverts, numtriangles, elements);
1513 c_rt_lighttris += numtriangles;
1519 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1520 GL_DepthMask(false);
1522 GL_ColorPointer(varray_color4f);
1523 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1524 memset(&m, 0, sizeof(m));
1525 m.tex[0] = R_GetTexture(basetexture);
1526 m.pointer_texcoord[0] = texcoord2f;
1527 if (r_textureunits.integer >= 2)
1530 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1531 m.pointer_texcoord[1] = varray_texcoord2f[1];
1532 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1534 R_Mesh_State_Texture(&m);
1535 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1537 color[0] = bound(0, color2[0], 1);
1538 color[1] = bound(0, color2[1], 1);
1539 color[2] = bound(0, color2[2], 1);
1540 if (r_textureunits.integer >= 2)
1541 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1543 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1544 R_Mesh_Draw(numverts, numtriangles, elements);
1546 c_rt_lighttris += numtriangles;
1551 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)
1554 float color[3], color2[3], colorscale;
1556 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1559 glosstexture = r_shadow_blankglosstexture;
1560 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1562 colorscale = r_shadow_glossintensity.value;
1564 bumptexture = r_shadow_blankbumptexture;
1565 if (glosstexture == r_shadow_blankglosstexture)
1566 colorscale *= r_shadow_gloss2intensity.value;
1567 GL_VertexPointer(vertex3f);
1569 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1571 // 2/0/0/1/2 3D combine blendsquare path
1572 memset(&m, 0, sizeof(m));
1573 m.tex[0] = R_GetTexture(bumptexture);
1574 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1575 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1576 m.pointer_texcoord[0] = texcoord2f;
1577 m.pointer_texcoord[1] = varray_texcoord3f[1];
1578 R_Mesh_State_Texture(&m);
1579 qglColorMask(0,0,0,1);
1580 // this squares the result
1581 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1582 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1583 R_Mesh_Draw(numverts, numtriangles, elements);
1585 c_rt_lighttris += numtriangles;
1587 memset(&m, 0, sizeof(m));
1588 R_Mesh_State_Texture(&m);
1589 // square alpha in framebuffer a few times to make it shiny
1590 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1591 // these comments are a test run through this math for intensity 0.5
1592 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1593 // 0.25 * 0.25 = 0.0625 (this is another pass)
1594 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1595 R_Mesh_Draw(numverts, numtriangles, elements);
1597 c_rt_lighttris += numtriangles;
1598 R_Mesh_Draw(numverts, numtriangles, elements);
1600 c_rt_lighttris += numtriangles;
1602 memset(&m, 0, sizeof(m));
1603 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1604 m.pointer_texcoord[0] = varray_texcoord3f[0];
1605 R_Mesh_State_Texture(&m);
1606 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1607 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1608 R_Mesh_Draw(numverts, numtriangles, elements);
1610 c_rt_lighttris += numtriangles;
1612 memset(&m, 0, sizeof(m));
1613 m.tex[0] = R_GetTexture(glosstexture);
1616 m.texcubemap[1] = R_GetTexture(lightcubemap);
1617 m.pointer_texcoord[1] = varray_texcoord3f[1];
1618 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1620 m.pointer_texcoord[0] = texcoord2f;
1621 R_Mesh_State_Texture(&m);
1622 qglColorMask(1,1,1,0);
1623 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1624 VectorScale(lightcolor, colorscale, color2);
1625 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1627 color[0] = bound(0, color2[0], 1);
1628 color[1] = bound(0, color2[1], 1);
1629 color[2] = bound(0, color2[2], 1);
1630 GL_Color(color[0], color[1], color[2], 1);
1631 R_Mesh_Draw(numverts, numtriangles, elements);
1633 c_rt_lighttris += numtriangles;
1636 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1638 // 2/0/0/2 3D combine blendsquare path
1639 memset(&m, 0, sizeof(m));
1640 m.tex[0] = R_GetTexture(bumptexture);
1641 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1642 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1643 m.pointer_texcoord[0] = texcoord2f;
1644 m.pointer_texcoord[1] = varray_texcoord3f[1];
1645 R_Mesh_State_Texture(&m);
1646 qglColorMask(0,0,0,1);
1647 // this squares the result
1648 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1649 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1650 R_Mesh_Draw(numverts, numtriangles, elements);
1652 c_rt_lighttris += numtriangles;
1654 memset(&m, 0, sizeof(m));
1655 R_Mesh_State_Texture(&m);
1656 // square alpha in framebuffer a few times to make it shiny
1657 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1658 // these comments are a test run through this math for intensity 0.5
1659 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1660 // 0.25 * 0.25 = 0.0625 (this is another pass)
1661 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1662 R_Mesh_Draw(numverts, numtriangles, elements);
1664 c_rt_lighttris += numtriangles;
1665 R_Mesh_Draw(numverts, numtriangles, elements);
1667 c_rt_lighttris += numtriangles;
1669 memset(&m, 0, sizeof(m));
1670 m.tex[0] = R_GetTexture(glosstexture);
1671 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1672 m.pointer_texcoord[0] = texcoord2f;
1673 m.pointer_texcoord[1] = varray_texcoord3f[1];
1674 R_Mesh_State_Texture(&m);
1675 qglColorMask(1,1,1,0);
1676 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1677 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1678 VectorScale(lightcolor, colorscale, color2);
1679 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1681 color[0] = bound(0, color2[0], 1);
1682 color[1] = bound(0, color2[1], 1);
1683 color[2] = bound(0, color2[2], 1);
1684 GL_Color(color[0], color[1], color[2], 1);
1685 R_Mesh_Draw(numverts, numtriangles, elements);
1687 c_rt_lighttris += numtriangles;
1690 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1692 // 2/0/0/2/2 2D combine blendsquare path
1693 memset(&m, 0, sizeof(m));
1694 m.tex[0] = R_GetTexture(bumptexture);
1695 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1696 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1697 m.pointer_texcoord[0] = texcoord2f;
1698 m.pointer_texcoord[1] = varray_texcoord3f[1];
1699 R_Mesh_State_Texture(&m);
1700 qglColorMask(0,0,0,1);
1701 // this squares the result
1702 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1703 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1704 R_Mesh_Draw(numverts, numtriangles, elements);
1706 c_rt_lighttris += numtriangles;
1708 memset(&m, 0, sizeof(m));
1709 R_Mesh_State_Texture(&m);
1710 // square alpha in framebuffer a few times to make it shiny
1711 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1712 // these comments are a test run through this math for intensity 0.5
1713 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1714 // 0.25 * 0.25 = 0.0625 (this is another pass)
1715 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1716 R_Mesh_Draw(numverts, numtriangles, elements);
1718 c_rt_lighttris += numtriangles;
1719 R_Mesh_Draw(numverts, numtriangles, elements);
1721 c_rt_lighttris += numtriangles;
1723 memset(&m, 0, sizeof(m));
1724 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1725 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1726 m.pointer_texcoord[0] = varray_texcoord2f[0];
1727 m.pointer_texcoord[1] = varray_texcoord2f[1];
1728 R_Mesh_State_Texture(&m);
1729 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1730 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1731 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1732 R_Mesh_Draw(numverts, numtriangles, elements);
1734 c_rt_lighttris += numtriangles;
1736 memset(&m, 0, sizeof(m));
1737 m.tex[0] = R_GetTexture(glosstexture);
1740 m.texcubemap[1] = R_GetTexture(lightcubemap);
1741 m.pointer_texcoord[1] = varray_texcoord3f[1];
1742 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1744 m.pointer_texcoord[0] = texcoord2f;
1745 R_Mesh_State_Texture(&m);
1746 qglColorMask(1,1,1,0);
1747 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1748 VectorScale(lightcolor, colorscale, color2);
1749 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1751 color[0] = bound(0, color2[0], 1);
1752 color[1] = bound(0, color2[1], 1);
1753 color[2] = bound(0, color2[2], 1);
1754 GL_Color(color[0], color[1], color[2], 1);
1755 R_Mesh_Draw(numverts, numtriangles, elements);
1757 c_rt_lighttris += numtriangles;
1763 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1765 R_Mesh_Matrix(matrix);
1766 if (r_shadow_showtris.integer)
1770 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1771 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1772 qglDisable(GL_DEPTH_TEST);
1773 qglDisable(GL_STENCIL_TEST);
1774 //qglDisable(GL_CULL_FACE);
1775 qglColorMask(1,1,1,1);
1776 memset(&m, 0, sizeof(m));
1777 R_Mesh_State_Texture(&m);
1778 GL_Color(0,0.1,0,1);
1779 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1780 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1782 GL_VertexPointer(mesh->vertex3f);
1783 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1785 //qglEnable(GL_CULL_FACE);
1787 qglEnable(GL_DEPTH_TEST);
1790 qglEnable(GL_STENCIL_TEST);
1791 qglColorMask(0,0,0,0);
1794 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1797 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)
1800 R_Mesh_Matrix(matrix);
1801 if (r_shadow_showtris.integer)
1804 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1805 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1806 qglDisable(GL_DEPTH_TEST);
1807 qglDisable(GL_STENCIL_TEST);
1808 //qglDisable(GL_CULL_FACE);
1809 memset(&m, 0, sizeof(m));
1810 R_Mesh_State_Texture(&m);
1811 GL_Color(0.2,0,0,1);
1812 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1813 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1815 GL_VertexPointer(mesh->vertex3f);
1816 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1818 //qglEnable(GL_CULL_FACE);
1820 qglEnable(GL_DEPTH_TEST);
1822 qglEnable(GL_STENCIL_TEST);
1824 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1826 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, light->cubemap);
1827 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, light->cubemap);
1831 cvar_t r_editlights = {0, "r_editlights", "0"};
1832 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1833 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1834 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1835 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1836 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1837 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1838 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1839 worldlight_t *r_shadow_worldlightchain;
1840 worldlight_t *r_shadow_selectedlight;
1841 vec3_t r_editlights_cursorlocation;
1843 static int lightpvsbytes;
1844 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1846 typedef struct cubemapinfo_s
1849 rtexture_t *texture;
1853 #define MAX_CUBEMAPS 128
1854 static int numcubemaps;
1855 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1857 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1858 typedef struct suffixinfo_s
1861 int flipx, flipy, flipdiagonal;
1864 static suffixinfo_t suffix[3][6] =
1867 {"posx", false, false, false},
1868 {"negx", false, false, false},
1869 {"posy", false, false, false},
1870 {"negy", false, false, false},
1871 {"posz", false, false, false},
1872 {"negz", false, false, false}
1875 {"px", false, false, false},
1876 {"nx", false, false, false},
1877 {"py", false, false, false},
1878 {"ny", false, false, false},
1879 {"pz", false, false, false},
1880 {"nz", false, false, false}
1883 {"ft", true, false, true},
1884 {"bk", false, true, true},
1885 {"lf", true, true, false},
1886 {"rt", false, false, false},
1887 {"up", false, false, false},
1888 {"dn", false, false, false}
1892 static int componentorder[4] = {0, 1, 2, 3};
1894 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1896 int i, j, cubemapsize;
1897 qbyte *cubemappixels, *image_rgba;
1898 rtexture_t *cubemaptexture;
1900 // must start 0 so the first loadimagepixels has no requested width/height
1902 cubemappixels = NULL;
1903 cubemaptexture = NULL;
1904 for (j = 0;j < 3 && !cubemappixels;j++)
1906 for (i = 0;i < 6;i++)
1908 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1909 if ((image_rgba = loadimagepixels(name, true, cubemapsize, cubemapsize)))
1911 if (image_width == image_height)
1913 if (!cubemappixels && image_width >= 1)
1915 cubemapsize = image_width;
1916 // note this clears to black, so unavailable sizes are black
1917 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1920 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
1923 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1924 Mem_Free(image_rgba);
1930 if (!r_shadow_filters_texturepool)
1931 r_shadow_filters_texturepool = R_AllocTexturePool();
1932 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1933 Mem_Free(cubemappixels);
1936 Con_Printf("Failed to load Cubemap \"%s\"\n", basename);
1937 return cubemaptexture;
1940 rtexture_t *R_Shadow_Cubemap(const char *basename)
1943 for (i = 0;i < numcubemaps;i++)
1944 if (!strcasecmp(cubemaps[i].basename, basename))
1945 return cubemaps[i].texture;
1946 if (i >= MAX_CUBEMAPS)
1949 strcpy(cubemaps[i].basename, basename);
1950 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1951 return cubemaps[i].texture;
1954 void R_Shadow_FreeCubemaps(void)
1957 R_FreeTexturePool(&r_shadow_filters_texturepool);
1960 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1962 int i, j, k, l, maxverts = 256, tris;
1963 float *vertex3f = NULL, mins[3], maxs[3];
1965 shadowmesh_t *mesh, *castmesh = NULL;
1967 if (radius < 15 || DotProduct(color, color) < 0.03)
1969 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1973 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1974 VectorCopy(origin, e->origin);
1975 VectorCopy(color, e->light);
1976 e->lightradius = radius;
1978 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1980 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1983 e->castshadows = castshadow;
1985 e->cullradius = e->lightradius;
1986 for (k = 0;k < 3;k++)
1988 mins[k] = e->origin[k] - e->lightradius;
1989 maxs[k] = e->origin[k] + e->lightradius;
1992 e->next = r_shadow_worldlightchain;
1993 r_shadow_worldlightchain = e;
1994 if (cubemapname && cubemapname[0])
1996 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1997 strcpy(e->cubemapname, cubemapname);
1998 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2000 // FIXME: rewrite this to store ALL geometry into a cache in the light
2002 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2003 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2006 if (cl.worldmodel->brushq3.num_leafs)
2010 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2011 VectorCopy(e->origin, e->mins);
2012 VectorCopy(e->origin, e->maxs);
2013 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2014 face->lighttemp_castshadow = false;
2015 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2017 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2019 for (k = 0;k < 3;k++)
2021 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2022 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2024 for (j = 0;j < leaf->numleaffaces;j++)
2026 face = leaf->firstleafface[j];
2027 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2028 face->lighttemp_castshadow = true;
2033 // add surfaces to shadow casting mesh and light mesh
2034 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2036 if (face->lighttemp_castshadow)
2038 face->lighttemp_castshadow = false;
2039 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2042 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2043 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2044 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2045 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);
2050 else if (cl.worldmodel->brushq1.numleafs)
2054 VectorCopy(e->origin, e->mins);
2055 VectorCopy(e->origin, e->maxs);
2056 i = CL_PointQ1Contents(e->origin);
2058 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2059 surf->lighttemp_castshadow = false;
2061 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2064 qbyte *bytesurfacepvs;
2066 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
2067 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2069 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2071 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
2073 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2075 for (k = 0;k < 3;k++)
2077 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2078 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2083 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2084 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2085 surf->lighttemp_castshadow = true;
2087 Mem_Free(byteleafpvs);
2088 Mem_Free(bytesurfacepvs);
2092 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2093 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
2095 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2097 for (k = 0;k < 3;k++)
2099 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2100 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2102 for (j = 0;j < leaf->nummarksurfaces;j++)
2104 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2105 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2106 surf->lighttemp_castshadow = true;
2112 // add surfaces to shadow casting mesh and light mesh
2113 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2115 if (surf->lighttemp_castshadow)
2117 surf->lighttemp_castshadow = false;
2118 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
2119 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);
2120 if (!(surf->flags & SURF_DRAWSKY))
2121 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);
2127 // limit box to light bounds (in case it grew larger)
2128 for (k = 0;k < 3;k++)
2130 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
2131 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
2133 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2135 // cast shadow volume from castmesh
2136 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2140 for (mesh = castmesh;mesh;mesh = mesh->next)
2142 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2143 maxverts = max(maxverts, mesh->numverts * 2);
2148 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2149 // now that we have the buffers big enough, construct and add
2150 // the shadow volume mesh
2152 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2153 for (mesh = castmesh;mesh;mesh = mesh->next)
2155 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2156 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)))
2157 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2162 // we're done with castmesh now
2163 Mod_ShadowMesh_Free(castmesh);
2166 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2167 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2170 if (e->meshchain_shadow)
2171 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2172 k += mesh->numtriangles;
2174 if (e->meshchain_light)
2175 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2176 l += mesh->numtriangles;
2177 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);
2180 void R_Shadow_FreeWorldLight(worldlight_t *light)
2182 worldlight_t **lightpointer;
2183 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2184 if (*lightpointer != light)
2185 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2186 *lightpointer = light->next;
2187 if (light->cubemapname)
2188 Mem_Free(light->cubemapname);
2189 if (light->meshchain_shadow)
2190 Mod_ShadowMesh_Free(light->meshchain_shadow);
2191 if (light->meshchain_light)
2192 Mod_ShadowMesh_Free(light->meshchain_light);
2196 void R_Shadow_ClearWorldLights(void)
2198 while (r_shadow_worldlightchain)
2199 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2200 r_shadow_selectedlight = NULL;
2201 R_Shadow_FreeCubemaps();
2204 void R_Shadow_SelectLight(worldlight_t *light)
2206 if (r_shadow_selectedlight)
2207 r_shadow_selectedlight->selected = false;
2208 r_shadow_selectedlight = light;
2209 if (r_shadow_selectedlight)
2210 r_shadow_selectedlight->selected = true;
2213 rtexture_t *lighttextures[5];
2215 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2217 float scale = r_editlights_cursorgrid.value * 0.5f;
2218 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);
2221 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2224 const worldlight_t *light;
2227 if (light->selected)
2228 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2229 if (!light->meshchain_shadow)
2231 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);
2234 void R_Shadow_DrawLightSprites(void)
2238 worldlight_t *light;
2240 for (i = 0;i < 5;i++)
2242 lighttextures[i] = NULL;
2243 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2244 lighttextures[i] = pic->tex;
2247 for (light = r_shadow_worldlightchain;light;light = light->next)
2248 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2249 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2252 void R_Shadow_SelectLightInView(void)
2254 float bestrating, rating, temp[3];
2255 worldlight_t *best, *light;
2258 for (light = r_shadow_worldlightchain;light;light = light->next)
2260 VectorSubtract(light->origin, r_vieworigin, temp);
2261 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2264 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2265 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2267 bestrating = rating;
2272 R_Shadow_SelectLight(best);
2275 void R_Shadow_LoadWorldLights(void)
2277 int n, a, style, shadow;
2278 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2279 float origin[3], radius, color[3];
2280 if (cl.worldmodel == NULL)
2282 Con_Printf("No map loaded.\n");
2285 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2286 strlcat (name, ".rtlights", sizeof (name));
2287 lightsstring = FS_LoadFile(name, false);
2295 while (*s && *s != '\n')
2301 // check for modifier flags
2307 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);
2313 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);
2316 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2317 radius *= r_editlights_rtlightssizescale.value;
2318 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2323 Con_Printf("invalid rtlights file \"%s\"\n", name);
2324 Mem_Free(lightsstring);
2328 void R_Shadow_SaveWorldLights(void)
2330 worldlight_t *light;
2331 int bufchars, bufmaxchars;
2333 char name[MAX_QPATH];
2335 if (!r_shadow_worldlightchain)
2337 if (cl.worldmodel == NULL)
2339 Con_Printf("No map loaded.\n");
2342 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2343 strlcat (name, ".rtlights", sizeof (name));
2344 bufchars = bufmaxchars = 0;
2346 for (light = r_shadow_worldlightchain;light;light = light->next)
2348 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 : "");
2349 if (bufchars + (int) strlen(line) > bufmaxchars)
2351 bufmaxchars = bufchars + strlen(line) + 2048;
2353 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2357 memcpy(buf, oldbuf, bufchars);
2363 memcpy(buf + bufchars, line, strlen(line));
2364 bufchars += strlen(line);
2368 FS_WriteFile(name, buf, bufchars);
2373 void R_Shadow_LoadLightsFile(void)
2376 char name[MAX_QPATH], *lightsstring, *s, *t;
2377 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2378 if (cl.worldmodel == NULL)
2380 Con_Printf("No map loaded.\n");
2383 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2384 strlcat (name, ".lights", sizeof (name));
2385 lightsstring = FS_LoadFile(name, false);
2393 while (*s && *s != '\n')
2398 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);
2402 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);
2405 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2406 radius = bound(15, radius, 4096);
2407 VectorScale(color, (2.0f / (8388608.0f)), color);
2408 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2413 Con_Printf("invalid lights file \"%s\"\n", name);
2414 Mem_Free(lightsstring);
2418 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2420 int entnum, style, islight;
2421 char key[256], value[1024];
2422 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2425 if (cl.worldmodel == NULL)
2427 Con_Printf("No map loaded.\n");
2430 data = cl.worldmodel->brush.entities;
2433 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2436 origin[0] = origin[1] = origin[2] = 0;
2437 originhack[0] = originhack[1] = originhack[2] = 0;
2438 color[0] = color[1] = color[2] = 1;
2439 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2446 if (!COM_ParseToken(&data, false))
2448 if (com_token[0] == '}')
2449 break; // end of entity
2450 if (com_token[0] == '_')
2451 strcpy(key, com_token + 1);
2453 strcpy(key, com_token);
2454 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2455 key[strlen(key)-1] = 0;
2456 if (!COM_ParseToken(&data, false))
2458 strcpy(value, com_token);
2460 // now that we have the key pair worked out...
2461 if (!strcmp("light", key))
2462 light = atof(value);
2463 else if (!strcmp("origin", key))
2464 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2465 else if (!strcmp("color", key))
2466 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2467 else if (!strcmp("wait", key))
2468 fadescale = atof(value);
2469 else if (!strcmp("classname", key))
2471 if (!strncmp(value, "light", 5))
2474 if (!strcmp(value, "light_fluoro"))
2479 overridecolor[0] = 1;
2480 overridecolor[1] = 1;
2481 overridecolor[2] = 1;
2483 if (!strcmp(value, "light_fluorospark"))
2488 overridecolor[0] = 1;
2489 overridecolor[1] = 1;
2490 overridecolor[2] = 1;
2492 if (!strcmp(value, "light_globe"))
2497 overridecolor[0] = 1;
2498 overridecolor[1] = 0.8;
2499 overridecolor[2] = 0.4;
2501 if (!strcmp(value, "light_flame_large_yellow"))
2506 overridecolor[0] = 1;
2507 overridecolor[1] = 0.5;
2508 overridecolor[2] = 0.1;
2510 if (!strcmp(value, "light_flame_small_yellow"))
2515 overridecolor[0] = 1;
2516 overridecolor[1] = 0.5;
2517 overridecolor[2] = 0.1;
2519 if (!strcmp(value, "light_torch_small_white"))
2524 overridecolor[0] = 1;
2525 overridecolor[1] = 0.5;
2526 overridecolor[2] = 0.1;
2528 if (!strcmp(value, "light_torch_small_walltorch"))
2533 overridecolor[0] = 1;
2534 overridecolor[1] = 0.5;
2535 overridecolor[2] = 0.1;
2539 else if (!strcmp("style", key))
2540 style = atoi(value);
2541 else if (cl.worldmodel->type == mod_brushq3)
2543 if (!strcmp("scale", key))
2544 lightscale = atof(value);
2545 if (!strcmp("fade", key))
2546 fadescale = atof(value);
2549 if (light <= 0 && islight)
2551 if (lightscale <= 0)
2555 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2556 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2557 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2558 VectorCopy(overridecolor, color);
2559 VectorScale(color, light, color);
2560 VectorAdd(origin, originhack, origin);
2562 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2567 void R_Shadow_SetCursorLocationForView(void)
2569 vec_t dist, push, frac;
2570 vec3_t dest, endpos, normal;
2571 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2572 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2575 dist = frac * r_editlights_cursordistance.value;
2576 push = r_editlights_cursorpushback.value;
2580 VectorMA(endpos, push, r_viewforward, endpos);
2581 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2583 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2584 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2585 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2588 void R_Shadow_UpdateWorldLightSelection(void)
2590 if (r_editlights.integer)
2592 R_Shadow_SetCursorLocationForView();
2593 R_Shadow_SelectLightInView();
2594 R_Shadow_DrawLightSprites();
2597 R_Shadow_SelectLight(NULL);
2600 void R_Shadow_EditLights_Clear_f(void)
2602 R_Shadow_ClearWorldLights();
2605 void R_Shadow_EditLights_Reload_f(void)
2607 r_shadow_reloadlights = true;
2610 void R_Shadow_EditLights_Save_f(void)
2613 R_Shadow_SaveWorldLights();
2616 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2618 R_Shadow_ClearWorldLights();
2619 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2622 void R_Shadow_EditLights_ImportLightsFile_f(void)
2624 R_Shadow_ClearWorldLights();
2625 R_Shadow_LoadLightsFile();
2628 void R_Shadow_EditLights_Spawn_f(void)
2631 if (!r_editlights.integer)
2633 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2636 if (Cmd_Argc() != 1)
2638 Con_Printf("r_editlights_spawn does not take parameters\n");
2641 color[0] = color[1] = color[2] = 1;
2642 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2645 void R_Shadow_EditLights_Edit_f(void)
2647 vec3_t origin, color;
2650 char cubemapname[1024];
2651 if (!r_editlights.integer)
2653 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2656 if (!r_shadow_selectedlight)
2658 Con_Printf("No selected light.\n");
2661 VectorCopy(r_shadow_selectedlight->origin, origin);
2662 radius = r_shadow_selectedlight->lightradius;
2663 VectorCopy(r_shadow_selectedlight->light, color);
2664 style = r_shadow_selectedlight->style;
2665 if (r_shadow_selectedlight->cubemapname)
2666 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2669 shadows = r_shadow_selectedlight->castshadows;
2670 if (!strcmp(Cmd_Argv(1), "origin"))
2672 if (Cmd_Argc() != 5)
2674 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2677 origin[0] = atof(Cmd_Argv(2));
2678 origin[1] = atof(Cmd_Argv(3));
2679 origin[2] = atof(Cmd_Argv(4));
2681 else if (!strcmp(Cmd_Argv(1), "originx"))
2683 if (Cmd_Argc() != 3)
2685 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2688 origin[0] = atof(Cmd_Argv(2));
2690 else if (!strcmp(Cmd_Argv(1), "originy"))
2692 if (Cmd_Argc() != 3)
2694 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2697 origin[1] = atof(Cmd_Argv(2));
2699 else if (!strcmp(Cmd_Argv(1), "originz"))
2701 if (Cmd_Argc() != 3)
2703 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2706 origin[2] = atof(Cmd_Argv(2));
2708 else if (!strcmp(Cmd_Argv(1), "move"))
2710 if (Cmd_Argc() != 5)
2712 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2715 origin[0] += atof(Cmd_Argv(2));
2716 origin[1] += atof(Cmd_Argv(3));
2717 origin[2] += atof(Cmd_Argv(4));
2719 else if (!strcmp(Cmd_Argv(1), "movex"))
2721 if (Cmd_Argc() != 3)
2723 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2726 origin[0] += atof(Cmd_Argv(2));
2728 else if (!strcmp(Cmd_Argv(1), "movey"))
2730 if (Cmd_Argc() != 3)
2732 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2735 origin[1] += atof(Cmd_Argv(2));
2737 else if (!strcmp(Cmd_Argv(1), "movez"))
2739 if (Cmd_Argc() != 3)
2741 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2744 origin[2] += atof(Cmd_Argv(2));
2746 else if (!strcmp(Cmd_Argv(1), "color"))
2748 if (Cmd_Argc() != 5)
2750 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2753 color[0] = atof(Cmd_Argv(2));
2754 color[1] = atof(Cmd_Argv(3));
2755 color[2] = atof(Cmd_Argv(4));
2757 else if (!strcmp(Cmd_Argv(1), "radius"))
2759 if (Cmd_Argc() != 3)
2761 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2764 radius = atof(Cmd_Argv(2));
2766 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2768 if (Cmd_Argc() != 3)
2770 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2773 style = atoi(Cmd_Argv(2));
2775 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2779 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2782 if (Cmd_Argc() == 3)
2783 strcpy(cubemapname, Cmd_Argv(2));
2787 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2789 if (Cmd_Argc() != 3)
2791 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2794 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2798 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2799 Con_Printf("Selected light's properties:\n");
2800 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2801 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2802 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2803 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2804 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2805 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2808 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2809 r_shadow_selectedlight = NULL;
2810 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2813 extern int con_vislines;
2814 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2818 if (r_shadow_selectedlight == NULL)
2822 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2823 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;
2824 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2825 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;
2826 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2827 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2828 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;
2831 void R_Shadow_EditLights_ToggleShadow_f(void)
2833 if (!r_editlights.integer)
2835 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2838 if (!r_shadow_selectedlight)
2840 Con_Printf("No selected light.\n");
2843 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);
2844 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2845 r_shadow_selectedlight = NULL;
2848 void R_Shadow_EditLights_Remove_f(void)
2850 if (!r_editlights.integer)
2852 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2855 if (!r_shadow_selectedlight)
2857 Con_Printf("No selected light.\n");
2860 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2861 r_shadow_selectedlight = NULL;
2864 void R_Shadow_EditLights_Help_f(void)
2867 "Documentation on r_editlights system:\n"
2869 "r_editlights : enable/disable editing mode\n"
2870 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2871 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2872 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2873 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2874 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2875 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2876 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2878 "r_editlights_help : this help\n"
2879 "r_editlights_clear : remove all lights\n"
2880 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2881 "r_editlights_save : save to .rtlights file\n"
2882 "r_editlights_spawn : create a light with default settings\n"
2883 "r_editlights_edit command : edit selected light - more documentation below\n"
2884 "r_editlights_remove : remove selected light\n"
2885 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2886 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2887 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2889 "origin x y z : set light location\n"
2890 "originx x: set x component of light location\n"
2891 "originy y: set y component of light location\n"
2892 "originz z: set z component of light location\n"
2893 "move x y z : adjust light location\n"
2894 "movex x: adjust x component of light location\n"
2895 "movey y: adjust y component of light location\n"
2896 "movez z: adjust z component of light location\n"
2897 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2898 "radius radius : set radius (size) of light\n"
2899 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2900 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2901 "shadows 1/0 : turn on/off shadows\n"
2902 "<nothing> : print light properties to console\n"
2906 void R_Shadow_EditLights_Init(void)
2908 Cvar_RegisterVariable(&r_editlights);
2909 Cvar_RegisterVariable(&r_editlights_cursordistance);
2910 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2911 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2912 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2913 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2914 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2915 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2916 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2917 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2918 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2919 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2920 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2921 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2922 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2923 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2924 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2925 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);