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 qglDisable(GL_SCISSOR_TEST);
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 qglDisable(GL_SCISSOR_TEST);
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 qglDisable(GL_SCISSOR_TEST);
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 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1157 qglEnable(GL_SCISSOR_TEST);
1162 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1164 float *color4f = varray_color4f;
1165 float dist, dot, intensity, v[3], n[3];
1166 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1168 Matrix4x4_Transform(m, vertex3f, v);
1169 if ((dist = DotProduct(v, v)) < 1)
1171 Matrix4x4_Transform3x3(m, normal3f, n);
1172 if ((dot = DotProduct(n, v)) > 0)
1175 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1176 VectorScale(lightcolor, intensity, color4f);
1181 VectorClear(color4f);
1187 VectorClear(color4f);
1193 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1195 float *color4f = varray_color4f;
1196 float dist, dot, intensity, v[3], n[3];
1197 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1199 Matrix4x4_Transform(m, vertex3f, v);
1200 if ((dist = fabs(v[2])) < 1)
1202 Matrix4x4_Transform3x3(m, normal3f, n);
1203 if ((dot = DotProduct(n, v)) > 0)
1205 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1206 VectorScale(lightcolor, intensity, color4f);
1211 VectorClear(color4f);
1217 VectorClear(color4f);
1223 // FIXME: this should be done in a vertex program when possible
1224 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1225 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1229 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1230 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1231 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1238 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1242 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1243 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1250 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)
1254 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1256 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1257 // the cubemap normalizes this for us
1258 out3f[0] = DotProduct(svector3f, lightdir);
1259 out3f[1] = DotProduct(tvector3f, lightdir);
1260 out3f[2] = DotProduct(normal3f, lightdir);
1264 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)
1267 float lightdir[3], eyedir[3], halfdir[3];
1268 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1270 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1271 VectorNormalizeFast(lightdir);
1272 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1273 VectorNormalizeFast(eyedir);
1274 VectorAdd(lightdir, eyedir, halfdir);
1275 // the cubemap normalizes this for us
1276 out3f[0] = DotProduct(svector3f, halfdir);
1277 out3f[1] = DotProduct(tvector3f, halfdir);
1278 out3f[2] = DotProduct(normal3f, halfdir);
1282 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)
1285 float color[3], color2[3];
1287 GL_VertexPointer(vertex3f);
1288 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1291 bumptexture = r_shadow_blankbumptexture;
1293 // colorscale accounts for how much we multiply the brightness during combine
1294 // mult is how many times the final pass of the lighting will be
1295 // performed to get more brightness than otherwise possible
1296 // limit mult to 64 for sanity sake
1297 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1299 // 3/2 3D combine path (Geforce3, Radeon 8500)
1300 memset(&m, 0, sizeof(m));
1301 m.tex[0] = R_GetTexture(bumptexture);
1302 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1303 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1304 m.texcombinergb[0] = GL_REPLACE;
1305 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1306 m.pointer_texcoord[0] = texcoord2f;
1307 m.pointer_texcoord[1] = varray_texcoord3f[1];
1308 m.pointer_texcoord[2] = varray_texcoord3f[2];
1309 R_Mesh_State_Texture(&m);
1310 qglColorMask(0,0,0,1);
1311 GL_BlendFunc(GL_ONE, GL_ZERO);
1312 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1313 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1314 R_Mesh_Draw(numverts, numtriangles, elements);
1316 c_rt_lighttris += numtriangles;
1318 memset(&m, 0, sizeof(m));
1319 m.tex[0] = R_GetTexture(basetexture);
1320 m.pointer_texcoord[0] = texcoord2f;
1323 m.texcubemap[1] = R_GetTexture(lightcubemap);
1324 m.pointer_texcoord[1] = varray_texcoord3f[1];
1325 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1327 R_Mesh_State_Texture(&m);
1328 qglColorMask(1,1,1,0);
1329 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1330 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1331 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1333 color[0] = bound(0, color2[0], 1);
1334 color[1] = bound(0, color2[1], 1);
1335 color[2] = bound(0, color2[2], 1);
1336 GL_Color(color[0], color[1], color[2], 1);
1337 R_Mesh_Draw(numverts, numtriangles, elements);
1339 c_rt_lighttris += numtriangles;
1342 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1344 // 1/2/2 3D combine path (original Radeon)
1345 memset(&m, 0, sizeof(m));
1346 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1347 m.pointer_texcoord[0] = varray_texcoord3f[0];
1348 R_Mesh_State_Texture(&m);
1349 qglColorMask(0,0,0,1);
1350 GL_BlendFunc(GL_ONE, GL_ZERO);
1351 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1352 R_Mesh_Draw(numverts, numtriangles, elements);
1354 c_rt_lighttris += numtriangles;
1356 memset(&m, 0, sizeof(m));
1357 m.tex[0] = R_GetTexture(bumptexture);
1358 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1359 m.texcombinergb[0] = GL_REPLACE;
1360 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1361 m.pointer_texcoord[0] = texcoord2f;
1362 m.pointer_texcoord[1] = varray_texcoord3f[1];
1363 R_Mesh_State_Texture(&m);
1364 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1365 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1366 R_Mesh_Draw(numverts, numtriangles, elements);
1368 c_rt_lighttris += numtriangles;
1370 memset(&m, 0, sizeof(m));
1371 m.tex[0] = R_GetTexture(basetexture);
1372 m.pointer_texcoord[0] = texcoord2f;
1375 m.texcubemap[1] = R_GetTexture(lightcubemap);
1376 m.pointer_texcoord[1] = varray_texcoord3f[1];
1377 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1379 R_Mesh_State_Texture(&m);
1380 qglColorMask(1,1,1,0);
1381 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1382 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1383 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1385 color[0] = bound(0, color2[0], 1);
1386 color[1] = bound(0, color2[1], 1);
1387 color[2] = bound(0, color2[2], 1);
1388 GL_Color(color[0], color[1], color[2], 1);
1389 R_Mesh_Draw(numverts, numtriangles, elements);
1391 c_rt_lighttris += numtriangles;
1394 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1396 // 2/2 3D combine path (original Radeon)
1397 memset(&m, 0, sizeof(m));
1398 m.tex[0] = R_GetTexture(bumptexture);
1399 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1400 m.texcombinergb[0] = GL_REPLACE;
1401 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1402 m.pointer_texcoord[0] = texcoord2f;
1403 m.pointer_texcoord[1] = varray_texcoord3f[1];
1404 R_Mesh_State_Texture(&m);
1405 qglColorMask(0,0,0,1);
1406 GL_BlendFunc(GL_ONE, GL_ZERO);
1407 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1408 R_Mesh_Draw(numverts, numtriangles, elements);
1410 c_rt_lighttris += numtriangles;
1412 memset(&m, 0, sizeof(m));
1413 m.tex[0] = R_GetTexture(basetexture);
1414 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1415 m.pointer_texcoord[0] = texcoord2f;
1416 m.pointer_texcoord[1] = varray_texcoord3f[1];
1417 R_Mesh_State_Texture(&m);
1418 qglColorMask(1,1,1,0);
1419 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1420 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1421 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1422 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1424 color[0] = bound(0, color2[0], 1);
1425 color[1] = bound(0, color2[1], 1);
1426 color[2] = bound(0, color2[2], 1);
1427 GL_Color(color[0], color[1], color[2], 1);
1428 R_Mesh_Draw(numverts, numtriangles, elements);
1430 c_rt_lighttris += numtriangles;
1433 else if (r_textureunits.integer >= 4)
1435 // 4/2 2D combine path (Geforce3, Radeon 8500)
1436 memset(&m, 0, sizeof(m));
1437 m.tex[0] = R_GetTexture(bumptexture);
1438 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1439 m.texcombinergb[0] = GL_REPLACE;
1440 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1441 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1442 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1443 m.pointer_texcoord[0] = texcoord2f;
1444 m.pointer_texcoord[1] = varray_texcoord3f[1];
1445 m.pointer_texcoord[2] = varray_texcoord2f[2];
1446 m.pointer_texcoord[3] = varray_texcoord2f[3];
1447 R_Mesh_State_Texture(&m);
1448 qglColorMask(0,0,0,1);
1449 GL_BlendFunc(GL_ONE, GL_ZERO);
1450 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1451 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1452 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1453 R_Mesh_Draw(numverts, numtriangles, elements);
1455 c_rt_lighttris += numtriangles;
1457 memset(&m, 0, sizeof(m));
1458 m.tex[0] = R_GetTexture(basetexture);
1459 m.pointer_texcoord[0] = texcoord2f;
1462 m.texcubemap[1] = R_GetTexture(lightcubemap);
1463 m.pointer_texcoord[1] = varray_texcoord3f[1];
1464 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1466 R_Mesh_State_Texture(&m);
1467 qglColorMask(1,1,1,0);
1468 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1469 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1470 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1472 color[0] = bound(0, color2[0], 1);
1473 color[1] = bound(0, color2[1], 1);
1474 color[2] = bound(0, color2[2], 1);
1475 GL_Color(color[0], color[1], color[2], 1);
1476 R_Mesh_Draw(numverts, numtriangles, elements);
1478 c_rt_lighttris += numtriangles;
1483 // 2/2/2 2D combine path (any dot3 card)
1484 memset(&m, 0, sizeof(m));
1485 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1486 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1487 m.pointer_texcoord[0] = varray_texcoord2f[0];
1488 m.pointer_texcoord[1] = varray_texcoord2f[1];
1489 R_Mesh_State_Texture(&m);
1490 qglColorMask(0,0,0,1);
1491 GL_BlendFunc(GL_ONE, GL_ZERO);
1492 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1493 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1494 R_Mesh_Draw(numverts, numtriangles, elements);
1496 c_rt_lighttris += numtriangles;
1498 memset(&m, 0, sizeof(m));
1499 m.tex[0] = R_GetTexture(bumptexture);
1500 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1501 m.texcombinergb[0] = GL_REPLACE;
1502 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1503 m.pointer_texcoord[0] = texcoord2f;
1504 m.pointer_texcoord[1] = varray_texcoord3f[1];
1505 R_Mesh_State_Texture(&m);
1506 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1507 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1508 R_Mesh_Draw(numverts, numtriangles, elements);
1510 c_rt_lighttris += numtriangles;
1512 memset(&m, 0, sizeof(m));
1513 m.tex[0] = R_GetTexture(basetexture);
1514 m.pointer_texcoord[0] = texcoord2f;
1517 m.texcubemap[1] = R_GetTexture(lightcubemap);
1518 m.pointer_texcoord[1] = varray_texcoord3f[1];
1519 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1521 R_Mesh_State_Texture(&m);
1522 qglColorMask(1,1,1,0);
1523 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1524 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1525 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1527 color[0] = bound(0, color2[0], 1);
1528 color[1] = bound(0, color2[1], 1);
1529 color[2] = bound(0, color2[2], 1);
1530 GL_Color(color[0], color[1], color[2], 1);
1531 R_Mesh_Draw(numverts, numtriangles, elements);
1533 c_rt_lighttris += numtriangles;
1539 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1540 GL_DepthMask(false);
1542 GL_ColorPointer(varray_color4f);
1543 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1544 memset(&m, 0, sizeof(m));
1545 m.tex[0] = R_GetTexture(basetexture);
1546 m.pointer_texcoord[0] = texcoord2f;
1547 if (r_textureunits.integer >= 2)
1550 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1551 m.pointer_texcoord[1] = varray_texcoord2f[1];
1552 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1554 R_Mesh_State_Texture(&m);
1555 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1557 color[0] = bound(0, color2[0], 1);
1558 color[1] = bound(0, color2[1], 1);
1559 color[2] = bound(0, color2[2], 1);
1560 if (r_textureunits.integer >= 2)
1561 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1563 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1564 R_Mesh_Draw(numverts, numtriangles, elements);
1566 c_rt_lighttris += numtriangles;
1571 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)
1574 float color[3], color2[3], colorscale;
1576 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1579 glosstexture = r_shadow_blankglosstexture;
1580 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1582 colorscale = r_shadow_glossintensity.value;
1584 bumptexture = r_shadow_blankbumptexture;
1585 if (glosstexture == r_shadow_blankglosstexture)
1586 colorscale *= r_shadow_gloss2intensity.value;
1587 GL_VertexPointer(vertex3f);
1589 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1591 // 2/0/0/1/2 3D combine blendsquare path
1592 memset(&m, 0, sizeof(m));
1593 m.tex[0] = R_GetTexture(bumptexture);
1594 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1595 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1596 m.pointer_texcoord[0] = texcoord2f;
1597 m.pointer_texcoord[1] = varray_texcoord3f[1];
1598 R_Mesh_State_Texture(&m);
1599 qglColorMask(0,0,0,1);
1600 // this squares the result
1601 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1602 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1603 R_Mesh_Draw(numverts, numtriangles, elements);
1605 c_rt_lighttris += numtriangles;
1607 memset(&m, 0, sizeof(m));
1608 R_Mesh_State_Texture(&m);
1609 // square alpha in framebuffer a few times to make it shiny
1610 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1611 // these comments are a test run through this math for intensity 0.5
1612 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1613 // 0.25 * 0.25 = 0.0625 (this is another pass)
1614 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1615 R_Mesh_Draw(numverts, numtriangles, elements);
1617 c_rt_lighttris += numtriangles;
1618 R_Mesh_Draw(numverts, numtriangles, elements);
1620 c_rt_lighttris += numtriangles;
1622 memset(&m, 0, sizeof(m));
1623 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1624 m.pointer_texcoord[0] = varray_texcoord3f[0];
1625 R_Mesh_State_Texture(&m);
1626 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1627 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1628 R_Mesh_Draw(numverts, numtriangles, elements);
1630 c_rt_lighttris += numtriangles;
1632 memset(&m, 0, sizeof(m));
1633 m.tex[0] = R_GetTexture(glosstexture);
1636 m.texcubemap[1] = R_GetTexture(lightcubemap);
1637 m.pointer_texcoord[1] = varray_texcoord3f[1];
1638 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1640 m.pointer_texcoord[0] = texcoord2f;
1641 R_Mesh_State_Texture(&m);
1642 qglColorMask(1,1,1,0);
1643 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1644 VectorScale(lightcolor, colorscale, color2);
1645 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1647 color[0] = bound(0, color2[0], 1);
1648 color[1] = bound(0, color2[1], 1);
1649 color[2] = bound(0, color2[2], 1);
1650 GL_Color(color[0], color[1], color[2], 1);
1651 R_Mesh_Draw(numverts, numtriangles, elements);
1653 c_rt_lighttris += numtriangles;
1656 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1658 // 2/0/0/2 3D combine blendsquare path
1659 memset(&m, 0, sizeof(m));
1660 m.tex[0] = R_GetTexture(bumptexture);
1661 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1662 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1663 m.pointer_texcoord[0] = texcoord2f;
1664 m.pointer_texcoord[1] = varray_texcoord3f[1];
1665 R_Mesh_State_Texture(&m);
1666 qglColorMask(0,0,0,1);
1667 // this squares the result
1668 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1669 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1670 R_Mesh_Draw(numverts, numtriangles, elements);
1672 c_rt_lighttris += numtriangles;
1674 memset(&m, 0, sizeof(m));
1675 R_Mesh_State_Texture(&m);
1676 // square alpha in framebuffer a few times to make it shiny
1677 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1678 // these comments are a test run through this math for intensity 0.5
1679 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1680 // 0.25 * 0.25 = 0.0625 (this is another pass)
1681 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1682 R_Mesh_Draw(numverts, numtriangles, elements);
1684 c_rt_lighttris += numtriangles;
1685 R_Mesh_Draw(numverts, numtriangles, elements);
1687 c_rt_lighttris += numtriangles;
1689 memset(&m, 0, sizeof(m));
1690 m.tex[0] = R_GetTexture(glosstexture);
1691 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1692 m.pointer_texcoord[0] = texcoord2f;
1693 m.pointer_texcoord[1] = varray_texcoord3f[1];
1694 R_Mesh_State_Texture(&m);
1695 qglColorMask(1,1,1,0);
1696 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1697 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1698 VectorScale(lightcolor, colorscale, color2);
1699 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1701 color[0] = bound(0, color2[0], 1);
1702 color[1] = bound(0, color2[1], 1);
1703 color[2] = bound(0, color2[2], 1);
1704 GL_Color(color[0], color[1], color[2], 1);
1705 R_Mesh_Draw(numverts, numtriangles, elements);
1707 c_rt_lighttris += numtriangles;
1710 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1712 // 2/0/0/2/2 2D combine blendsquare path
1713 memset(&m, 0, sizeof(m));
1714 m.tex[0] = R_GetTexture(bumptexture);
1715 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1716 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1717 m.pointer_texcoord[0] = texcoord2f;
1718 m.pointer_texcoord[1] = varray_texcoord3f[1];
1719 R_Mesh_State_Texture(&m);
1720 qglColorMask(0,0,0,1);
1721 // this squares the result
1722 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1723 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1724 R_Mesh_Draw(numverts, numtriangles, elements);
1726 c_rt_lighttris += numtriangles;
1728 memset(&m, 0, sizeof(m));
1729 R_Mesh_State_Texture(&m);
1730 // square alpha in framebuffer a few times to make it shiny
1731 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1732 // these comments are a test run through this math for intensity 0.5
1733 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1734 // 0.25 * 0.25 = 0.0625 (this is another pass)
1735 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1736 R_Mesh_Draw(numverts, numtriangles, elements);
1738 c_rt_lighttris += numtriangles;
1739 R_Mesh_Draw(numverts, numtriangles, elements);
1741 c_rt_lighttris += numtriangles;
1743 memset(&m, 0, sizeof(m));
1744 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1745 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1746 m.pointer_texcoord[0] = varray_texcoord2f[0];
1747 m.pointer_texcoord[1] = varray_texcoord2f[1];
1748 R_Mesh_State_Texture(&m);
1749 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1750 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1751 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1752 R_Mesh_Draw(numverts, numtriangles, elements);
1754 c_rt_lighttris += numtriangles;
1756 memset(&m, 0, sizeof(m));
1757 m.tex[0] = R_GetTexture(glosstexture);
1760 m.texcubemap[1] = R_GetTexture(lightcubemap);
1761 m.pointer_texcoord[1] = varray_texcoord3f[1];
1762 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1764 m.pointer_texcoord[0] = texcoord2f;
1765 R_Mesh_State_Texture(&m);
1766 qglColorMask(1,1,1,0);
1767 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1768 VectorScale(lightcolor, colorscale, color2);
1769 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1771 color[0] = bound(0, color2[0], 1);
1772 color[1] = bound(0, color2[1], 1);
1773 color[2] = bound(0, color2[2], 1);
1774 GL_Color(color[0], color[1], color[2], 1);
1775 R_Mesh_Draw(numverts, numtriangles, elements);
1777 c_rt_lighttris += numtriangles;
1783 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1785 R_Mesh_Matrix(matrix);
1786 if (r_shadow_showtris.integer)
1790 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1791 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1792 qglDisable(GL_DEPTH_TEST);
1793 qglDisable(GL_STENCIL_TEST);
1794 //qglDisable(GL_CULL_FACE);
1795 qglColorMask(1,1,1,1);
1796 memset(&m, 0, sizeof(m));
1797 R_Mesh_State_Texture(&m);
1798 GL_Color(0,0.1,0,1);
1799 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1800 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1802 GL_VertexPointer(mesh->vertex3f);
1803 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1805 //qglEnable(GL_CULL_FACE);
1807 qglEnable(GL_DEPTH_TEST);
1810 qglEnable(GL_STENCIL_TEST);
1811 qglColorMask(0,0,0,0);
1814 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1817 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)
1820 R_Mesh_Matrix(matrix);
1821 if (r_shadow_showtris.integer)
1824 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1825 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1826 qglDisable(GL_DEPTH_TEST);
1827 qglDisable(GL_STENCIL_TEST);
1828 //qglDisable(GL_CULL_FACE);
1829 memset(&m, 0, sizeof(m));
1830 R_Mesh_State_Texture(&m);
1831 GL_Color(0.2,0,0,1);
1832 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1833 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1835 GL_VertexPointer(mesh->vertex3f);
1836 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1838 //qglEnable(GL_CULL_FACE);
1840 qglEnable(GL_DEPTH_TEST);
1842 qglEnable(GL_STENCIL_TEST);
1844 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1846 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);
1847 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);
1851 cvar_t r_editlights = {0, "r_editlights", "0"};
1852 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1853 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1854 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1855 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1856 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1857 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1858 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1859 worldlight_t *r_shadow_worldlightchain;
1860 worldlight_t *r_shadow_selectedlight;
1861 vec3_t r_editlights_cursorlocation;
1863 static int lightpvsbytes;
1864 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1866 typedef struct cubemapinfo_s
1869 rtexture_t *texture;
1873 #define MAX_CUBEMAPS 128
1874 static int numcubemaps;
1875 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1877 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1878 typedef struct suffixinfo_s
1881 int flipx, flipy, flipdiagonal;
1884 static suffixinfo_t suffix[3][6] =
1887 {"posx", false, false, false},
1888 {"negx", false, false, false},
1889 {"posy", false, false, false},
1890 {"negy", false, false, false},
1891 {"posz", false, false, false},
1892 {"negz", false, false, false}
1895 {"px", false, false, false},
1896 {"nx", false, false, false},
1897 {"py", false, false, false},
1898 {"ny", false, false, false},
1899 {"pz", false, false, false},
1900 {"nz", false, false, false}
1903 {"ft", true, false, true},
1904 {"bk", false, true, true},
1905 {"lf", true, true, false},
1906 {"rt", false, false, false},
1907 {"up", false, false, false},
1908 {"dn", false, false, false}
1912 static int componentorder[4] = {0, 1, 2, 3};
1914 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1916 int i, j, cubemapsize;
1917 qbyte *cubemappixels, *image_rgba;
1918 rtexture_t *cubemaptexture;
1920 // must start 0 so the first loadimagepixels has no requested width/height
1922 cubemappixels = NULL;
1923 cubemaptexture = NULL;
1924 for (j = 0;j < 3 && !cubemappixels;j++)
1926 for (i = 0;i < 6;i++)
1928 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1929 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
1931 if (image_width == image_height)
1933 if (!cubemappixels && image_width >= 1)
1935 cubemapsize = image_width;
1936 // note this clears to black, so unavailable sizes are black
1937 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1940 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);
1943 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1944 Mem_Free(image_rgba);
1950 if (!r_shadow_filters_texturepool)
1951 r_shadow_filters_texturepool = R_AllocTexturePool();
1952 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1953 Mem_Free(cubemappixels);
1957 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1958 for (j = 0;j < 3;j++)
1959 for (i = 0;i < 6;i++)
1960 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1961 Con_Printf(" and was unable to find any of them.\n");
1963 return cubemaptexture;
1966 rtexture_t *R_Shadow_Cubemap(const char *basename)
1969 for (i = 0;i < numcubemaps;i++)
1970 if (!strcasecmp(cubemaps[i].basename, basename))
1971 return cubemaps[i].texture;
1972 if (i >= MAX_CUBEMAPS)
1975 strcpy(cubemaps[i].basename, basename);
1976 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1977 return cubemaps[i].texture;
1980 void R_Shadow_FreeCubemaps(void)
1983 R_FreeTexturePool(&r_shadow_filters_texturepool);
1986 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)
1988 int i, j, k, l, maxverts = 256, tris;
1989 float *vertex3f = NULL, mins[3], maxs[3];
1991 shadowmesh_t *mesh, *castmesh = NULL;
1993 if (radius < 15 || DotProduct(color, color) < 0.03)
1995 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1999 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
2000 VectorCopy(origin, e->origin);
2001 VectorCopy(angles, e->angles);
2002 VectorCopy(color, e->color);
2005 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
2007 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
2010 e->drawshadows = shadowenable;
2013 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);
2014 Matrix4x4_Invert_Simple(&e->matrix_worldtolight, &e->matrix_lighttoworld);
2015 Matrix4x4_Concat(&e->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &e->matrix_worldtolight);
2016 Matrix4x4_Concat(&e->matrix_worldtoattenuationz, &matrix_attenuationz, &e->matrix_worldtolight);
2018 e->cullradius = e->radius;
2019 for (k = 0;k < 3;k++)
2021 mins[k] = e->origin[k] - e->radius;
2022 maxs[k] = e->origin[k] + e->radius;
2025 e->next = r_shadow_worldlightchain;
2026 r_shadow_worldlightchain = e;
2027 if (cubemapname && cubemapname[0])
2029 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2030 strcpy(e->cubemapname, cubemapname);
2031 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2033 // FIXME: rewrite this to store ALL geometry into a cache in the light
2035 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2036 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2039 if (cl.worldmodel->brushq3.num_leafs)
2043 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2044 VectorCopy(e->origin, e->mins);
2045 VectorCopy(e->origin, e->maxs);
2046 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2047 face->lighttemp_castshadow = false;
2048 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2050 if ((leaf->clusterindex < 0 || lightpvs[leaf->clusterindex >> 3] & (1 << (leaf->clusterindex & 7))) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2052 for (k = 0;k < 3;k++)
2054 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2055 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2057 for (j = 0;j < leaf->numleaffaces;j++)
2059 face = leaf->firstleafface[j];
2060 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2061 face->lighttemp_castshadow = true;
2066 // add surfaces to shadow casting mesh and light mesh
2067 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2069 if (face->lighttemp_castshadow)
2071 face->lighttemp_castshadow = false;
2072 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2075 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2076 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2077 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2078 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);
2083 else if (cl.worldmodel->brushq1.numleafs)
2087 VectorCopy(e->origin, e->mins);
2088 VectorCopy(e->origin, e->maxs);
2089 i = CL_PointQ1Contents(e->origin);
2091 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2092 surf->lighttemp_castshadow = false;
2094 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2097 qbyte *bytesurfacepvs;
2099 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
2100 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2102 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2104 for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
2106 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2108 for (k = 0;k < 3;k++)
2110 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2111 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2116 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2117 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2118 surf->lighttemp_castshadow = true;
2120 Mem_Free(byteleafpvs);
2121 Mem_Free(bytesurfacepvs);
2125 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
2126 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
2128 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2130 for (k = 0;k < 3;k++)
2132 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2133 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2135 for (j = 0;j < leaf->nummarksurfaces;j++)
2137 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2138 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2139 surf->lighttemp_castshadow = true;
2145 // add surfaces to shadow casting mesh and light mesh
2146 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2148 if (surf->lighttemp_castshadow)
2150 surf->lighttemp_castshadow = false;
2151 if (e->drawshadows && (surf->flags & SURF_SHADOWCAST))
2152 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);
2153 if (!(surf->flags & SURF_DRAWSKY))
2154 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);
2160 // limit box to light bounds (in case it grew larger)
2161 for (k = 0;k < 3;k++)
2163 if (e->mins[k] < e->origin[k] - e->radius) e->mins[k] = e->origin[k] - e->radius;
2164 if (e->maxs[k] > e->origin[k] + e->radius) e->maxs[k] = e->origin[k] + e->radius;
2166 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2168 // cast shadow volume from castmesh
2169 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2173 for (mesh = castmesh;mesh;mesh = mesh->next)
2175 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2176 maxverts = max(maxverts, mesh->numverts * 2);
2181 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2182 // now that we have the buffers big enough, construct and add
2183 // the shadow volume mesh
2185 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2186 for (mesh = castmesh;mesh;mesh = mesh->next)
2188 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2189 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)))
2190 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2195 // we're done with castmesh now
2196 Mod_ShadowMesh_Free(castmesh);
2199 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2200 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2203 if (e->meshchain_shadow)
2204 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2205 k += mesh->numtriangles;
2207 if (e->meshchain_light)
2208 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2209 l += mesh->numtriangles;
2210 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);
2213 void R_Shadow_FreeWorldLight(worldlight_t *light)
2215 worldlight_t **lightpointer;
2216 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2217 if (*lightpointer != light)
2218 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2219 *lightpointer = light->next;
2220 if (light->cubemapname)
2221 Mem_Free(light->cubemapname);
2222 if (light->meshchain_shadow)
2223 Mod_ShadowMesh_Free(light->meshchain_shadow);
2224 if (light->meshchain_light)
2225 Mod_ShadowMesh_Free(light->meshchain_light);
2229 void R_Shadow_ClearWorldLights(void)
2231 while (r_shadow_worldlightchain)
2232 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2233 r_shadow_selectedlight = NULL;
2234 R_Shadow_FreeCubemaps();
2237 void R_Shadow_SelectLight(worldlight_t *light)
2239 if (r_shadow_selectedlight)
2240 r_shadow_selectedlight->selected = false;
2241 r_shadow_selectedlight = light;
2242 if (r_shadow_selectedlight)
2243 r_shadow_selectedlight->selected = true;
2246 rtexture_t *lighttextures[5];
2248 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2250 float scale = r_editlights_cursorgrid.value * 0.5f;
2251 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);
2254 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2257 const worldlight_t *light;
2260 if (light->selected)
2261 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2262 if (!light->meshchain_shadow)
2264 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);
2267 void R_Shadow_DrawLightSprites(void)
2271 worldlight_t *light;
2273 for (i = 0;i < 5;i++)
2275 lighttextures[i] = NULL;
2276 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2277 lighttextures[i] = pic->tex;
2280 for (light = r_shadow_worldlightchain;light;light = light->next)
2281 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2282 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2285 void R_Shadow_SelectLightInView(void)
2287 float bestrating, rating, temp[3];
2288 worldlight_t *best, *light;
2291 for (light = r_shadow_worldlightchain;light;light = light->next)
2293 VectorSubtract(light->origin, r_vieworigin, temp);
2294 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2297 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2298 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2300 bestrating = rating;
2305 R_Shadow_SelectLight(best);
2308 void R_Shadow_LoadWorldLights(void)
2310 int n, a, style, shadow;
2311 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2312 float origin[3], radius, color[3], angles[3], corona;
2313 if (cl.worldmodel == NULL)
2315 Con_Printf("No map loaded.\n");
2318 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2319 strlcat (name, ".rtlights", sizeof (name));
2320 lightsstring = FS_LoadFile(name, false);
2328 while (*s && *s != '\n')
2334 // check for modifier flags
2340 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]);
2344 VectorClear(angles);
2351 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);
2354 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2355 radius *= r_editlights_rtlightssizescale.value;
2356 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2361 Con_Printf("invalid rtlights file \"%s\"\n", name);
2362 Mem_Free(lightsstring);
2366 void R_Shadow_SaveWorldLights(void)
2368 worldlight_t *light;
2369 int bufchars, bufmaxchars;
2371 char name[MAX_QPATH];
2373 if (!r_shadow_worldlightchain)
2375 if (cl.worldmodel == NULL)
2377 Con_Printf("No map loaded.\n");
2380 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2381 strlcat (name, ".rtlights", sizeof (name));
2382 bufchars = bufmaxchars = 0;
2384 for (light = r_shadow_worldlightchain;light;light = light->next)
2386 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 : "");
2387 if (bufchars + (int) strlen(line) > bufmaxchars)
2389 bufmaxchars = bufchars + strlen(line) + 2048;
2391 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2395 memcpy(buf, oldbuf, bufchars);
2401 memcpy(buf + bufchars, line, strlen(line));
2402 bufchars += strlen(line);
2406 FS_WriteFile(name, buf, bufchars);
2411 void R_Shadow_LoadLightsFile(void)
2414 char name[MAX_QPATH], *lightsstring, *s, *t;
2415 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2416 if (cl.worldmodel == NULL)
2418 Con_Printf("No map loaded.\n");
2421 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2422 strlcat (name, ".lights", sizeof (name));
2423 lightsstring = FS_LoadFile(name, false);
2431 while (*s && *s != '\n')
2436 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);
2440 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);
2443 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2444 radius = bound(15, radius, 4096);
2445 VectorScale(color, (2.0f / (8388608.0f)), color);
2446 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2451 Con_Printf("invalid lights file \"%s\"\n", name);
2452 Mem_Free(lightsstring);
2456 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2458 int entnum, style, islight;
2459 char key[256], value[1024];
2460 float origin[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2463 if (cl.worldmodel == NULL)
2465 Con_Printf("No map loaded.\n");
2468 data = cl.worldmodel->brush.entities;
2471 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2474 origin[0] = origin[1] = origin[2] = 0;
2475 originhack[0] = originhack[1] = originhack[2] = 0;
2476 color[0] = color[1] = color[2] = 1;
2477 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2484 if (!COM_ParseToken(&data, false))
2486 if (com_token[0] == '}')
2487 break; // end of entity
2488 if (com_token[0] == '_')
2489 strcpy(key, com_token + 1);
2491 strcpy(key, com_token);
2492 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2493 key[strlen(key)-1] = 0;
2494 if (!COM_ParseToken(&data, false))
2496 strcpy(value, com_token);
2498 // now that we have the key pair worked out...
2499 if (!strcmp("light", key))
2500 light = atof(value);
2501 else if (!strcmp("origin", key))
2502 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2503 else if (!strcmp("color", key))
2504 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2505 else if (!strcmp("wait", key))
2506 fadescale = atof(value);
2507 else if (!strcmp("classname", key))
2509 if (!strncmp(value, "light", 5))
2512 if (!strcmp(value, "light_fluoro"))
2517 overridecolor[0] = 1;
2518 overridecolor[1] = 1;
2519 overridecolor[2] = 1;
2521 if (!strcmp(value, "light_fluorospark"))
2526 overridecolor[0] = 1;
2527 overridecolor[1] = 1;
2528 overridecolor[2] = 1;
2530 if (!strcmp(value, "light_globe"))
2535 overridecolor[0] = 1;
2536 overridecolor[1] = 0.8;
2537 overridecolor[2] = 0.4;
2539 if (!strcmp(value, "light_flame_large_yellow"))
2544 overridecolor[0] = 1;
2545 overridecolor[1] = 0.5;
2546 overridecolor[2] = 0.1;
2548 if (!strcmp(value, "light_flame_small_yellow"))
2553 overridecolor[0] = 1;
2554 overridecolor[1] = 0.5;
2555 overridecolor[2] = 0.1;
2557 if (!strcmp(value, "light_torch_small_white"))
2562 overridecolor[0] = 1;
2563 overridecolor[1] = 0.5;
2564 overridecolor[2] = 0.1;
2566 if (!strcmp(value, "light_torch_small_walltorch"))
2571 overridecolor[0] = 1;
2572 overridecolor[1] = 0.5;
2573 overridecolor[2] = 0.1;
2577 else if (!strcmp("style", key))
2578 style = atoi(value);
2579 else if (cl.worldmodel->type == mod_brushq3)
2581 if (!strcmp("scale", key))
2582 lightscale = atof(value);
2583 if (!strcmp("fade", key))
2584 fadescale = atof(value);
2587 if (light <= 0 && islight)
2589 if (lightscale <= 0)
2593 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2594 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2595 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2596 VectorCopy(overridecolor, color);
2597 VectorScale(color, light, color);
2598 VectorAdd(origin, originhack, origin);
2600 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2605 void R_Shadow_SetCursorLocationForView(void)
2607 vec_t dist, push, frac;
2608 vec3_t dest, endpos, normal;
2609 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2610 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2613 dist = frac * r_editlights_cursordistance.value;
2614 push = r_editlights_cursorpushback.value;
2618 VectorMA(endpos, push, r_viewforward, endpos);
2619 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2621 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2622 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2623 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2626 void R_Shadow_UpdateWorldLightSelection(void)
2628 if (r_editlights.integer)
2630 R_Shadow_SetCursorLocationForView();
2631 R_Shadow_SelectLightInView();
2632 R_Shadow_DrawLightSprites();
2635 R_Shadow_SelectLight(NULL);
2638 void R_Shadow_EditLights_Clear_f(void)
2640 R_Shadow_ClearWorldLights();
2643 void R_Shadow_EditLights_Reload_f(void)
2645 r_shadow_reloadlights = true;
2648 void R_Shadow_EditLights_Save_f(void)
2651 R_Shadow_SaveWorldLights();
2654 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2656 R_Shadow_ClearWorldLights();
2657 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2660 void R_Shadow_EditLights_ImportLightsFile_f(void)
2662 R_Shadow_ClearWorldLights();
2663 R_Shadow_LoadLightsFile();
2666 void R_Shadow_EditLights_Spawn_f(void)
2669 if (!r_editlights.integer)
2671 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2674 if (Cmd_Argc() != 1)
2676 Con_Printf("r_editlights_spawn does not take parameters\n");
2679 color[0] = color[1] = color[2] = 1;
2680 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2683 void R_Shadow_EditLights_Edit_f(void)
2685 vec3_t origin, angles, color;
2686 vec_t radius, corona;
2688 char cubemapname[1024];
2689 if (!r_editlights.integer)
2691 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2694 if (!r_shadow_selectedlight)
2696 Con_Printf("No selected light.\n");
2699 VectorCopy(r_shadow_selectedlight->origin, origin);
2700 VectorCopy(r_shadow_selectedlight->angles, angles);
2701 VectorCopy(r_shadow_selectedlight->color, color);
2702 radius = r_shadow_selectedlight->radius;
2703 style = r_shadow_selectedlight->style;
2704 if (r_shadow_selectedlight->cubemapname)
2705 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2708 shadows = r_shadow_selectedlight->drawshadows;
2709 corona = r_shadow_selectedlight->corona;
2710 if (!strcmp(Cmd_Argv(1), "origin"))
2712 if (Cmd_Argc() != 5)
2714 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2717 origin[0] = atof(Cmd_Argv(2));
2718 origin[1] = atof(Cmd_Argv(3));
2719 origin[2] = atof(Cmd_Argv(4));
2721 else if (!strcmp(Cmd_Argv(1), "originx"))
2723 if (Cmd_Argc() != 3)
2725 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2728 origin[0] = atof(Cmd_Argv(2));
2730 else if (!strcmp(Cmd_Argv(1), "originy"))
2732 if (Cmd_Argc() != 3)
2734 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2737 origin[1] = atof(Cmd_Argv(2));
2739 else if (!strcmp(Cmd_Argv(1), "originz"))
2741 if (Cmd_Argc() != 3)
2743 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2746 origin[2] = atof(Cmd_Argv(2));
2748 else if (!strcmp(Cmd_Argv(1), "move"))
2750 if (Cmd_Argc() != 5)
2752 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2755 origin[0] += atof(Cmd_Argv(2));
2756 origin[1] += atof(Cmd_Argv(3));
2757 origin[2] += atof(Cmd_Argv(4));
2759 else if (!strcmp(Cmd_Argv(1), "movex"))
2761 if (Cmd_Argc() != 3)
2763 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2766 origin[0] += atof(Cmd_Argv(2));
2768 else if (!strcmp(Cmd_Argv(1), "movey"))
2770 if (Cmd_Argc() != 3)
2772 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2775 origin[1] += atof(Cmd_Argv(2));
2777 else if (!strcmp(Cmd_Argv(1), "movez"))
2779 if (Cmd_Argc() != 3)
2781 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2784 origin[2] += atof(Cmd_Argv(2));
2786 if (!strcmp(Cmd_Argv(1), "angles"))
2788 if (Cmd_Argc() != 5)
2790 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2793 angles[0] = atof(Cmd_Argv(2));
2794 angles[1] = atof(Cmd_Argv(3));
2795 angles[2] = atof(Cmd_Argv(4));
2797 else if (!strcmp(Cmd_Argv(1), "anglesx"))
2799 if (Cmd_Argc() != 3)
2801 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2804 angles[0] = atof(Cmd_Argv(2));
2806 else if (!strcmp(Cmd_Argv(1), "anglesy"))
2808 if (Cmd_Argc() != 3)
2810 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2813 angles[1] = atof(Cmd_Argv(2));
2815 else if (!strcmp(Cmd_Argv(1), "anglesz"))
2817 if (Cmd_Argc() != 3)
2819 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2822 angles[2] = atof(Cmd_Argv(2));
2824 else if (!strcmp(Cmd_Argv(1), "color"))
2826 if (Cmd_Argc() != 5)
2828 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
2831 color[0] = atof(Cmd_Argv(2));
2832 color[1] = atof(Cmd_Argv(3));
2833 color[2] = atof(Cmd_Argv(4));
2835 else if (!strcmp(Cmd_Argv(1), "radius"))
2837 if (Cmd_Argc() != 3)
2839 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2842 radius = atof(Cmd_Argv(2));
2844 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2846 if (Cmd_Argc() != 3)
2848 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2851 style = atoi(Cmd_Argv(2));
2853 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2857 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2860 if (Cmd_Argc() == 3)
2861 strcpy(cubemapname, Cmd_Argv(2));
2865 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2867 if (Cmd_Argc() != 3)
2869 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2872 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2874 else if (!strcmp(Cmd_Argv(1), "corona"))
2876 if (Cmd_Argc() != 3)
2878 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2881 corona = atof(Cmd_Argv(2));
2885 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2886 Con_Printf("Selected light's properties:\n");
2887 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2888 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
2889 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
2890 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
2891 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
2892 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
2893 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->drawshadows ? "yes" : "no");
2894 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2897 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2898 r_shadow_selectedlight = NULL;
2899 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
2902 extern int con_vislines;
2903 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2907 if (r_shadow_selectedlight == NULL)
2911 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2912 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;
2913 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;
2914 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;
2915 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2916 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2917 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2918 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;
2919 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2922 void R_Shadow_EditLights_ToggleShadow_f(void)
2924 if (!r_editlights.integer)
2926 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2929 if (!r_shadow_selectedlight)
2931 Con_Printf("No selected light.\n");
2934 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);
2935 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2936 r_shadow_selectedlight = NULL;
2939 void R_Shadow_EditLights_ToggleCorona_f(void)
2941 if (!r_editlights.integer)
2943 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2946 if (!r_shadow_selectedlight)
2948 Con_Printf("No selected light.\n");
2951 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);
2952 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2953 r_shadow_selectedlight = NULL;
2956 void R_Shadow_EditLights_Remove_f(void)
2958 if (!r_editlights.integer)
2960 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2963 if (!r_shadow_selectedlight)
2965 Con_Printf("No selected light.\n");
2968 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2969 r_shadow_selectedlight = NULL;
2972 void R_Shadow_EditLights_Help_f(void)
2975 "Documentation on r_editlights system:\n"
2977 "r_editlights : enable/disable editing mode\n"
2978 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
2979 "r_editlights_cursorpushback : push back cursor this far from surface\n"
2980 "r_editlights_cursorpushoff : push cursor off surface this far\n"
2981 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
2982 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
2983 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
2984 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
2986 "r_editlights_help : this help\n"
2987 "r_editlights_clear : remove all lights\n"
2988 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
2989 "r_editlights_save : save to .rtlights file\n"
2990 "r_editlights_spawn : create a light with default settings\n"
2991 "r_editlights_edit command : edit selected light - more documentation below\n"
2992 "r_editlights_remove : remove selected light\n"
2993 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
2994 "r_editlights_importlightentitiesfrommap : reload light entities\n"
2995 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
2997 "origin x y z : set light location\n"
2998 "originx x: set x component of light location\n"
2999 "originy y: set y component of light location\n"
3000 "originz z: set z component of light location\n"
3001 "move x y z : adjust light location\n"
3002 "movex x: adjust x component of light location\n"
3003 "movey y: adjust y component of light location\n"
3004 "movez z: adjust z component of light location\n"
3005 "angles x y z : set light angles\n"
3006 "anglesx x: set x component of light angles\n"
3007 "anglesy y: set y component of light angles\n"
3008 "anglesz z: set z component of light angles\n"
3009 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3010 "radius radius : set radius (size) of light\n"
3011 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3012 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3013 "shadows 1/0 : turn on/off shadows\n"
3014 "corona n : set corona intensity\n"
3015 "<nothing> : print light properties to console\n"
3019 void R_Shadow_EditLights_Init(void)
3021 Cvar_RegisterVariable(&r_editlights);
3022 Cvar_RegisterVariable(&r_editlights_cursordistance);
3023 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3024 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3025 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3026 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3027 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3028 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3029 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3030 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3031 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3032 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3033 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3034 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3035 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3036 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3037 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3038 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3039 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);