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 matrix4x4_t matrix_attenuationxyz =
308 {0.5, 0.0, 0.0, 0.5},
309 {0.0, 0.5, 0.0, 0.5},
310 {0.0, 0.0, 0.5, 0.5},
315 matrix4x4_t matrix_attenuationz =
318 {0.0, 0.0, 0.5, 0.5},
319 {0.0, 0.0, 0.0, 0.0},
320 {0.0, 0.0, 0.0, 0.0},
325 void R_Shadow_ResizeTriangleFacingLight(int numtris)
327 // make sure trianglefacinglight is big enough for this volume
328 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
329 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
330 if (maxtrianglefacinglight < numtris)
332 maxtrianglefacinglight = numtris;
333 if (trianglefacinglight)
334 Mem_Free(trianglefacinglight);
335 if (trianglefacinglightlist)
336 Mem_Free(trianglefacinglightlist);
337 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
338 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
342 int *R_Shadow_ResizeShadowElements(int numtris)
344 // make sure shadowelements is big enough for this volume
345 if (maxshadowelements < numtris * 24)
347 maxshadowelements = numtris * 24;
349 Mem_Free(shadowelements);
350 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
352 return shadowelements;
356 // readable version of some code found below
357 //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]))))
358 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
360 float dir0[3], dir1[3], normal[3];
362 // calculate two mostly perpendicular edge directions
363 VectorSubtract(a, b, dir0);
364 VectorSubtract(c, b, dir1);
366 // we have two edge directions, we can calculate a third vector from
367 // them, which is the direction of the surface normal (it's magnitude
369 CrossProduct(dir0, dir1, normal);
371 // compare distance of light along normal, with distance of any point
372 // of the triangle along the same normal (the triangle is planar,
373 // I.E. flat, so all points give the same answer)
374 return DotProduct(p, normal) > DotProduct(a, normal);
376 int checkcastshadowfromedge(int t, int i)
380 if (t >= trianglerange_start && t < trianglerange_end)
382 if (t < i && !trianglefacinglight[t])
393 te = inelement3i + t * 3;
394 v[0] = invertex3f + te[0] * 3;
395 v[1] = invertex3f + te[1] * 3;
396 v[2] = invertex3f + te[2] * 3;
397 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
406 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)
408 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
410 const int *e, *n, *te;
413 // make sure trianglefacinglight is big enough for this volume
414 if (maxtrianglefacinglight < trianglerange_end)
415 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
417 if (maxvertexupdate < innumvertices)
419 maxvertexupdate = innumvertices;
421 Mem_Free(vertexupdate);
423 Mem_Free(vertexremap);
424 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
425 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
429 if (r_shadow_singlepassvolumegeneration.integer)
431 // one pass approach (identify lit/dark faces and generate sides while doing so)
432 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
434 // calculate triangle facing flag
435 v[0] = invertex3f + e[0] * 3;
436 v[1] = invertex3f + e[1] * 3;
437 v[2] = invertex3f + e[2] * 3;
438 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
440 // make sure the vertices are created
441 for (j = 0;j < 3;j++)
443 if (vertexupdate[e[j]] != vertexupdatenum)
445 vertexupdate[e[j]] = vertexupdatenum;
446 vertexremap[e[j]] = outvertices;
447 VectorCopy(v[j], outvertex3f);
448 VectorSubtract(v[j], relativelightorigin, temp);
449 f = projectdistance / VectorLength(temp);
450 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
455 // output the front and back triangles
456 vr[0] = vertexremap[e[0]];
457 vr[1] = vertexremap[e[1]];
458 vr[2] = vertexremap[e[2]];
459 outelement3i[0] = vr[0];
460 outelement3i[1] = vr[1];
461 outelement3i[2] = vr[2];
462 outelement3i[3] = vr[2] + 1;
463 outelement3i[4] = vr[1] + 1;
464 outelement3i[5] = vr[0] + 1;
467 // output the sides (facing outward from this triangle)
469 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]))))
471 outelement3i[0] = vr[1];
472 outelement3i[1] = vr[0];
473 outelement3i[2] = vr[0] + 1;
474 outelement3i[3] = vr[1];
475 outelement3i[4] = vr[0] + 1;
476 outelement3i[5] = vr[1] + 1;
481 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]))))
483 outelement3i[0] = vr[2];
484 outelement3i[1] = vr[1];
485 outelement3i[2] = vr[1] + 1;
486 outelement3i[3] = vr[2];
487 outelement3i[4] = vr[1] + 1;
488 outelement3i[5] = vr[2] + 1;
493 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]))))
495 outelement3i[0] = vr[0];
496 outelement3i[1] = vr[2];
497 outelement3i[2] = vr[2] + 1;
498 outelement3i[3] = vr[0];
499 outelement3i[4] = vr[2] + 1;
500 outelement3i[5] = vr[0] + 1;
507 // this triangle is not facing the light
508 // output the sides (facing inward to this triangle)
510 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
512 vr[0] = vertexremap[e[0]];
513 vr[1] = vertexremap[e[1]];
514 outelement3i[0] = vr[1];
515 outelement3i[1] = vr[0] + 1;
516 outelement3i[2] = vr[0];
517 outelement3i[3] = vr[1];
518 outelement3i[4] = vr[1] + 1;
519 outelement3i[5] = vr[0] + 1;
524 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
526 vr[1] = vertexremap[e[1]];
527 vr[2] = vertexremap[e[2]];
528 outelement3i[0] = vr[2];
529 outelement3i[1] = vr[1] + 1;
530 outelement3i[2] = vr[1];
531 outelement3i[3] = vr[2];
532 outelement3i[4] = vr[2] + 1;
533 outelement3i[5] = vr[1] + 1;
538 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
540 vr[0] = vertexremap[e[0]];
541 vr[2] = vertexremap[e[2]];
542 outelement3i[0] = vr[0];
543 outelement3i[1] = vr[2] + 1;
544 outelement3i[2] = vr[2];
545 outelement3i[3] = vr[0];
546 outelement3i[4] = vr[0] + 1;
547 outelement3i[5] = vr[2] + 1;
556 // two pass approach (identify lit/dark faces and then generate sides)
557 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
559 // calculate triangle facing flag
560 v[0] = invertex3f + e[0] * 3;
561 v[1] = invertex3f + e[1] * 3;
562 v[2] = invertex3f + e[2] * 3;
563 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
565 trianglefacinglightlist[numfacing++] = i;
566 // make sure the vertices are created
567 for (j = 0;j < 3;j++)
569 if (vertexupdate[e[j]] != vertexupdatenum)
571 vertexupdate[e[j]] = vertexupdatenum;
572 vertexremap[e[j]] = outvertices;
573 VectorSubtract(v[j], relativelightorigin, temp);
574 f = projectdistance / VectorLength(temp);
575 VectorCopy(v[j], outvertex3f);
576 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
581 // output the front and back triangles
582 outelement3i[0] = vertexremap[e[0]];
583 outelement3i[1] = vertexremap[e[1]];
584 outelement3i[2] = vertexremap[e[2]];
585 outelement3i[3] = vertexremap[e[2]] + 1;
586 outelement3i[4] = vertexremap[e[1]] + 1;
587 outelement3i[5] = vertexremap[e[0]] + 1;
592 for (i = 0;i < numfacing;i++)
594 t = trianglefacinglightlist[i];
595 e = inelement3i + t * 3;
596 n = inneighbor3i + t * 3;
597 // output the sides (facing outward from this triangle)
599 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]))))
601 vr[0] = vertexremap[e[0]];
602 vr[1] = vertexremap[e[1]];
603 outelement3i[0] = vr[1];
604 outelement3i[1] = vr[0];
605 outelement3i[2] = vr[0] + 1;
606 outelement3i[3] = vr[1];
607 outelement3i[4] = vr[0] + 1;
608 outelement3i[5] = vr[1] + 1;
613 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]))))
615 vr[1] = vertexremap[e[1]];
616 vr[2] = vertexremap[e[2]];
617 outelement3i[0] = vr[2];
618 outelement3i[1] = vr[1];
619 outelement3i[2] = vr[1] + 1;
620 outelement3i[3] = vr[2];
621 outelement3i[4] = vr[1] + 1;
622 outelement3i[5] = vr[2] + 1;
627 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]))))
629 vr[0] = vertexremap[e[0]];
630 vr[2] = vertexremap[e[2]];
631 outelement3i[0] = vr[0];
632 outelement3i[1] = vr[2];
633 outelement3i[2] = vr[2] + 1;
634 outelement3i[3] = vr[0];
635 outelement3i[4] = vr[2] + 1;
636 outelement3i[5] = vr[0] + 1;
643 *outnumvertices = outvertices;
647 float varray_vertex3f2[65536*3];
649 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
652 if (projectdistance < 0.1)
654 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
660 // make sure shadowelements is big enough for this volume
661 if (maxshadowelements < numtris * 24)
662 R_Shadow_ResizeShadowElements(numtris);
664 // check which triangles are facing the light, and then output
665 // triangle elements and vertices... by clever use of elements we
666 // can construct the whole shadow from the unprojected vertices and
667 // the projected vertices
668 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
670 GL_VertexPointer(varray_vertex3f2);
671 if (r_shadowstage == SHADOWSTAGE_STENCIL)
673 // decrement stencil if frontface is behind depthbuffer
674 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
675 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
676 R_Mesh_Draw(outverts, tris, shadowelements);
678 c_rt_shadowtris += numtris;
679 // increment stencil if backface is behind depthbuffer
680 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
681 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
683 R_Mesh_Draw(outverts, tris, shadowelements);
685 c_rt_shadowtris += numtris;
689 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
692 if (r_shadowstage == SHADOWSTAGE_STENCIL)
694 // decrement stencil if frontface is behind depthbuffer
695 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
696 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
697 for (mesh = firstmesh;mesh;mesh = mesh->next)
699 GL_VertexPointer(mesh->vertex3f);
700 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
701 c_rtcached_shadowmeshes++;
702 c_rtcached_shadowtris += mesh->numtriangles;
704 // increment stencil if backface is behind depthbuffer
705 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
706 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
708 for (mesh = firstmesh;mesh;mesh = mesh->next)
710 GL_VertexPointer(mesh->vertex3f);
711 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
712 c_rtcached_shadowmeshes++;
713 c_rtcached_shadowtris += mesh->numtriangles;
717 float r_shadow_attenpower, r_shadow_attenscale;
718 static void R_Shadow_MakeTextures(void)
720 int x, y, z, d, side;
721 float v[3], s, t, intensity;
723 R_FreeTexturePool(&r_shadow_texturepool);
724 r_shadow_texturepool = R_AllocTexturePool();
725 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
726 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
728 #define ATTEN2DSIZE 64
729 #define ATTEN3DSIZE 32
730 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
735 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
740 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
745 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
746 if (gl_texturecubemap)
748 for (side = 0;side < 6;side++)
750 for (y = 0;y < NORMSIZE;y++)
752 for (x = 0;x < NORMSIZE;x++)
754 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
755 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
789 intensity = 127.0f / sqrt(DotProduct(v, v));
790 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
791 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
792 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
793 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
797 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
800 r_shadow_normalcubetexture = NULL;
801 for (y = 0;y < ATTEN2DSIZE;y++)
803 for (x = 0;x < ATTEN2DSIZE;x++)
805 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
806 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
808 intensity = 1.0f - sqrt(DotProduct(v, v));
810 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
811 d = bound(0, intensity, 255);
812 data[(y*ATTEN2DSIZE+x)*4+0] = d;
813 data[(y*ATTEN2DSIZE+x)*4+1] = d;
814 data[(y*ATTEN2DSIZE+x)*4+2] = d;
815 data[(y*ATTEN2DSIZE+x)*4+3] = d;
818 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
819 if (r_shadow_texture3d.integer)
821 for (z = 0;z < ATTEN3DSIZE;z++)
823 for (y = 0;y < ATTEN3DSIZE;y++)
825 for (x = 0;x < ATTEN3DSIZE;x++)
827 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
828 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
829 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
830 intensity = 1.0f - sqrt(DotProduct(v, v));
832 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
833 d = bound(0, intensity, 255);
834 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
835 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
836 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
837 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
841 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
846 void R_Shadow_Stage_Begin(void)
850 if (r_shadow_texture3d.integer && !gl_texture3d)
851 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
853 if (!r_shadow_attenuation2dtexture
854 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
855 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
856 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
857 R_Shadow_MakeTextures();
859 memset(&m, 0, sizeof(m));
860 GL_BlendFunc(GL_ONE, GL_ZERO);
863 R_Mesh_State_Texture(&m);
864 GL_Color(0, 0, 0, 1);
865 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
866 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
867 r_shadowstage = SHADOWSTAGE_NONE;
869 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
870 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
871 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
874 void R_Shadow_LoadWorldLightsIfNeeded(void)
876 if (r_shadow_reloadlights && cl.worldmodel)
878 R_Shadow_ClearWorldLights();
879 r_shadow_reloadlights = false;
880 R_Shadow_LoadWorldLights();
881 if (r_shadow_worldlightchain == NULL)
883 R_Shadow_LoadLightsFile();
884 if (r_shadow_worldlightchain == NULL)
885 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
890 void R_Shadow_Stage_ShadowVolumes(void)
893 memset(&m, 0, sizeof(m));
894 R_Mesh_State_Texture(&m);
895 GL_Color(1, 1, 1, 1);
896 qglColorMask(0, 0, 0, 0);
897 GL_BlendFunc(GL_ONE, GL_ZERO);
900 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
901 //if (r_shadow_polygonoffset.value != 0)
903 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
904 // qglEnable(GL_POLYGON_OFFSET_FILL);
907 // qglDisable(GL_POLYGON_OFFSET_FILL);
908 qglDepthFunc(GL_LESS);
909 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
910 qglEnable(GL_STENCIL_TEST);
911 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
912 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
913 r_shadowstage = SHADOWSTAGE_STENCIL;
914 qglClear(GL_STENCIL_BUFFER_BIT);
916 // LordHavoc note: many shadow volumes reside entirely inside the world
917 // (that is to say they are entirely bounded by their lit surfaces),
918 // which can be optimized by handling things as an inverted light volume,
919 // with the shadow boundaries of the world being simulated by an altered
920 // (129) bias to stencil clearing on such lights
921 // FIXME: generate inverted light volumes for use as shadow volumes and
922 // optimize for them as noted above
925 void R_Shadow_Stage_LightWithoutShadows(void)
928 memset(&m, 0, sizeof(m));
929 R_Mesh_State_Texture(&m);
930 GL_BlendFunc(GL_ONE, GL_ONE);
933 qglPolygonOffset(0, 0);
934 //qglDisable(GL_POLYGON_OFFSET_FILL);
935 GL_Color(1, 1, 1, 1);
936 qglColorMask(1, 1, 1, 1);
937 qglDepthFunc(GL_EQUAL);
938 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
939 qglDisable(GL_STENCIL_TEST);
940 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
941 qglStencilFunc(GL_EQUAL, 128, 0xFF);
942 r_shadowstage = SHADOWSTAGE_LIGHT;
946 void R_Shadow_Stage_LightWithShadows(void)
949 memset(&m, 0, sizeof(m));
950 R_Mesh_State_Texture(&m);
951 GL_BlendFunc(GL_ONE, GL_ONE);
954 qglPolygonOffset(0, 0);
955 //qglDisable(GL_POLYGON_OFFSET_FILL);
956 GL_Color(1, 1, 1, 1);
957 qglColorMask(1, 1, 1, 1);
958 qglDepthFunc(GL_EQUAL);
959 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
960 qglEnable(GL_STENCIL_TEST);
961 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
962 // only draw light where this geometry was already rendered AND the
963 // stencil is 128 (values other than this mean shadow)
964 qglStencilFunc(GL_EQUAL, 128, 0xFF);
965 r_shadowstage = SHADOWSTAGE_LIGHT;
969 void R_Shadow_Stage_End(void)
972 memset(&m, 0, sizeof(m));
973 R_Mesh_State_Texture(&m);
974 GL_BlendFunc(GL_ONE, GL_ZERO);
977 qglPolygonOffset(0, 0);
978 //qglDisable(GL_POLYGON_OFFSET_FILL);
979 GL_Color(1, 1, 1, 1);
980 qglColorMask(1, 1, 1, 1);
981 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
982 qglDepthFunc(GL_LEQUAL);
983 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
984 qglDisable(GL_STENCIL_TEST);
985 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
986 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
987 r_shadowstage = SHADOWSTAGE_NONE;
990 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
992 int i, ix1, iy1, ix2, iy2;
993 float x1, y1, x2, y2, x, y, f;
996 if (!r_shadow_scissor.integer)
998 // if view is inside the box, just say yes it's visible
999 // LordHavoc: for some odd reason scissor seems broken without stencil
1000 // (?!? seems like a driver bug) so abort if gl_stencil is false
1001 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1003 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
1006 for (i = 0;i < 3;i++)
1008 if (r_viewforward[i] >= 0)
1019 f = DotProduct(r_viewforward, r_vieworigin) + 1;
1020 if (DotProduct(r_viewforward, v2) <= f)
1022 // entirely behind nearclip plane
1025 if (DotProduct(r_viewforward, v) >= f)
1027 // entirely infront of nearclip plane
1028 x1 = y1 = x2 = y2 = 0;
1029 for (i = 0;i < 8;i++)
1031 v[0] = (i & 1) ? mins[0] : maxs[0];
1032 v[1] = (i & 2) ? mins[1] : maxs[1];
1033 v[2] = (i & 4) ? mins[2] : maxs[2];
1035 GL_TransformToScreen(v, v2);
1036 //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]);
1055 // clipped by nearclip plane
1056 // this is nasty and crude...
1057 // create viewspace bbox
1058 for (i = 0;i < 8;i++)
1060 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1061 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1062 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1063 v2[0] = -DotProduct(v, r_viewleft);
1064 v2[1] = DotProduct(v, r_viewup);
1065 v2[2] = DotProduct(v, r_viewforward);
1068 if (smins[0] > v2[0]) smins[0] = v2[0];
1069 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1070 if (smins[1] > v2[1]) smins[1] = v2[1];
1071 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1072 if (smins[2] > v2[2]) smins[2] = v2[2];
1073 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1077 smins[0] = smaxs[0] = v2[0];
1078 smins[1] = smaxs[1] = v2[1];
1079 smins[2] = smaxs[2] = v2[2];
1082 // now we have a bbox in viewspace
1083 // clip it to the view plane
1086 // return true if that culled the box
1087 if (smins[2] >= smaxs[2])
1089 // ok some of it is infront of the view, transform each corner back to
1090 // worldspace and then to screenspace and make screen rect
1091 // initialize these variables just to avoid compiler warnings
1092 x1 = y1 = x2 = y2 = 0;
1093 for (i = 0;i < 8;i++)
1095 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1096 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1097 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1098 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1099 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1100 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1102 GL_TransformToScreen(v, v2);
1103 //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]);
1120 // this code doesn't handle boxes with any points behind view properly
1121 x1 = 1000;x2 = -1000;
1122 y1 = 1000;y2 = -1000;
1123 for (i = 0;i < 8;i++)
1125 v[0] = (i & 1) ? mins[0] : maxs[0];
1126 v[1] = (i & 2) ? mins[1] : maxs[1];
1127 v[2] = (i & 4) ? mins[2] : maxs[2];
1129 GL_TransformToScreen(v, v2);
1130 //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]);
1148 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1149 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1150 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1151 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1152 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1153 if (ix2 <= ix1 || iy2 <= iy1)
1155 // set up the scissor rectangle
1156 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1157 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1158 //qglEnable(GL_SCISSOR_TEST);
1163 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1165 float *color4f = varray_color4f;
1166 float dist, dot, intensity, v[3], n[3];
1167 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1169 Matrix4x4_Transform(m, vertex3f, v);
1170 if ((dist = DotProduct(v, v)) < 1)
1172 Matrix4x4_Transform3x3(m, normal3f, n);
1173 if ((dot = DotProduct(n, v)) > 0)
1176 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1177 VectorScale(lightcolor, intensity, color4f);
1182 VectorClear(color4f);
1188 VectorClear(color4f);
1194 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1196 float *color4f = varray_color4f;
1197 float dist, dot, intensity, v[3], n[3];
1198 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1200 Matrix4x4_Transform(m, vertex3f, v);
1201 if ((dist = fabs(v[2])) < 1)
1203 Matrix4x4_Transform3x3(m, normal3f, n);
1204 if ((dot = DotProduct(n, v)) > 0)
1206 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1207 VectorScale(lightcolor, intensity, color4f);
1212 VectorClear(color4f);
1218 VectorClear(color4f);
1224 // FIXME: this should be done in a vertex program when possible
1225 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1226 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1230 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1231 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1232 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1239 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1243 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1244 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1251 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)
1255 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1257 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1258 // the cubemap normalizes this for us
1259 out3f[0] = DotProduct(svector3f, lightdir);
1260 out3f[1] = DotProduct(tvector3f, lightdir);
1261 out3f[2] = DotProduct(normal3f, lightdir);
1265 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)
1268 float lightdir[3], eyedir[3], halfdir[3];
1269 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1271 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1272 VectorNormalizeFast(lightdir);
1273 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1274 VectorNormalizeFast(eyedir);
1275 VectorAdd(lightdir, eyedir, halfdir);
1276 // the cubemap normalizes this for us
1277 out3f[0] = DotProduct(svector3f, halfdir);
1278 out3f[1] = DotProduct(tvector3f, halfdir);
1279 out3f[2] = DotProduct(normal3f, halfdir);
1283 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_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1286 float color[3], color2[3];
1288 GL_VertexPointer(vertex3f);
1289 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1292 bumptexture = r_shadow_blankbumptexture;
1294 // colorscale accounts for how much we multiply the brightness during combine
1295 // mult is how many times the final pass of the lighting will be
1296 // performed to get more brightness than otherwise possible
1297 // limit mult to 64 for sanity sake
1298 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1300 // 3/2 3D combine path (Geforce3, Radeon 8500)
1301 memset(&m, 0, sizeof(m));
1302 m.tex[0] = R_GetTexture(bumptexture);
1303 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1304 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1305 m.texcombinergb[0] = GL_REPLACE;
1306 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1307 m.pointer_texcoord[0] = texcoord2f;
1308 m.pointer_texcoord[1] = varray_texcoord3f[1];
1309 m.pointer_texcoord[2] = varray_texcoord3f[2];
1310 R_Mesh_State_Texture(&m);
1311 qglColorMask(0,0,0,1);
1312 GL_BlendFunc(GL_ONE, GL_ZERO);
1313 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1314 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1315 R_Mesh_Draw(numverts, numtriangles, elements);
1317 c_rt_lighttris += numtriangles;
1319 memset(&m, 0, sizeof(m));
1320 m.tex[0] = R_GetTexture(basetexture);
1321 m.pointer_texcoord[0] = texcoord2f;
1324 m.texcubemap[1] = R_GetTexture(lightcubemap);
1325 m.pointer_texcoord[1] = varray_texcoord3f[1];
1326 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1328 R_Mesh_State_Texture(&m);
1329 qglColorMask(1,1,1,0);
1330 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1331 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1332 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1334 color[0] = bound(0, color2[0], 1);
1335 color[1] = bound(0, color2[1], 1);
1336 color[2] = bound(0, color2[2], 1);
1337 GL_Color(color[0], color[1], color[2], 1);
1338 R_Mesh_Draw(numverts, numtriangles, elements);
1340 c_rt_lighttris += numtriangles;
1343 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1345 // 1/2/2 3D combine path (original Radeon)
1346 memset(&m, 0, sizeof(m));
1347 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1348 m.pointer_texcoord[0] = varray_texcoord3f[0];
1349 R_Mesh_State_Texture(&m);
1350 qglColorMask(0,0,0,1);
1351 GL_BlendFunc(GL_ONE, GL_ZERO);
1352 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1353 R_Mesh_Draw(numverts, numtriangles, elements);
1355 c_rt_lighttris += numtriangles;
1357 memset(&m, 0, sizeof(m));
1358 m.tex[0] = R_GetTexture(bumptexture);
1359 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1360 m.texcombinergb[0] = GL_REPLACE;
1361 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1362 m.pointer_texcoord[0] = texcoord2f;
1363 m.pointer_texcoord[1] = varray_texcoord3f[1];
1364 R_Mesh_State_Texture(&m);
1365 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1366 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1367 R_Mesh_Draw(numverts, numtriangles, elements);
1369 c_rt_lighttris += numtriangles;
1371 memset(&m, 0, sizeof(m));
1372 m.tex[0] = R_GetTexture(basetexture);
1373 m.pointer_texcoord[0] = texcoord2f;
1376 m.texcubemap[1] = R_GetTexture(lightcubemap);
1377 m.pointer_texcoord[1] = varray_texcoord3f[1];
1378 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1380 R_Mesh_State_Texture(&m);
1381 qglColorMask(1,1,1,0);
1382 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1383 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1384 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1386 color[0] = bound(0, color2[0], 1);
1387 color[1] = bound(0, color2[1], 1);
1388 color[2] = bound(0, color2[2], 1);
1389 GL_Color(color[0], color[1], color[2], 1);
1390 R_Mesh_Draw(numverts, numtriangles, elements);
1392 c_rt_lighttris += numtriangles;
1395 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1397 // 2/2 3D combine path (original Radeon)
1398 memset(&m, 0, sizeof(m));
1399 m.tex[0] = R_GetTexture(bumptexture);
1400 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1401 m.texcombinergb[0] = GL_REPLACE;
1402 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1403 m.pointer_texcoord[0] = texcoord2f;
1404 m.pointer_texcoord[1] = varray_texcoord3f[1];
1405 R_Mesh_State_Texture(&m);
1406 qglColorMask(0,0,0,1);
1407 GL_BlendFunc(GL_ONE, GL_ZERO);
1408 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1409 R_Mesh_Draw(numverts, numtriangles, elements);
1411 c_rt_lighttris += numtriangles;
1413 memset(&m, 0, sizeof(m));
1414 m.tex[0] = R_GetTexture(basetexture);
1415 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1416 m.pointer_texcoord[0] = texcoord2f;
1417 m.pointer_texcoord[1] = varray_texcoord3f[1];
1418 R_Mesh_State_Texture(&m);
1419 qglColorMask(1,1,1,0);
1420 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1421 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1422 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1423 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1425 color[0] = bound(0, color2[0], 1);
1426 color[1] = bound(0, color2[1], 1);
1427 color[2] = bound(0, color2[2], 1);
1428 GL_Color(color[0], color[1], color[2], 1);
1429 R_Mesh_Draw(numverts, numtriangles, elements);
1431 c_rt_lighttris += numtriangles;
1434 else if (r_textureunits.integer >= 4)
1436 // 4/2 2D combine path (Geforce3, Radeon 8500)
1437 memset(&m, 0, sizeof(m));
1438 m.tex[0] = R_GetTexture(bumptexture);
1439 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1440 m.texcombinergb[0] = GL_REPLACE;
1441 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1442 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1443 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1444 m.pointer_texcoord[0] = texcoord2f;
1445 m.pointer_texcoord[1] = varray_texcoord3f[1];
1446 m.pointer_texcoord[2] = varray_texcoord2f[2];
1447 m.pointer_texcoord[3] = varray_texcoord2f[3];
1448 R_Mesh_State_Texture(&m);
1449 qglColorMask(0,0,0,1);
1450 GL_BlendFunc(GL_ONE, GL_ZERO);
1451 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1452 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1453 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1454 R_Mesh_Draw(numverts, numtriangles, elements);
1456 c_rt_lighttris += numtriangles;
1458 memset(&m, 0, sizeof(m));
1459 m.tex[0] = R_GetTexture(basetexture);
1460 m.pointer_texcoord[0] = texcoord2f;
1463 m.texcubemap[1] = R_GetTexture(lightcubemap);
1464 m.pointer_texcoord[1] = varray_texcoord3f[1];
1465 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1467 R_Mesh_State_Texture(&m);
1468 qglColorMask(1,1,1,0);
1469 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1470 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1471 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1473 color[0] = bound(0, color2[0], 1);
1474 color[1] = bound(0, color2[1], 1);
1475 color[2] = bound(0, color2[2], 1);
1476 GL_Color(color[0], color[1], color[2], 1);
1477 R_Mesh_Draw(numverts, numtriangles, elements);
1479 c_rt_lighttris += numtriangles;
1484 // 2/2/2 2D combine path (any dot3 card)
1485 memset(&m, 0, sizeof(m));
1486 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1487 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1488 m.pointer_texcoord[0] = varray_texcoord2f[0];
1489 m.pointer_texcoord[1] = varray_texcoord2f[1];
1490 R_Mesh_State_Texture(&m);
1491 qglColorMask(0,0,0,1);
1492 GL_BlendFunc(GL_ONE, GL_ZERO);
1493 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1494 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1495 R_Mesh_Draw(numverts, numtriangles, elements);
1497 c_rt_lighttris += numtriangles;
1499 memset(&m, 0, sizeof(m));
1500 m.tex[0] = R_GetTexture(bumptexture);
1501 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1502 m.texcombinergb[0] = GL_REPLACE;
1503 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1504 m.pointer_texcoord[0] = texcoord2f;
1505 m.pointer_texcoord[1] = varray_texcoord3f[1];
1506 R_Mesh_State_Texture(&m);
1507 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1508 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1509 R_Mesh_Draw(numverts, numtriangles, elements);
1511 c_rt_lighttris += numtriangles;
1513 memset(&m, 0, sizeof(m));
1514 m.tex[0] = R_GetTexture(basetexture);
1515 m.pointer_texcoord[0] = texcoord2f;
1518 m.texcubemap[1] = R_GetTexture(lightcubemap);
1519 m.pointer_texcoord[1] = varray_texcoord3f[1];
1520 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1522 R_Mesh_State_Texture(&m);
1523 qglColorMask(1,1,1,0);
1524 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1525 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1526 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1528 color[0] = bound(0, color2[0], 1);
1529 color[1] = bound(0, color2[1], 1);
1530 color[2] = bound(0, color2[2], 1);
1531 GL_Color(color[0], color[1], color[2], 1);
1532 R_Mesh_Draw(numverts, numtriangles, elements);
1534 c_rt_lighttris += numtriangles;
1540 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1541 GL_DepthMask(false);
1543 GL_ColorPointer(varray_color4f);
1544 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1545 memset(&m, 0, sizeof(m));
1546 m.tex[0] = R_GetTexture(basetexture);
1547 m.pointer_texcoord[0] = texcoord2f;
1548 if (r_textureunits.integer >= 2)
1551 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1552 m.pointer_texcoord[1] = varray_texcoord2f[1];
1553 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1555 R_Mesh_State_Texture(&m);
1556 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1558 color[0] = bound(0, color2[0], 1);
1559 color[1] = bound(0, color2[1], 1);
1560 color[2] = bound(0, color2[2], 1);
1561 if (r_textureunits.integer >= 2)
1562 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1564 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1565 R_Mesh_Draw(numverts, numtriangles, elements);
1567 c_rt_lighttris += numtriangles;
1572 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_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1575 float color[3], color2[3], colorscale;
1577 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1580 glosstexture = r_shadow_blankglosstexture;
1581 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1583 colorscale = r_shadow_glossintensity.value;
1585 bumptexture = r_shadow_blankbumptexture;
1586 if (glosstexture == r_shadow_blankglosstexture)
1587 colorscale *= r_shadow_gloss2intensity.value;
1588 GL_VertexPointer(vertex3f);
1590 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1592 // 2/0/0/1/2 3D combine blendsquare path
1593 memset(&m, 0, sizeof(m));
1594 m.tex[0] = R_GetTexture(bumptexture);
1595 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1596 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1597 m.pointer_texcoord[0] = texcoord2f;
1598 m.pointer_texcoord[1] = varray_texcoord3f[1];
1599 R_Mesh_State_Texture(&m);
1600 qglColorMask(0,0,0,1);
1601 // this squares the result
1602 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1603 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1604 R_Mesh_Draw(numverts, numtriangles, elements);
1606 c_rt_lighttris += numtriangles;
1608 memset(&m, 0, sizeof(m));
1609 R_Mesh_State_Texture(&m);
1610 // square alpha in framebuffer a few times to make it shiny
1611 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1612 // these comments are a test run through this math for intensity 0.5
1613 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1614 // 0.25 * 0.25 = 0.0625 (this is another pass)
1615 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1616 R_Mesh_Draw(numverts, numtriangles, elements);
1618 c_rt_lighttris += numtriangles;
1619 R_Mesh_Draw(numverts, numtriangles, elements);
1621 c_rt_lighttris += numtriangles;
1623 memset(&m, 0, sizeof(m));
1624 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1625 m.pointer_texcoord[0] = varray_texcoord3f[0];
1626 R_Mesh_State_Texture(&m);
1627 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1628 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1629 R_Mesh_Draw(numverts, numtriangles, elements);
1631 c_rt_lighttris += numtriangles;
1633 memset(&m, 0, sizeof(m));
1634 m.tex[0] = R_GetTexture(glosstexture);
1637 m.texcubemap[1] = R_GetTexture(lightcubemap);
1638 m.pointer_texcoord[1] = varray_texcoord3f[1];
1639 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1641 m.pointer_texcoord[0] = texcoord2f;
1642 R_Mesh_State_Texture(&m);
1643 qglColorMask(1,1,1,0);
1644 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1645 VectorScale(lightcolor, colorscale, color2);
1646 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1648 color[0] = bound(0, color2[0], 1);
1649 color[1] = bound(0, color2[1], 1);
1650 color[2] = bound(0, color2[2], 1);
1651 GL_Color(color[0], color[1], color[2], 1);
1652 R_Mesh_Draw(numverts, numtriangles, elements);
1654 c_rt_lighttris += numtriangles;
1657 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1659 // 2/0/0/2 3D combine blendsquare path
1660 memset(&m, 0, sizeof(m));
1661 m.tex[0] = R_GetTexture(bumptexture);
1662 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1663 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1664 m.pointer_texcoord[0] = texcoord2f;
1665 m.pointer_texcoord[1] = varray_texcoord3f[1];
1666 R_Mesh_State_Texture(&m);
1667 qglColorMask(0,0,0,1);
1668 // this squares the result
1669 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1670 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1671 R_Mesh_Draw(numverts, numtriangles, elements);
1673 c_rt_lighttris += numtriangles;
1675 memset(&m, 0, sizeof(m));
1676 R_Mesh_State_Texture(&m);
1677 // square alpha in framebuffer a few times to make it shiny
1678 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1679 // these comments are a test run through this math for intensity 0.5
1680 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1681 // 0.25 * 0.25 = 0.0625 (this is another pass)
1682 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1683 R_Mesh_Draw(numverts, numtriangles, elements);
1685 c_rt_lighttris += numtriangles;
1686 R_Mesh_Draw(numverts, numtriangles, elements);
1688 c_rt_lighttris += numtriangles;
1690 memset(&m, 0, sizeof(m));
1691 m.tex[0] = R_GetTexture(glosstexture);
1692 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1693 m.pointer_texcoord[0] = texcoord2f;
1694 m.pointer_texcoord[1] = varray_texcoord3f[1];
1695 R_Mesh_State_Texture(&m);
1696 qglColorMask(1,1,1,0);
1697 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1698 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1699 VectorScale(lightcolor, colorscale, color2);
1700 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1702 color[0] = bound(0, color2[0], 1);
1703 color[1] = bound(0, color2[1], 1);
1704 color[2] = bound(0, color2[2], 1);
1705 GL_Color(color[0], color[1], color[2], 1);
1706 R_Mesh_Draw(numverts, numtriangles, elements);
1708 c_rt_lighttris += numtriangles;
1711 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1713 // 2/0/0/2/2 2D combine blendsquare path
1714 memset(&m, 0, sizeof(m));
1715 m.tex[0] = R_GetTexture(bumptexture);
1716 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1717 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1718 m.pointer_texcoord[0] = texcoord2f;
1719 m.pointer_texcoord[1] = varray_texcoord3f[1];
1720 R_Mesh_State_Texture(&m);
1721 qglColorMask(0,0,0,1);
1722 // this squares the result
1723 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1724 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1725 R_Mesh_Draw(numverts, numtriangles, elements);
1727 c_rt_lighttris += numtriangles;
1729 memset(&m, 0, sizeof(m));
1730 R_Mesh_State_Texture(&m);
1731 // square alpha in framebuffer a few times to make it shiny
1732 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1733 // these comments are a test run through this math for intensity 0.5
1734 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1735 // 0.25 * 0.25 = 0.0625 (this is another pass)
1736 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1737 R_Mesh_Draw(numverts, numtriangles, elements);
1739 c_rt_lighttris += numtriangles;
1740 R_Mesh_Draw(numverts, numtriangles, elements);
1742 c_rt_lighttris += numtriangles;
1744 memset(&m, 0, sizeof(m));
1745 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1746 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1747 m.pointer_texcoord[0] = varray_texcoord2f[0];
1748 m.pointer_texcoord[1] = varray_texcoord2f[1];
1749 R_Mesh_State_Texture(&m);
1750 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1751 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1752 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1753 R_Mesh_Draw(numverts, numtriangles, elements);
1755 c_rt_lighttris += numtriangles;
1757 memset(&m, 0, sizeof(m));
1758 m.tex[0] = R_GetTexture(glosstexture);
1761 m.texcubemap[1] = R_GetTexture(lightcubemap);
1762 m.pointer_texcoord[1] = varray_texcoord3f[1];
1763 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1765 m.pointer_texcoord[0] = texcoord2f;
1766 R_Mesh_State_Texture(&m);
1767 qglColorMask(1,1,1,0);
1768 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1769 VectorScale(lightcolor, colorscale, color2);
1770 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1772 color[0] = bound(0, color2[0], 1);
1773 color[1] = bound(0, color2[1], 1);
1774 color[2] = bound(0, color2[2], 1);
1775 GL_Color(color[0], color[1], color[2], 1);
1776 R_Mesh_Draw(numverts, numtriangles, elements);
1778 c_rt_lighttris += numtriangles;
1784 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1786 R_Mesh_Matrix(matrix);
1787 if (r_shadow_showtris.integer)
1791 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1792 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1793 qglDisable(GL_DEPTH_TEST);
1794 qglDisable(GL_STENCIL_TEST);
1795 //qglDisable(GL_CULL_FACE);
1796 qglColorMask(1,1,1,1);
1797 memset(&m, 0, sizeof(m));
1798 R_Mesh_State_Texture(&m);
1799 GL_Color(0,0.1,0,1);
1800 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1801 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1803 GL_VertexPointer(mesh->vertex3f);
1804 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1806 //qglEnable(GL_CULL_FACE);
1808 qglEnable(GL_DEPTH_TEST);
1811 qglEnable(GL_STENCIL_TEST);
1812 qglColorMask(0,0,0,0);
1815 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1818 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_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz)
1821 R_Mesh_Matrix(matrix);
1822 if (r_shadow_showtris.integer)
1825 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1826 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1827 qglDisable(GL_DEPTH_TEST);
1828 qglDisable(GL_STENCIL_TEST);
1829 //qglDisable(GL_CULL_FACE);
1830 memset(&m, 0, sizeof(m));
1831 R_Mesh_State_Texture(&m);
1832 GL_Color(0.2,0,0,1);
1833 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1834 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1836 GL_VertexPointer(mesh->vertex3f);
1837 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1839 //qglEnable(GL_CULL_FACE);
1841 qglEnable(GL_DEPTH_TEST);
1843 qglEnable(GL_STENCIL_TEST);
1845 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1847 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightradius, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, light->cubemap);
1848 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightradius, lightcolor, matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, light->cubemap);
1852 cvar_t r_editlights = {0, "r_editlights", "0"};
1853 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1854 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1855 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1856 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1857 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1858 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1859 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1860 worldlight_t *r_shadow_worldlightchain;
1861 worldlight_t *r_shadow_selectedlight;
1862 vec3_t r_editlights_cursorlocation;
1864 static int lightpvsbytes;
1865 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1866 static qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1868 typedef struct cubemapinfo_s
1871 rtexture_t *texture;
1875 #define MAX_CUBEMAPS 128
1876 static int numcubemaps;
1877 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1879 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1880 typedef struct suffixinfo_s
1883 int flipx, flipy, flipdiagonal;
1886 static suffixinfo_t suffix[3][6] =
1889 {"posx", false, false, false},
1890 {"negx", false, false, false},
1891 {"posy", false, false, false},
1892 {"negy", false, false, false},
1893 {"posz", false, false, false},
1894 {"negz", false, false, false}
1897 {"px", false, false, false},
1898 {"nx", false, false, false},
1899 {"py", false, false, false},
1900 {"ny", false, false, false},
1901 {"pz", false, false, false},
1902 {"nz", false, false, false}
1905 {"ft", true, false, true},
1906 {"bk", false, true, true},
1907 {"lf", true, true, false},
1908 {"rt", false, false, false},
1909 {"up", false, false, false},
1910 {"dn", false, false, false}
1914 static int componentorder[4] = {0, 1, 2, 3};
1916 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1918 int i, j, cubemapsize;
1919 qbyte *cubemappixels, *image_rgba;
1920 rtexture_t *cubemaptexture;
1922 // must start 0 so the first loadimagepixels has no requested width/height
1924 cubemappixels = NULL;
1925 cubemaptexture = NULL;
1926 for (j = 0;j < 3 && !cubemappixels;j++)
1928 for (i = 0;i < 6;i++)
1930 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1931 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
1933 if (image_width == image_height)
1935 if (!cubemappixels && image_width >= 1)
1937 cubemapsize = image_width;
1938 // note this clears to black, so unavailable sizes are black
1939 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1942 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);
1945 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1946 Mem_Free(image_rgba);
1952 if (!r_shadow_filters_texturepool)
1953 r_shadow_filters_texturepool = R_AllocTexturePool();
1954 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1955 Mem_Free(cubemappixels);
1959 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1960 for (j = 0;j < 3;j++)
1961 for (i = 0;i < 6;i++)
1962 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1963 Con_Printf(" and was unable to find any of them.\n");
1965 return cubemaptexture;
1968 rtexture_t *R_Shadow_Cubemap(const char *basename)
1971 for (i = 0;i < numcubemaps;i++)
1972 if (!strcasecmp(cubemaps[i].basename, basename))
1973 return cubemaps[i].texture;
1974 if (i >= MAX_CUBEMAPS)
1977 strcpy(cubemaps[i].basename, basename);
1978 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1979 return cubemaps[i].texture;
1982 void R_Shadow_FreeCubemaps(void)
1985 R_FreeTexturePool(&r_shadow_filters_texturepool);
1988 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
1990 int i, j, k, l, maxverts = 256, tris;
1991 float *vertex3f = NULL, mins[3], maxs[3];
1993 shadowmesh_t *mesh, *castmesh = NULL;
1995 if (radius < 15 || DotProduct(color, color) < 0.03)
1997 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2001 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
2002 VectorCopy(origin, e->origin);
2003 VectorCopy(angles, e->angles);
2004 VectorCopy(color, e->color);
2007 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
2009 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
2012 e->drawshadows = shadowenable;
2015 Matrix4x4_CreateFromQuakeEntity(&e->matrix_lighttoworld, e->origin[0], e->origin[1], e->origin[2], e->angles[0], e->angles[1], e->angles[2], e->radius);
2016 Matrix4x4_Invert_Simple(&e->matrix_worldtolight, &e->matrix_lighttoworld);
2017 Matrix4x4_Concat(&e->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &e->matrix_worldtolight);
2018 Matrix4x4_Concat(&e->matrix_worldtoattenuationz, &matrix_attenuationz, &e->matrix_worldtolight);
2020 e->cullradius = e->radius;
2021 for (k = 0;k < 3;k++)
2023 mins[k] = e->origin[k] - e->radius;
2024 maxs[k] = e->origin[k] + e->radius;
2027 e->next = r_shadow_worldlightchain;
2028 r_shadow_worldlightchain = e;
2029 if (cubemapname && cubemapname[0])
2031 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2032 strcpy(e->cubemapname, cubemapname);
2033 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2035 // FIXME: rewrite this to store ALL geometry into a cache in the light
2037 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2038 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2041 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightfullpvs, sizeof(lightfullpvs));
2042 memset(lightpvs, 0, lightpvsbytes);
2043 if (cl.worldmodel->brushq3.num_leafs)
2048 // make a pvs that only includes things within the box
2049 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2050 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2051 SETPVSBIT(lightpvs, leaf->clusterindex);
2053 // make a cluster list for fast visibility checking during rendering
2054 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2055 if (CHECKPVSBIT(lightpvs, i))
2057 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2058 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2059 if (CHECKPVSBIT(lightpvs, i))
2060 e->clusterindices[e->numclusters++] = i;
2062 VectorCopy(e->origin, e->mins);
2063 VectorCopy(e->origin, e->maxs);
2064 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2065 face->lighttemp_castshadow = false;
2066 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2068 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2070 for (k = 0;k < 3;k++)
2072 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2073 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2075 for (j = 0;j < leaf->numleaffaces;j++)
2077 face = leaf->firstleafface[j];
2078 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2079 face->lighttemp_castshadow = true;
2084 // add surfaces to shadow casting mesh and light mesh
2085 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2087 if (face->lighttemp_castshadow)
2089 face->lighttemp_castshadow = false;
2090 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2093 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2094 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2095 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2096 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);
2101 else if (cl.worldmodel->brushq1.num_leafs)
2105 VectorCopy(e->origin, e->mins);
2106 VectorCopy(e->origin, e->maxs);
2107 i = CL_PointQ1Contents(e->origin);
2109 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2110 surf->lighttemp_castshadow = false;
2112 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2115 qbyte *bytesurfacepvs;
2117 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
2118 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2120 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2122 // make a pvs that only includes things within the box
2123 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2125 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2127 SETPVSBIT(lightpvs, leaf->clusterindex);
2128 for (k = 0;k < 3;k++)
2130 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2131 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2136 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2137 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2138 surf->lighttemp_castshadow = true;
2140 Mem_Free(byteleafpvs);
2141 Mem_Free(bytesurfacepvs);
2143 // make a cluster list for fast visibility checking during rendering
2144 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2145 if (CHECKPVSBIT(lightpvs, i))
2147 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2148 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2149 if (CHECKPVSBIT(lightpvs, i))
2150 e->clusterindices[e->numclusters++] = i;
2154 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2156 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2158 // make a pvs that only includes things within the box
2159 SETPVSBIT(lightpvs, leaf->clusterindex);
2160 for (k = 0;k < 3;k++)
2162 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2163 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2165 for (j = 0;j < leaf->nummarksurfaces;j++)
2167 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2168 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2169 surf->lighttemp_castshadow = true;
2174 // make a pvs that only includes things within the box
2175 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2176 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2177 SETPVSBIT(lightpvs, leaf->clusterindex);
2179 // make a cluster list for fast visibility checking during rendering
2180 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2181 if (CHECKPVSBIT(lightpvs, i))
2183 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2184 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2185 if (CHECKPVSBIT(lightpvs, i))
2186 e->clusterindices[e->numclusters++] = i;
2189 // add surfaces to shadow casting mesh and light mesh
2190 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2192 if (surf->lighttemp_castshadow)
2194 surf->lighttemp_castshadow = false;
2195 if (e->drawshadows && (surf->flags & SURF_SHADOWCAST))
2196 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);
2197 if (!(surf->flags & SURF_DRAWSKY))
2198 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);
2204 // limit box to light bounds (in case it grew larger)
2205 for (k = 0;k < 3;k++)
2207 if (e->mins[k] < e->origin[k] - e->radius) e->mins[k] = e->origin[k] - e->radius;
2208 if (e->maxs[k] > e->origin[k] + e->radius) e->maxs[k] = e->origin[k] + e->radius;
2210 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2212 // cast shadow volume from castmesh
2213 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2217 for (mesh = castmesh;mesh;mesh = mesh->next)
2219 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2220 maxverts = max(maxverts, mesh->numverts * 2);
2225 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2226 // now that we have the buffers big enough, construct and add
2227 // the shadow volume mesh
2229 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2230 for (mesh = castmesh;mesh;mesh = mesh->next)
2232 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2233 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)))
2234 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2239 // we're done with castmesh now
2240 Mod_ShadowMesh_Free(castmesh);
2243 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2244 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2247 if (e->meshchain_shadow)
2248 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2249 k += mesh->numtriangles;
2251 if (e->meshchain_light)
2252 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2253 l += mesh->numtriangles;
2254 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);
2257 void R_Shadow_FreeWorldLight(worldlight_t *light)
2259 worldlight_t **lightpointer;
2260 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2261 if (*lightpointer != light)
2262 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2263 *lightpointer = light->next;
2264 if (light->cubemapname)
2265 Mem_Free(light->cubemapname);
2266 if (light->meshchain_shadow)
2267 Mod_ShadowMesh_Free(light->meshchain_shadow);
2268 if (light->meshchain_light)
2269 Mod_ShadowMesh_Free(light->meshchain_light);
2273 void R_Shadow_ClearWorldLights(void)
2275 while (r_shadow_worldlightchain)
2276 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2277 r_shadow_selectedlight = NULL;
2278 R_Shadow_FreeCubemaps();
2281 void R_Shadow_SelectLight(worldlight_t *light)
2283 if (r_shadow_selectedlight)
2284 r_shadow_selectedlight->selected = false;
2285 r_shadow_selectedlight = light;
2286 if (r_shadow_selectedlight)
2287 r_shadow_selectedlight->selected = true;
2290 rtexture_t *lighttextures[5];
2292 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2294 float scale = r_editlights_cursorgrid.value * 0.5f;
2295 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);
2298 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2301 const worldlight_t *light;
2304 if (light->selected)
2305 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2306 if (!light->meshchain_shadow)
2308 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);
2311 void R_Shadow_DrawLightSprites(void)
2315 worldlight_t *light;
2317 for (i = 0;i < 5;i++)
2319 lighttextures[i] = NULL;
2320 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2321 lighttextures[i] = pic->tex;
2324 for (light = r_shadow_worldlightchain;light;light = light->next)
2325 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2326 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2329 void R_Shadow_SelectLightInView(void)
2331 float bestrating, rating, temp[3];
2332 worldlight_t *best, *light;
2335 for (light = r_shadow_worldlightchain;light;light = light->next)
2337 VectorSubtract(light->origin, r_vieworigin, temp);
2338 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2341 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2342 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2344 bestrating = rating;
2349 R_Shadow_SelectLight(best);
2352 void R_Shadow_LoadWorldLights(void)
2354 int n, a, style, shadow;
2355 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2356 float origin[3], radius, color[3], angles[3], corona;
2357 if (cl.worldmodel == NULL)
2359 Con_Printf("No map loaded.\n");
2362 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2363 strlcat (name, ".rtlights", sizeof (name));
2364 lightsstring = FS_LoadFile(name, false);
2372 while (*s && *s != '\n')
2378 // check for modifier flags
2384 a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2388 VectorClear(angles);
2395 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);
2398 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2399 radius *= r_editlights_rtlightssizescale.value;
2400 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2405 Con_Printf("invalid rtlights file \"%s\"\n", name);
2406 Mem_Free(lightsstring);
2410 void R_Shadow_SaveWorldLights(void)
2412 worldlight_t *light;
2413 int bufchars, bufmaxchars;
2415 char name[MAX_QPATH];
2417 if (!r_shadow_worldlightchain)
2419 if (cl.worldmodel == NULL)
2421 Con_Printf("No map loaded.\n");
2424 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2425 strlcat (name, ".rtlights", sizeof (name));
2426 bufchars = bufmaxchars = 0;
2428 for (light = r_shadow_worldlightchain;light;light = light->next)
2430 sprintf(line, "%s%f %f %f %f %f %f %f %d %s\n", light->drawshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2431 if (bufchars + (int) strlen(line) > bufmaxchars)
2433 bufmaxchars = bufchars + strlen(line) + 2048;
2435 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2439 memcpy(buf, oldbuf, bufchars);
2445 memcpy(buf + bufchars, line, strlen(line));
2446 bufchars += strlen(line);
2450 FS_WriteFile(name, buf, bufchars);
2455 void R_Shadow_LoadLightsFile(void)
2458 char name[MAX_QPATH], *lightsstring, *s, *t;
2459 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2460 if (cl.worldmodel == NULL)
2462 Con_Printf("No map loaded.\n");
2465 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2466 strlcat (name, ".lights", sizeof (name));
2467 lightsstring = FS_LoadFile(name, false);
2475 while (*s && *s != '\n')
2480 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);
2484 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);
2487 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2488 radius = bound(15, radius, 4096);
2489 VectorScale(color, (2.0f / (8388608.0f)), color);
2490 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2495 Con_Printf("invalid lights file \"%s\"\n", name);
2496 Mem_Free(lightsstring);
2500 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2502 int entnum, style, islight;
2503 char key[256], value[1024];
2504 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2507 if (cl.worldmodel == NULL)
2509 Con_Printf("No map loaded.\n");
2512 data = cl.worldmodel->brush.entities;
2515 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2518 origin[0] = origin[1] = origin[2] = 0;
2519 originhack[0] = originhack[1] = originhack[2] = 0;
2520 color[0] = color[1] = color[2] = 1;
2521 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2528 if (!COM_ParseToken(&data, false))
2530 if (com_token[0] == '}')
2531 break; // end of entity
2532 if (com_token[0] == '_')
2533 strcpy(key, com_token + 1);
2535 strcpy(key, com_token);
2536 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2537 key[strlen(key)-1] = 0;
2538 if (!COM_ParseToken(&data, false))
2540 strcpy(value, com_token);
2542 // now that we have the key pair worked out...
2543 if (!strcmp("light", key))
2544 light = atof(value);
2545 else if (!strcmp("origin", key))
2546 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2547 else if (!strcmp("color", key))
2548 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2549 else if (!strcmp("wait", key))
2550 fadescale = atof(value);
2551 else if (!strcmp("classname", key))
2553 if (!strncmp(value, "light", 5))
2556 if (!strcmp(value, "light_fluoro"))
2561 overridecolor[0] = 1;
2562 overridecolor[1] = 1;
2563 overridecolor[2] = 1;
2565 if (!strcmp(value, "light_fluorospark"))
2570 overridecolor[0] = 1;
2571 overridecolor[1] = 1;
2572 overridecolor[2] = 1;
2574 if (!strcmp(value, "light_globe"))
2579 overridecolor[0] = 1;
2580 overridecolor[1] = 0.8;
2581 overridecolor[2] = 0.4;
2583 if (!strcmp(value, "light_flame_large_yellow"))
2588 overridecolor[0] = 1;
2589 overridecolor[1] = 0.5;
2590 overridecolor[2] = 0.1;
2592 if (!strcmp(value, "light_flame_small_yellow"))
2597 overridecolor[0] = 1;
2598 overridecolor[1] = 0.5;
2599 overridecolor[2] = 0.1;
2601 if (!strcmp(value, "light_torch_small_white"))
2606 overridecolor[0] = 1;
2607 overridecolor[1] = 0.5;
2608 overridecolor[2] = 0.1;
2610 if (!strcmp(value, "light_torch_small_walltorch"))
2615 overridecolor[0] = 1;
2616 overridecolor[1] = 0.5;
2617 overridecolor[2] = 0.1;
2621 else if (!strcmp("style", key))
2622 style = atoi(value);
2623 else if (cl.worldmodel->type == mod_brushq3)
2625 if (!strcmp("scale", key))
2626 lightscale = atof(value);
2627 if (!strcmp("fade", key))
2628 fadescale = atof(value);
2631 if (light <= 0 && islight)
2633 if (lightscale <= 0)
2637 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2638 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2639 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2640 VectorCopy(overridecolor, color);
2641 VectorScale(color, light, color);
2642 VectorAdd(origin, originhack, origin);
2644 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2649 void R_Shadow_SetCursorLocationForView(void)
2651 vec_t dist, push, frac;
2652 vec3_t dest, endpos, normal;
2653 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2654 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2657 dist = frac * r_editlights_cursordistance.value;
2658 push = r_editlights_cursorpushback.value;
2662 VectorMA(endpos, push, r_viewforward, endpos);
2663 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2665 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2666 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2667 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2670 void R_Shadow_UpdateWorldLightSelection(void)
2672 if (r_editlights.integer)
2674 R_Shadow_SetCursorLocationForView();
2675 R_Shadow_SelectLightInView();
2676 R_Shadow_DrawLightSprites();
2679 R_Shadow_SelectLight(NULL);
2682 void R_Shadow_EditLights_Clear_f(void)
2684 R_Shadow_ClearWorldLights();
2687 void R_Shadow_EditLights_Reload_f(void)
2689 r_shadow_reloadlights = true;
2692 void R_Shadow_EditLights_Save_f(void)
2695 R_Shadow_SaveWorldLights();
2698 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2700 R_Shadow_ClearWorldLights();
2701 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2704 void R_Shadow_EditLights_ImportLightsFile_f(void)
2706 R_Shadow_ClearWorldLights();
2707 R_Shadow_LoadLightsFile();
2710 void R_Shadow_EditLights_Spawn_f(void)
2713 if (!r_editlights.integer)
2715 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2718 if (Cmd_Argc() != 1)
2720 Con_Printf("r_editlights_spawn does not take parameters\n");
2723 color[0] = color[1] = color[2] = 1;
2724 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2727 void R_Shadow_EditLights_Edit_f(void)
2729 vec3_t origin, angles, color;
2730 vec_t radius, corona;
2732 char cubemapname[1024];
2733 if (!r_editlights.integer)
2735 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2738 if (!r_shadow_selectedlight)
2740 Con_Printf("No selected light.\n");
2743 VectorCopy(r_shadow_selectedlight->origin, origin);
2744 VectorCopy(r_shadow_selectedlight->angles, angles);
2745 VectorCopy(r_shadow_selectedlight->color, color);
2746 radius = r_shadow_selectedlight->radius;
2747 style = r_shadow_selectedlight->style;
2748 if (r_shadow_selectedlight->cubemapname)
2749 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2752 shadows = r_shadow_selectedlight->drawshadows;
2753 corona = r_shadow_selectedlight->corona;
2754 if (!strcmp(Cmd_Argv(1), "origin"))
2756 if (Cmd_Argc() != 5)
2758 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2761 origin[0] = atof(Cmd_Argv(2));
2762 origin[1] = atof(Cmd_Argv(3));
2763 origin[2] = atof(Cmd_Argv(4));
2765 else if (!strcmp(Cmd_Argv(1), "originx"))
2767 if (Cmd_Argc() != 3)
2769 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2772 origin[0] = atof(Cmd_Argv(2));
2774 else if (!strcmp(Cmd_Argv(1), "originy"))
2776 if (Cmd_Argc() != 3)
2778 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2781 origin[1] = atof(Cmd_Argv(2));
2783 else if (!strcmp(Cmd_Argv(1), "originz"))
2785 if (Cmd_Argc() != 3)
2787 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2790 origin[2] = atof(Cmd_Argv(2));
2792 else if (!strcmp(Cmd_Argv(1), "move"))
2794 if (Cmd_Argc() != 5)
2796 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2799 origin[0] += atof(Cmd_Argv(2));
2800 origin[1] += atof(Cmd_Argv(3));
2801 origin[2] += atof(Cmd_Argv(4));
2803 else if (!strcmp(Cmd_Argv(1), "movex"))
2805 if (Cmd_Argc() != 3)
2807 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2810 origin[0] += atof(Cmd_Argv(2));
2812 else if (!strcmp(Cmd_Argv(1), "movey"))
2814 if (Cmd_Argc() != 3)
2816 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2819 origin[1] += atof(Cmd_Argv(2));
2821 else if (!strcmp(Cmd_Argv(1), "movez"))
2823 if (Cmd_Argc() != 3)
2825 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2828 origin[2] += atof(Cmd_Argv(2));
2830 if (!strcmp(Cmd_Argv(1), "angles"))
2832 if (Cmd_Argc() != 5)
2834 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2837 angles[0] = atof(Cmd_Argv(2));
2838 angles[1] = atof(Cmd_Argv(3));
2839 angles[2] = atof(Cmd_Argv(4));
2841 else if (!strcmp(Cmd_Argv(1), "anglesx"))
2843 if (Cmd_Argc() != 3)
2845 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2848 angles[0] = atof(Cmd_Argv(2));
2850 else if (!strcmp(Cmd_Argv(1), "anglesy"))
2852 if (Cmd_Argc() != 3)
2854 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2857 angles[1] = atof(Cmd_Argv(2));
2859 else if (!strcmp(Cmd_Argv(1), "anglesz"))
2861 if (Cmd_Argc() != 3)
2863 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2866 angles[2] = atof(Cmd_Argv(2));
2868 else if (!strcmp(Cmd_Argv(1), "color"))
2870 if (Cmd_Argc() != 5)
2872 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
2875 color[0] = atof(Cmd_Argv(2));
2876 color[1] = atof(Cmd_Argv(3));
2877 color[2] = atof(Cmd_Argv(4));
2879 else if (!strcmp(Cmd_Argv(1), "radius"))
2881 if (Cmd_Argc() != 3)
2883 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2886 radius = atof(Cmd_Argv(2));
2888 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2890 if (Cmd_Argc() != 3)
2892 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2895 style = atoi(Cmd_Argv(2));
2897 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2901 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2904 if (Cmd_Argc() == 3)
2905 strcpy(cubemapname, Cmd_Argv(2));
2909 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2911 if (Cmd_Argc() != 3)
2913 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2916 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2918 else if (!strcmp(Cmd_Argv(1), "corona"))
2920 if (Cmd_Argc() != 3)
2922 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2925 corona = atof(Cmd_Argv(2));
2929 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2930 Con_Printf("Selected light's properties:\n");
2931 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2932 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
2933 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
2934 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
2935 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
2936 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
2937 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->drawshadows ? "yes" : "no");
2938 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2941 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2942 r_shadow_selectedlight = NULL;
2943 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
2946 extern int con_vislines;
2947 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2951 if (r_shadow_selectedlight == NULL)
2955 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2956 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;
2957 sprintf(temp, "Angles %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2958 sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2959 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2960 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2961 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2962 sprintf(temp, "Shadows %s", r_shadow_selectedlight->drawshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2963 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2966 void R_Shadow_EditLights_ToggleShadow_f(void)
2968 if (!r_editlights.integer)
2970 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2973 if (!r_shadow_selectedlight)
2975 Con_Printf("No selected light.\n");
2978 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->drawshadows, r_shadow_selectedlight->cubemapname);
2979 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2980 r_shadow_selectedlight = NULL;
2983 void R_Shadow_EditLights_ToggleCorona_f(void)
2985 if (!r_editlights.integer)
2987 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2990 if (!r_shadow_selectedlight)
2992 Con_Printf("No selected light.\n");
2995 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->drawshadows, r_shadow_selectedlight->cubemapname);
2996 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2997 r_shadow_selectedlight = NULL;
3000 void R_Shadow_EditLights_Remove_f(void)
3002 if (!r_editlights.integer)
3004 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3007 if (!r_shadow_selectedlight)
3009 Con_Printf("No selected light.\n");
3012 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3013 r_shadow_selectedlight = NULL;
3016 void R_Shadow_EditLights_Help_f(void)
3019 "Documentation on r_editlights system:\n"
3021 "r_editlights : enable/disable editing mode\n"
3022 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3023 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3024 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3025 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3026 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3027 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3028 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3030 "r_editlights_help : this help\n"
3031 "r_editlights_clear : remove all lights\n"
3032 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3033 "r_editlights_save : save to .rtlights file\n"
3034 "r_editlights_spawn : create a light with default settings\n"
3035 "r_editlights_edit command : edit selected light - more documentation below\n"
3036 "r_editlights_remove : remove selected light\n"
3037 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3038 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3039 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3041 "origin x y z : set light location\n"
3042 "originx x: set x component of light location\n"
3043 "originy y: set y component of light location\n"
3044 "originz z: set z component of light location\n"
3045 "move x y z : adjust light location\n"
3046 "movex x: adjust x component of light location\n"
3047 "movey y: adjust y component of light location\n"
3048 "movez z: adjust z component of light location\n"
3049 "angles x y z : set light angles\n"
3050 "anglesx x: set x component of light angles\n"
3051 "anglesy y: set y component of light angles\n"
3052 "anglesz z: set z component of light angles\n"
3053 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3054 "radius radius : set radius (size) of light\n"
3055 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3056 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3057 "shadows 1/0 : turn on/off shadows\n"
3058 "corona n : set corona intensity\n"
3059 "<nothing> : print light properties to console\n"
3063 void R_Shadow_EditLights_Init(void)
3065 Cvar_RegisterVariable(&r_editlights);
3066 Cvar_RegisterVariable(&r_editlights_cursordistance);
3067 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3068 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3069 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3070 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3071 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3072 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3073 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3074 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3075 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3076 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3077 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3078 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3079 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3080 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3081 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3082 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3083 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);