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, false, 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);
1937 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1938 for (j = 0;j < 3;j++)
1939 for (i = 0;i < 6;i++)
1940 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1941 Con_Printf(" and was unable to find any of them.\n");
1943 return cubemaptexture;
1946 rtexture_t *R_Shadow_Cubemap(const char *basename)
1949 for (i = 0;i < numcubemaps;i++)
1950 if (!strcasecmp(cubemaps[i].basename, basename))
1951 return cubemaps[i].texture;
1952 if (i >= MAX_CUBEMAPS)
1955 strcpy(cubemaps[i].basename, basename);
1956 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1957 return cubemaps[i].texture;
1960 void R_Shadow_FreeCubemaps(void)
1963 R_FreeTexturePool(&r_shadow_filters_texturepool);
1966 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1968 int i, j, k, l, maxverts = 256, tris;
1969 float *vertex3f = NULL, mins[3], maxs[3];
1971 shadowmesh_t *mesh, *castmesh = NULL;
1973 if (radius < 15 || DotProduct(color, color) < 0.03)
1975 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1979 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1980 VectorCopy(origin, e->origin);
1981 VectorCopy(color, e->light);
1982 e->lightradius = radius;
1984 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1986 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1989 e->castshadows = castshadow;
1991 e->cullradius = e->lightradius;
1992 for (k = 0;k < 3;k++)
1994 mins[k] = e->origin[k] - e->lightradius;
1995 maxs[k] = e->origin[k] + e->lightradius;
1998 e->next = r_shadow_worldlightchain;
1999 r_shadow_worldlightchain = e;
2000 if (cubemapname && cubemapname[0])
2002 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2003 strcpy(e->cubemapname, cubemapname);
2004 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2006 // FIXME: rewrite this to store ALL geometry into a cache in the light
2008 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2009 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2012 if (cl.worldmodel->brushq3.num_leafs)
2016 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2017 VectorCopy(e->origin, e->mins);
2018 VectorCopy(e->origin, e->maxs);
2019 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2020 face->lighttemp_castshadow = false;
2021 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2023 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2025 for (k = 0;k < 3;k++)
2027 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2028 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2030 for (j = 0;j < leaf->numleaffaces;j++)
2032 face = leaf->firstleafface[j];
2033 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2034 face->lighttemp_castshadow = true;
2039 // add surfaces to shadow casting mesh and light mesh
2040 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2042 if (face->lighttemp_castshadow)
2044 face->lighttemp_castshadow = false;
2045 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2048 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2049 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2050 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2051 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);
2056 else if (cl.worldmodel->brushq1.numleafs)
2060 VectorCopy(e->origin, e->mins);
2061 VectorCopy(e->origin, e->maxs);
2062 i = CL_PointQ1Contents(e->origin);
2064 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2065 surf->lighttemp_castshadow = false;
2067 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2070 qbyte *bytesurfacepvs;
2072 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
2073 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2075 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2077 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
2079 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2081 for (k = 0;k < 3;k++)
2083 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2084 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2089 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2090 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2091 surf->lighttemp_castshadow = true;
2093 Mem_Free(byteleafpvs);
2094 Mem_Free(bytesurfacepvs);
2098 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2099 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
2101 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2103 for (k = 0;k < 3;k++)
2105 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2106 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2108 for (j = 0;j < leaf->nummarksurfaces;j++)
2110 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2111 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2112 surf->lighttemp_castshadow = true;
2118 // add surfaces to shadow casting mesh and light mesh
2119 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2121 if (surf->lighttemp_castshadow)
2123 surf->lighttemp_castshadow = false;
2124 if (e->castshadows && (surf->flags & SURF_SHADOWCAST))
2125 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);
2126 if (!(surf->flags & SURF_DRAWSKY))
2127 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);
2133 // limit box to light bounds (in case it grew larger)
2134 for (k = 0;k < 3;k++)
2136 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
2137 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
2139 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2141 // cast shadow volume from castmesh
2142 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2146 for (mesh = castmesh;mesh;mesh = mesh->next)
2148 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2149 maxverts = max(maxverts, mesh->numverts * 2);
2154 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2155 // now that we have the buffers big enough, construct and add
2156 // the shadow volume mesh
2158 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2159 for (mesh = castmesh;mesh;mesh = mesh->next)
2161 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2162 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)))
2163 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2168 // we're done with castmesh now
2169 Mod_ShadowMesh_Free(castmesh);
2172 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2173 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2176 if (e->meshchain_shadow)
2177 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2178 k += mesh->numtriangles;
2180 if (e->meshchain_light)
2181 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2182 l += mesh->numtriangles;
2183 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);
2186 void R_Shadow_FreeWorldLight(worldlight_t *light)
2188 worldlight_t **lightpointer;
2189 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2190 if (*lightpointer != light)
2191 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2192 *lightpointer = light->next;
2193 if (light->cubemapname)
2194 Mem_Free(light->cubemapname);
2195 if (light->meshchain_shadow)
2196 Mod_ShadowMesh_Free(light->meshchain_shadow);
2197 if (light->meshchain_light)
2198 Mod_ShadowMesh_Free(light->meshchain_light);
2202 void R_Shadow_ClearWorldLights(void)
2204 while (r_shadow_worldlightchain)
2205 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2206 r_shadow_selectedlight = NULL;
2207 R_Shadow_FreeCubemaps();
2210 void R_Shadow_SelectLight(worldlight_t *light)
2212 if (r_shadow_selectedlight)
2213 r_shadow_selectedlight->selected = false;
2214 r_shadow_selectedlight = light;
2215 if (r_shadow_selectedlight)
2216 r_shadow_selectedlight->selected = true;
2219 rtexture_t *lighttextures[5];
2221 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2223 float scale = r_editlights_cursorgrid.value * 0.5f;
2224 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);
2227 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2230 const worldlight_t *light;
2233 if (light->selected)
2234 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2235 if (!light->meshchain_shadow)
2237 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);
2240 void R_Shadow_DrawLightSprites(void)
2244 worldlight_t *light;
2246 for (i = 0;i < 5;i++)
2248 lighttextures[i] = NULL;
2249 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2250 lighttextures[i] = pic->tex;
2253 for (light = r_shadow_worldlightchain;light;light = light->next)
2254 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2255 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2258 void R_Shadow_SelectLightInView(void)
2260 float bestrating, rating, temp[3];
2261 worldlight_t *best, *light;
2264 for (light = r_shadow_worldlightchain;light;light = light->next)
2266 VectorSubtract(light->origin, r_vieworigin, temp);
2267 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2270 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2271 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2273 bestrating = rating;
2278 R_Shadow_SelectLight(best);
2281 void R_Shadow_LoadWorldLights(void)
2283 int n, a, style, shadow;
2284 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2285 float origin[3], radius, color[3];
2286 if (cl.worldmodel == NULL)
2288 Con_Printf("No map loaded.\n");
2291 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2292 strlcat (name, ".rtlights", sizeof (name));
2293 lightsstring = FS_LoadFile(name, false);
2301 while (*s && *s != '\n')
2307 // check for modifier flags
2313 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);
2319 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);
2322 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2323 radius *= r_editlights_rtlightssizescale.value;
2324 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2329 Con_Printf("invalid rtlights file \"%s\"\n", name);
2330 Mem_Free(lightsstring);
2334 void R_Shadow_SaveWorldLights(void)
2336 worldlight_t *light;
2337 int bufchars, bufmaxchars;
2339 char name[MAX_QPATH];
2341 if (!r_shadow_worldlightchain)
2343 if (cl.worldmodel == NULL)
2345 Con_Printf("No map loaded.\n");
2348 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2349 strlcat (name, ".rtlights", sizeof (name));
2350 bufchars = bufmaxchars = 0;
2352 for (light = r_shadow_worldlightchain;light;light = light->next)
2354 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 : "");
2355 if (bufchars + (int) strlen(line) > bufmaxchars)
2357 bufmaxchars = bufchars + strlen(line) + 2048;
2359 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2363 memcpy(buf, oldbuf, bufchars);
2369 memcpy(buf + bufchars, line, strlen(line));
2370 bufchars += strlen(line);
2374 FS_WriteFile(name, buf, bufchars);
2379 void R_Shadow_LoadLightsFile(void)
2382 char name[MAX_QPATH], *lightsstring, *s, *t;
2383 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2384 if (cl.worldmodel == NULL)
2386 Con_Printf("No map loaded.\n");
2389 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2390 strlcat (name, ".lights", sizeof (name));
2391 lightsstring = FS_LoadFile(name, false);
2399 while (*s && *s != '\n')
2404 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);
2408 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);
2411 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2412 radius = bound(15, radius, 4096);
2413 VectorScale(color, (2.0f / (8388608.0f)), color);
2414 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2419 Con_Printf("invalid lights file \"%s\"\n", name);
2420 Mem_Free(lightsstring);
2424 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2426 int entnum, style, islight;
2427 char key[256], value[1024];
2428 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2431 if (cl.worldmodel == NULL)
2433 Con_Printf("No map loaded.\n");
2436 data = cl.worldmodel->brush.entities;
2439 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2442 origin[0] = origin[1] = origin[2] = 0;
2443 originhack[0] = originhack[1] = originhack[2] = 0;
2444 color[0] = color[1] = color[2] = 1;
2445 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2452 if (!COM_ParseToken(&data, false))
2454 if (com_token[0] == '}')
2455 break; // end of entity
2456 if (com_token[0] == '_')
2457 strcpy(key, com_token + 1);
2459 strcpy(key, com_token);
2460 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2461 key[strlen(key)-1] = 0;
2462 if (!COM_ParseToken(&data, false))
2464 strcpy(value, com_token);
2466 // now that we have the key pair worked out...
2467 if (!strcmp("light", key))
2468 light = atof(value);
2469 else if (!strcmp("origin", key))
2470 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2471 else if (!strcmp("color", key))
2472 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2473 else if (!strcmp("wait", key))
2474 fadescale = atof(value);
2475 else if (!strcmp("classname", key))
2477 if (!strncmp(value, "light", 5))
2480 if (!strcmp(value, "light_fluoro"))
2485 overridecolor[0] = 1;
2486 overridecolor[1] = 1;
2487 overridecolor[2] = 1;
2489 if (!strcmp(value, "light_fluorospark"))
2494 overridecolor[0] = 1;
2495 overridecolor[1] = 1;
2496 overridecolor[2] = 1;
2498 if (!strcmp(value, "light_globe"))
2503 overridecolor[0] = 1;
2504 overridecolor[1] = 0.8;
2505 overridecolor[2] = 0.4;
2507 if (!strcmp(value, "light_flame_large_yellow"))
2512 overridecolor[0] = 1;
2513 overridecolor[1] = 0.5;
2514 overridecolor[2] = 0.1;
2516 if (!strcmp(value, "light_flame_small_yellow"))
2521 overridecolor[0] = 1;
2522 overridecolor[1] = 0.5;
2523 overridecolor[2] = 0.1;
2525 if (!strcmp(value, "light_torch_small_white"))
2530 overridecolor[0] = 1;
2531 overridecolor[1] = 0.5;
2532 overridecolor[2] = 0.1;
2534 if (!strcmp(value, "light_torch_small_walltorch"))
2539 overridecolor[0] = 1;
2540 overridecolor[1] = 0.5;
2541 overridecolor[2] = 0.1;
2545 else if (!strcmp("style", key))
2546 style = atoi(value);
2547 else if (cl.worldmodel->type == mod_brushq3)
2549 if (!strcmp("scale", key))
2550 lightscale = atof(value);
2551 if (!strcmp("fade", key))
2552 fadescale = atof(value);
2555 if (light <= 0 && islight)
2557 if (lightscale <= 0)
2561 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2562 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2563 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2564 VectorCopy(overridecolor, color);
2565 VectorScale(color, light, color);
2566 VectorAdd(origin, originhack, origin);
2568 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2573 void R_Shadow_SetCursorLocationForView(void)
2575 vec_t dist, push, frac;
2576 vec3_t dest, endpos, normal;
2577 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2578 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2581 dist = frac * r_editlights_cursordistance.value;
2582 push = r_editlights_cursorpushback.value;
2586 VectorMA(endpos, push, r_viewforward, endpos);
2587 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2589 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2590 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2591 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2594 void R_Shadow_UpdateWorldLightSelection(void)
2596 if (r_editlights.integer)
2598 R_Shadow_SetCursorLocationForView();
2599 R_Shadow_SelectLightInView();
2600 R_Shadow_DrawLightSprites();
2603 R_Shadow_SelectLight(NULL);
2606 void R_Shadow_EditLights_Clear_f(void)
2608 R_Shadow_ClearWorldLights();
2611 void R_Shadow_EditLights_Reload_f(void)
2613 r_shadow_reloadlights = true;
2616 void R_Shadow_EditLights_Save_f(void)
2619 R_Shadow_SaveWorldLights();
2622 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2624 R_Shadow_ClearWorldLights();
2625 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2628 void R_Shadow_EditLights_ImportLightsFile_f(void)
2630 R_Shadow_ClearWorldLights();
2631 R_Shadow_LoadLightsFile();
2634 void R_Shadow_EditLights_Spawn_f(void)
2637 if (!r_editlights.integer)
2639 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2642 if (Cmd_Argc() != 1)
2644 Con_Printf("r_editlights_spawn does not take parameters\n");
2647 color[0] = color[1] = color[2] = 1;
2648 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2651 void R_Shadow_EditLights_Edit_f(void)
2653 vec3_t origin, color;
2656 char cubemapname[1024];
2657 if (!r_editlights.integer)
2659 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2662 if (!r_shadow_selectedlight)
2664 Con_Printf("No selected light.\n");
2667 VectorCopy(r_shadow_selectedlight->origin, origin);
2668 radius = r_shadow_selectedlight->lightradius;
2669 VectorCopy(r_shadow_selectedlight->light, color);
2670 style = r_shadow_selectedlight->style;
2671 if (r_shadow_selectedlight->cubemapname)
2672 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2675 shadows = r_shadow_selectedlight->castshadows;
2676 if (!strcmp(Cmd_Argv(1), "origin"))
2678 if (Cmd_Argc() != 5)
2680 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2683 origin[0] = atof(Cmd_Argv(2));
2684 origin[1] = atof(Cmd_Argv(3));
2685 origin[2] = atof(Cmd_Argv(4));
2687 else if (!strcmp(Cmd_Argv(1), "originx"))
2689 if (Cmd_Argc() != 3)
2691 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2694 origin[0] = atof(Cmd_Argv(2));
2696 else if (!strcmp(Cmd_Argv(1), "originy"))
2698 if (Cmd_Argc() != 3)
2700 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2703 origin[1] = atof(Cmd_Argv(2));
2705 else if (!strcmp(Cmd_Argv(1), "originz"))
2707 if (Cmd_Argc() != 3)
2709 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2712 origin[2] = atof(Cmd_Argv(2));
2714 else if (!strcmp(Cmd_Argv(1), "move"))
2716 if (Cmd_Argc() != 5)
2718 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2721 origin[0] += atof(Cmd_Argv(2));
2722 origin[1] += atof(Cmd_Argv(3));
2723 origin[2] += atof(Cmd_Argv(4));
2725 else if (!strcmp(Cmd_Argv(1), "movex"))
2727 if (Cmd_Argc() != 3)
2729 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2732 origin[0] += atof(Cmd_Argv(2));
2734 else if (!strcmp(Cmd_Argv(1), "movey"))
2736 if (Cmd_Argc() != 3)
2738 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2741 origin[1] += atof(Cmd_Argv(2));
2743 else if (!strcmp(Cmd_Argv(1), "movez"))
2745 if (Cmd_Argc() != 3)
2747 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2750 origin[2] += atof(Cmd_Argv(2));
2752 else if (!strcmp(Cmd_Argv(1), "color"))
2754 if (Cmd_Argc() != 5)
2756 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2759 color[0] = atof(Cmd_Argv(2));
2760 color[1] = atof(Cmd_Argv(3));
2761 color[2] = atof(Cmd_Argv(4));
2763 else if (!strcmp(Cmd_Argv(1), "radius"))
2765 if (Cmd_Argc() != 3)
2767 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2770 radius = atof(Cmd_Argv(2));
2772 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2774 if (Cmd_Argc() != 3)
2776 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2779 style = atoi(Cmd_Argv(2));
2781 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2785 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2788 if (Cmd_Argc() == 3)
2789 strcpy(cubemapname, Cmd_Argv(2));
2793 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2795 if (Cmd_Argc() != 3)
2797 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2800 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2804 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2805 Con_Printf("Selected light's properties:\n");
2806 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2807 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2808 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2809 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2810 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2811 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2814 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2815 r_shadow_selectedlight = NULL;
2816 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2819 extern int con_vislines;
2820 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2824 if (r_shadow_selectedlight == NULL)
2828 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2829 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;
2830 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2831 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;
2832 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2833 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2834 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;
2837 void R_Shadow_EditLights_ToggleShadow_f(void)
2839 if (!r_editlights.integer)
2841 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2844 if (!r_shadow_selectedlight)
2846 Con_Printf("No selected light.\n");
2849 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);
2850 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2851 r_shadow_selectedlight = NULL;
2854 void R_Shadow_EditLights_Remove_f(void)
2856 if (!r_editlights.integer)
2858 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2861 if (!r_shadow_selectedlight)
2863 Con_Printf("No selected light.\n");
2866 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2867 r_shadow_selectedlight = NULL;
2870 void R_Shadow_EditLights_Help_f(void)
2873 "Documentation on r_editlights system:\n"
2875 "r_editlights : enable/disable editing mode\n"
2876 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2877 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2878 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2879 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2880 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2881 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2882 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2884 "r_editlights_help : this help\n"
2885 "r_editlights_clear : remove all lights\n"
2886 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2887 "r_editlights_save : save to .rtlights file\n"
2888 "r_editlights_spawn : create a light with default settings\n"
2889 "r_editlights_edit command : edit selected light - more documentation below\n"
2890 "r_editlights_remove : remove selected light\n"
2891 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2892 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2893 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2895 "origin x y z : set light location\n"
2896 "originx x: set x component of light location\n"
2897 "originy y: set y component of light location\n"
2898 "originz z: set z component of light location\n"
2899 "move x y z : adjust light location\n"
2900 "movex x: adjust x component of light location\n"
2901 "movey y: adjust y component of light location\n"
2902 "movez z: adjust z component of light location\n"
2903 "color r g b : set color of light (can be brighter than 1 1 1)\n"
2904 "radius radius : set radius (size) of light\n"
2905 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
2906 "cubemap basename : set filter cubemap of light (not yet supported)\n"
2907 "shadows 1/0 : turn on/off shadows\n"
2908 "<nothing> : print light properties to console\n"
2912 void R_Shadow_EditLights_Init(void)
2914 Cvar_RegisterVariable(&r_editlights);
2915 Cvar_RegisterVariable(&r_editlights_cursordistance);
2916 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2917 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2918 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2919 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2920 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2921 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2922 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
2923 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2924 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2925 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2926 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2927 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2928 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2929 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2930 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2931 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);