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 if (gamemode == GAME_TENEBRAE)
302 Cvar_SetValue("r_shadow_gloss", 2);
303 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
305 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
306 R_Shadow_EditLights_Init();
307 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
310 matrix4x4_t matrix_attenuationxyz =
313 {0.5, 0.0, 0.0, 0.5},
314 {0.0, 0.5, 0.0, 0.5},
315 {0.0, 0.0, 0.5, 0.5},
320 matrix4x4_t matrix_attenuationz =
323 {0.0, 0.0, 0.5, 0.5},
324 {0.0, 0.0, 0.0, 0.0},
325 {0.0, 0.0, 0.0, 0.0},
330 void R_Shadow_ResizeTriangleFacingLight(int numtris)
332 // make sure trianglefacinglight is big enough for this volume
333 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
334 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
335 if (maxtrianglefacinglight < numtris)
337 maxtrianglefacinglight = numtris;
338 if (trianglefacinglight)
339 Mem_Free(trianglefacinglight);
340 if (trianglefacinglightlist)
341 Mem_Free(trianglefacinglightlist);
342 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
343 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
347 int *R_Shadow_ResizeShadowElements(int numtris)
349 // make sure shadowelements is big enough for this volume
350 if (maxshadowelements < numtris * 24)
352 maxshadowelements = numtris * 24;
354 Mem_Free(shadowelements);
355 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
357 return shadowelements;
361 // readable version of some code found below
362 //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]))))
363 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
365 float dir0[3], dir1[3], normal[3];
367 // calculate two mostly perpendicular edge directions
368 VectorSubtract(a, b, dir0);
369 VectorSubtract(c, b, dir1);
371 // we have two edge directions, we can calculate a third vector from
372 // them, which is the direction of the surface normal (it's magnitude
374 CrossProduct(dir0, dir1, normal);
376 // compare distance of light along normal, with distance of any point
377 // of the triangle along the same normal (the triangle is planar,
378 // I.E. flat, so all points give the same answer)
379 return DotProduct(p, normal) > DotProduct(a, normal);
381 int checkcastshadowfromedge(int t, int i)
385 if (t >= trianglerange_start && t < trianglerange_end)
387 if (t < i && !trianglefacinglight[t])
398 te = inelement3i + t * 3;
399 v[0] = invertex3f + te[0] * 3;
400 v[1] = invertex3f + te[1] * 3;
401 v[2] = invertex3f + te[2] * 3;
402 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
411 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)
413 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
415 const int *e, *n, *te;
418 // make sure trianglefacinglight is big enough for this volume
419 if (maxtrianglefacinglight < trianglerange_end)
420 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
422 if (maxvertexupdate < innumvertices)
424 maxvertexupdate = innumvertices;
426 Mem_Free(vertexupdate);
428 Mem_Free(vertexremap);
429 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
430 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
434 if (r_shadow_singlepassvolumegeneration.integer)
436 // one pass approach (identify lit/dark faces and generate sides while doing so)
437 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
439 // calculate triangle facing flag
440 v[0] = invertex3f + e[0] * 3;
441 v[1] = invertex3f + e[1] * 3;
442 v[2] = invertex3f + e[2] * 3;
443 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
445 // make sure the vertices are created
446 for (j = 0;j < 3;j++)
448 if (vertexupdate[e[j]] != vertexupdatenum)
450 vertexupdate[e[j]] = vertexupdatenum;
451 vertexremap[e[j]] = outvertices;
452 VectorCopy(v[j], outvertex3f);
453 VectorSubtract(v[j], relativelightorigin, temp);
454 f = projectdistance / VectorLength(temp);
455 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
460 // output the front and back triangles
461 vr[0] = vertexremap[e[0]];
462 vr[1] = vertexremap[e[1]];
463 vr[2] = vertexremap[e[2]];
464 outelement3i[0] = vr[0];
465 outelement3i[1] = vr[1];
466 outelement3i[2] = vr[2];
467 outelement3i[3] = vr[2] + 1;
468 outelement3i[4] = vr[1] + 1;
469 outelement3i[5] = vr[0] + 1;
472 // output the sides (facing outward from this triangle)
474 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]))))
476 outelement3i[0] = vr[1];
477 outelement3i[1] = vr[0];
478 outelement3i[2] = vr[0] + 1;
479 outelement3i[3] = vr[1];
480 outelement3i[4] = vr[0] + 1;
481 outelement3i[5] = vr[1] + 1;
486 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]))))
488 outelement3i[0] = vr[2];
489 outelement3i[1] = vr[1];
490 outelement3i[2] = vr[1] + 1;
491 outelement3i[3] = vr[2];
492 outelement3i[4] = vr[1] + 1;
493 outelement3i[5] = vr[2] + 1;
498 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]))))
500 outelement3i[0] = vr[0];
501 outelement3i[1] = vr[2];
502 outelement3i[2] = vr[2] + 1;
503 outelement3i[3] = vr[0];
504 outelement3i[4] = vr[2] + 1;
505 outelement3i[5] = vr[0] + 1;
512 // this triangle is not facing the light
513 // output the sides (facing inward to this triangle)
515 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
517 vr[0] = vertexremap[e[0]];
518 vr[1] = vertexremap[e[1]];
519 outelement3i[0] = vr[1];
520 outelement3i[1] = vr[0] + 1;
521 outelement3i[2] = vr[0];
522 outelement3i[3] = vr[1];
523 outelement3i[4] = vr[1] + 1;
524 outelement3i[5] = vr[0] + 1;
529 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
531 vr[1] = vertexremap[e[1]];
532 vr[2] = vertexremap[e[2]];
533 outelement3i[0] = vr[2];
534 outelement3i[1] = vr[1] + 1;
535 outelement3i[2] = vr[1];
536 outelement3i[3] = vr[2];
537 outelement3i[4] = vr[2] + 1;
538 outelement3i[5] = vr[1] + 1;
543 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
545 vr[0] = vertexremap[e[0]];
546 vr[2] = vertexremap[e[2]];
547 outelement3i[0] = vr[0];
548 outelement3i[1] = vr[2] + 1;
549 outelement3i[2] = vr[2];
550 outelement3i[3] = vr[0];
551 outelement3i[4] = vr[0] + 1;
552 outelement3i[5] = vr[2] + 1;
561 // two pass approach (identify lit/dark faces and then generate sides)
562 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
564 // calculate triangle facing flag
565 v[0] = invertex3f + e[0] * 3;
566 v[1] = invertex3f + e[1] * 3;
567 v[2] = invertex3f + e[2] * 3;
568 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
570 trianglefacinglightlist[numfacing++] = i;
571 // make sure the vertices are created
572 for (j = 0;j < 3;j++)
574 if (vertexupdate[e[j]] != vertexupdatenum)
576 vertexupdate[e[j]] = vertexupdatenum;
577 vertexremap[e[j]] = outvertices;
578 VectorSubtract(v[j], relativelightorigin, temp);
579 f = projectdistance / VectorLength(temp);
580 VectorCopy(v[j], outvertex3f);
581 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
586 // output the front and back triangles
587 outelement3i[0] = vertexremap[e[0]];
588 outelement3i[1] = vertexremap[e[1]];
589 outelement3i[2] = vertexremap[e[2]];
590 outelement3i[3] = vertexremap[e[2]] + 1;
591 outelement3i[4] = vertexremap[e[1]] + 1;
592 outelement3i[5] = vertexremap[e[0]] + 1;
597 for (i = 0;i < numfacing;i++)
599 t = trianglefacinglightlist[i];
600 e = inelement3i + t * 3;
601 n = inneighbor3i + t * 3;
602 // output the sides (facing outward from this triangle)
604 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]))))
606 vr[0] = vertexremap[e[0]];
607 vr[1] = vertexremap[e[1]];
608 outelement3i[0] = vr[1];
609 outelement3i[1] = vr[0];
610 outelement3i[2] = vr[0] + 1;
611 outelement3i[3] = vr[1];
612 outelement3i[4] = vr[0] + 1;
613 outelement3i[5] = vr[1] + 1;
618 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]))))
620 vr[1] = vertexremap[e[1]];
621 vr[2] = vertexremap[e[2]];
622 outelement3i[0] = vr[2];
623 outelement3i[1] = vr[1];
624 outelement3i[2] = vr[1] + 1;
625 outelement3i[3] = vr[2];
626 outelement3i[4] = vr[1] + 1;
627 outelement3i[5] = vr[2] + 1;
632 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]))))
634 vr[0] = vertexremap[e[0]];
635 vr[2] = vertexremap[e[2]];
636 outelement3i[0] = vr[0];
637 outelement3i[1] = vr[2];
638 outelement3i[2] = vr[2] + 1;
639 outelement3i[3] = vr[0];
640 outelement3i[4] = vr[2] + 1;
641 outelement3i[5] = vr[0] + 1;
648 *outnumvertices = outvertices;
652 float varray_vertex3f2[65536*3];
654 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
657 if (projectdistance < 0.1)
659 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
665 // make sure shadowelements is big enough for this volume
666 if (maxshadowelements < numtris * 24)
667 R_Shadow_ResizeShadowElements(numtris);
669 // check which triangles are facing the light, and then output
670 // triangle elements and vertices... by clever use of elements we
671 // can construct the whole shadow from the unprojected vertices and
672 // the projected vertices
673 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
675 GL_VertexPointer(varray_vertex3f2);
676 if (r_shadowstage == SHADOWSTAGE_STENCIL)
678 // decrement stencil if frontface is behind depthbuffer
679 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
680 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
681 R_Mesh_Draw(outverts, tris, shadowelements);
683 c_rt_shadowtris += numtris;
684 // increment stencil if backface is behind depthbuffer
685 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
686 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
688 R_Mesh_Draw(outverts, tris, shadowelements);
690 c_rt_shadowtris += numtris;
694 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
697 if (r_shadowstage == SHADOWSTAGE_STENCIL)
699 // decrement stencil if frontface is behind depthbuffer
700 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
701 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
702 for (mesh = firstmesh;mesh;mesh = mesh->next)
704 GL_VertexPointer(mesh->vertex3f);
705 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
706 c_rtcached_shadowmeshes++;
707 c_rtcached_shadowtris += mesh->numtriangles;
709 // increment stencil if backface is behind depthbuffer
710 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
711 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
713 for (mesh = firstmesh;mesh;mesh = mesh->next)
715 GL_VertexPointer(mesh->vertex3f);
716 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
717 c_rtcached_shadowmeshes++;
718 c_rtcached_shadowtris += mesh->numtriangles;
722 float r_shadow_attenpower, r_shadow_attenscale;
723 static void R_Shadow_MakeTextures(void)
725 int x, y, z, d, side;
726 float v[3], s, t, intensity;
728 R_FreeTexturePool(&r_shadow_texturepool);
729 r_shadow_texturepool = R_AllocTexturePool();
730 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
731 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
733 #define ATTEN2DSIZE 64
734 #define ATTEN3DSIZE 32
735 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
740 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
745 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
750 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
751 if (gl_texturecubemap)
753 for (side = 0;side < 6;side++)
755 for (y = 0;y < NORMSIZE;y++)
757 for (x = 0;x < NORMSIZE;x++)
759 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
760 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
794 intensity = 127.0f / sqrt(DotProduct(v, v));
795 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
796 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
797 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
798 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
802 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
805 r_shadow_normalcubetexture = NULL;
806 for (y = 0;y < ATTEN2DSIZE;y++)
808 for (x = 0;x < ATTEN2DSIZE;x++)
810 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
811 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
813 intensity = 1.0f - sqrt(DotProduct(v, v));
815 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
816 d = bound(0, intensity, 255);
817 data[(y*ATTEN2DSIZE+x)*4+0] = d;
818 data[(y*ATTEN2DSIZE+x)*4+1] = d;
819 data[(y*ATTEN2DSIZE+x)*4+2] = d;
820 data[(y*ATTEN2DSIZE+x)*4+3] = d;
823 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
824 if (r_shadow_texture3d.integer)
826 for (z = 0;z < ATTEN3DSIZE;z++)
828 for (y = 0;y < ATTEN3DSIZE;y++)
830 for (x = 0;x < ATTEN3DSIZE;x++)
832 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
833 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
834 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
835 intensity = 1.0f - sqrt(DotProduct(v, v));
837 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
838 d = bound(0, intensity, 255);
839 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
840 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
841 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
842 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
846 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
851 void R_Shadow_Stage_Begin(void)
855 if (r_shadow_texture3d.integer && !gl_texture3d)
856 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
858 if (!r_shadow_attenuation2dtexture
859 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
860 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
861 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
862 R_Shadow_MakeTextures();
864 memset(&m, 0, sizeof(m));
865 GL_BlendFunc(GL_ONE, GL_ZERO);
868 R_Mesh_State_Texture(&m);
869 GL_Color(0, 0, 0, 1);
870 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
871 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
872 r_shadowstage = SHADOWSTAGE_NONE;
874 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
875 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
876 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
879 void R_Shadow_LoadWorldLightsIfNeeded(void)
881 if (r_shadow_reloadlights && cl.worldmodel)
883 R_Shadow_ClearWorldLights();
884 r_shadow_reloadlights = false;
885 R_Shadow_LoadWorldLights();
886 if (r_shadow_worldlightchain == NULL)
888 R_Shadow_LoadLightsFile();
889 if (r_shadow_worldlightchain == NULL)
890 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
895 void R_Shadow_Stage_ShadowVolumes(void)
898 memset(&m, 0, sizeof(m));
899 R_Mesh_State_Texture(&m);
900 GL_Color(1, 1, 1, 1);
901 qglColorMask(0, 0, 0, 0);
902 GL_BlendFunc(GL_ONE, GL_ZERO);
905 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
906 //if (r_shadow_polygonoffset.value != 0)
908 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
909 // qglEnable(GL_POLYGON_OFFSET_FILL);
912 // qglDisable(GL_POLYGON_OFFSET_FILL);
913 qglDepthFunc(GL_LESS);
914 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
915 qglEnable(GL_STENCIL_TEST);
916 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
917 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
918 r_shadowstage = SHADOWSTAGE_STENCIL;
919 qglClear(GL_STENCIL_BUFFER_BIT);
921 // LordHavoc note: many shadow volumes reside entirely inside the world
922 // (that is to say they are entirely bounded by their lit surfaces),
923 // which can be optimized by handling things as an inverted light volume,
924 // with the shadow boundaries of the world being simulated by an altered
925 // (129) bias to stencil clearing on such lights
926 // FIXME: generate inverted light volumes for use as shadow volumes and
927 // optimize for them as noted above
930 void R_Shadow_Stage_LightWithoutShadows(void)
933 memset(&m, 0, sizeof(m));
934 R_Mesh_State_Texture(&m);
935 GL_BlendFunc(GL_ONE, GL_ONE);
938 qglPolygonOffset(0, 0);
939 //qglDisable(GL_POLYGON_OFFSET_FILL);
940 GL_Color(1, 1, 1, 1);
941 qglColorMask(1, 1, 1, 1);
942 qglDepthFunc(GL_EQUAL);
943 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
944 qglDisable(GL_STENCIL_TEST);
945 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
946 qglStencilFunc(GL_EQUAL, 128, 0xFF);
947 r_shadowstage = SHADOWSTAGE_LIGHT;
951 void R_Shadow_Stage_LightWithShadows(void)
954 memset(&m, 0, sizeof(m));
955 R_Mesh_State_Texture(&m);
956 GL_BlendFunc(GL_ONE, GL_ONE);
959 qglPolygonOffset(0, 0);
960 //qglDisable(GL_POLYGON_OFFSET_FILL);
961 GL_Color(1, 1, 1, 1);
962 qglColorMask(1, 1, 1, 1);
963 qglDepthFunc(GL_EQUAL);
964 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
965 qglEnable(GL_STENCIL_TEST);
966 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
967 // only draw light where this geometry was already rendered AND the
968 // stencil is 128 (values other than this mean shadow)
969 qglStencilFunc(GL_EQUAL, 128, 0xFF);
970 r_shadowstage = SHADOWSTAGE_LIGHT;
974 void R_Shadow_Stage_End(void)
977 memset(&m, 0, sizeof(m));
978 R_Mesh_State_Texture(&m);
979 GL_BlendFunc(GL_ONE, GL_ZERO);
982 qglPolygonOffset(0, 0);
983 //qglDisable(GL_POLYGON_OFFSET_FILL);
984 GL_Color(1, 1, 1, 1);
985 qglColorMask(1, 1, 1, 1);
986 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
987 qglDepthFunc(GL_LEQUAL);
988 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
989 qglDisable(GL_STENCIL_TEST);
990 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
991 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
992 r_shadowstage = SHADOWSTAGE_NONE;
995 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
997 int i, ix1, iy1, ix2, iy2;
998 float x1, y1, x2, y2, x, y, f;
1001 if (!r_shadow_scissor.integer)
1003 // if view is inside the box, just say yes it's visible
1004 // LordHavoc: for some odd reason scissor seems broken without stencil
1005 // (?!? seems like a driver bug) so abort if gl_stencil is false
1006 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1008 GL_Scissor(r_refdef.x, r_refdef.y, r_refdef.width, r_refdef.height);
1011 for (i = 0;i < 3;i++)
1013 if (r_viewforward[i] >= 0)
1024 f = DotProduct(r_viewforward, r_vieworigin) + 1;
1025 if (DotProduct(r_viewforward, v2) <= f)
1027 // entirely behind nearclip plane
1030 if (DotProduct(r_viewforward, v) >= f)
1032 // entirely infront of nearclip plane
1033 x1 = y1 = x2 = y2 = 0;
1034 for (i = 0;i < 8;i++)
1036 v[0] = (i & 1) ? mins[0] : maxs[0];
1037 v[1] = (i & 2) ? mins[1] : maxs[1];
1038 v[2] = (i & 4) ? mins[2] : maxs[2];
1040 GL_TransformToScreen(v, v2);
1041 //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]);
1060 // clipped by nearclip plane
1061 // this is nasty and crude...
1062 // create viewspace bbox
1063 for (i = 0;i < 8;i++)
1065 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1066 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1067 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1068 v2[0] = -DotProduct(v, r_viewleft);
1069 v2[1] = DotProduct(v, r_viewup);
1070 v2[2] = DotProduct(v, r_viewforward);
1073 if (smins[0] > v2[0]) smins[0] = v2[0];
1074 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1075 if (smins[1] > v2[1]) smins[1] = v2[1];
1076 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1077 if (smins[2] > v2[2]) smins[2] = v2[2];
1078 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1082 smins[0] = smaxs[0] = v2[0];
1083 smins[1] = smaxs[1] = v2[1];
1084 smins[2] = smaxs[2] = v2[2];
1087 // now we have a bbox in viewspace
1088 // clip it to the view plane
1091 // return true if that culled the box
1092 if (smins[2] >= smaxs[2])
1094 // ok some of it is infront of the view, transform each corner back to
1095 // worldspace and then to screenspace and make screen rect
1096 // initialize these variables just to avoid compiler warnings
1097 x1 = y1 = x2 = y2 = 0;
1098 for (i = 0;i < 8;i++)
1100 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1101 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1102 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1103 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1104 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1105 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1107 GL_TransformToScreen(v, v2);
1108 //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]);
1125 // this code doesn't handle boxes with any points behind view properly
1126 x1 = 1000;x2 = -1000;
1127 y1 = 1000;y2 = -1000;
1128 for (i = 0;i < 8;i++)
1130 v[0] = (i & 1) ? mins[0] : maxs[0];
1131 v[1] = (i & 2) ? mins[1] : maxs[1];
1132 v[2] = (i & 4) ? mins[2] : maxs[2];
1134 GL_TransformToScreen(v, v2);
1135 //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]);
1153 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1154 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1155 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1156 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1157 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1158 if (ix2 <= ix1 || iy2 <= iy1)
1160 // set up the scissor rectangle
1161 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1162 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1163 //qglEnable(GL_SCISSOR_TEST);
1168 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1170 float *color4f = varray_color4f;
1171 float dist, dot, intensity, v[3], n[3];
1172 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1174 Matrix4x4_Transform(m, vertex3f, v);
1175 if ((dist = DotProduct(v, v)) < 1)
1177 Matrix4x4_Transform3x3(m, normal3f, n);
1178 if ((dot = DotProduct(n, v)) > 0)
1181 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1182 VectorScale(lightcolor, intensity, color4f);
1187 VectorClear(color4f);
1193 VectorClear(color4f);
1199 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1201 float *color4f = varray_color4f;
1202 float dist, dot, intensity, v[3], n[3];
1203 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1205 Matrix4x4_Transform(m, vertex3f, v);
1206 if ((dist = fabs(v[2])) < 1)
1208 Matrix4x4_Transform3x3(m, normal3f, n);
1209 if ((dot = DotProduct(n, v)) > 0)
1211 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1212 VectorScale(lightcolor, intensity, color4f);
1217 VectorClear(color4f);
1223 VectorClear(color4f);
1229 // FIXME: this should be done in a vertex program when possible
1230 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1231 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1235 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1236 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1237 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1244 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1248 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1249 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1256 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)
1260 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1262 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1263 // the cubemap normalizes this for us
1264 out3f[0] = DotProduct(svector3f, lightdir);
1265 out3f[1] = DotProduct(tvector3f, lightdir);
1266 out3f[2] = DotProduct(normal3f, lightdir);
1270 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)
1273 float lightdir[3], eyedir[3], halfdir[3];
1274 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1276 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1277 VectorNormalizeFast(lightdir);
1278 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1279 VectorNormalizeFast(eyedir);
1280 VectorAdd(lightdir, eyedir, halfdir);
1281 // the cubemap normalizes this for us
1282 out3f[0] = DotProduct(svector3f, halfdir);
1283 out3f[1] = DotProduct(tvector3f, halfdir);
1284 out3f[2] = DotProduct(normal3f, halfdir);
1288 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)
1291 float color[3], color2[3];
1293 GL_VertexPointer(vertex3f);
1294 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1297 bumptexture = r_shadow_blankbumptexture;
1299 // colorscale accounts for how much we multiply the brightness during combine
1300 // mult is how many times the final pass of the lighting will be
1301 // performed to get more brightness than otherwise possible
1302 // limit mult to 64 for sanity sake
1303 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1305 // 3/2 3D combine path (Geforce3, Radeon 8500)
1306 memset(&m, 0, sizeof(m));
1307 m.tex[0] = R_GetTexture(bumptexture);
1308 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1309 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1310 m.texcombinergb[0] = GL_REPLACE;
1311 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1312 m.pointer_texcoord[0] = texcoord2f;
1313 m.pointer_texcoord[1] = varray_texcoord3f[1];
1314 m.pointer_texcoord[2] = varray_texcoord3f[2];
1315 R_Mesh_State_Texture(&m);
1316 qglColorMask(0,0,0,1);
1317 GL_BlendFunc(GL_ONE, GL_ZERO);
1318 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1319 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1320 R_Mesh_Draw(numverts, numtriangles, elements);
1322 c_rt_lighttris += numtriangles;
1324 memset(&m, 0, sizeof(m));
1325 m.tex[0] = R_GetTexture(basetexture);
1326 m.pointer_texcoord[0] = texcoord2f;
1329 m.texcubemap[1] = R_GetTexture(lightcubemap);
1330 m.pointer_texcoord[1] = varray_texcoord3f[1];
1331 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1333 R_Mesh_State_Texture(&m);
1334 qglColorMask(1,1,1,0);
1335 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1336 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1337 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1339 color[0] = bound(0, color2[0], 1);
1340 color[1] = bound(0, color2[1], 1);
1341 color[2] = bound(0, color2[2], 1);
1342 GL_Color(color[0], color[1], color[2], 1);
1343 R_Mesh_Draw(numverts, numtriangles, elements);
1345 c_rt_lighttris += numtriangles;
1348 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1350 // 1/2/2 3D combine path (original Radeon)
1351 memset(&m, 0, sizeof(m));
1352 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1353 m.pointer_texcoord[0] = varray_texcoord3f[0];
1354 R_Mesh_State_Texture(&m);
1355 qglColorMask(0,0,0,1);
1356 GL_BlendFunc(GL_ONE, GL_ZERO);
1357 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1358 R_Mesh_Draw(numverts, numtriangles, elements);
1360 c_rt_lighttris += numtriangles;
1362 memset(&m, 0, sizeof(m));
1363 m.tex[0] = R_GetTexture(bumptexture);
1364 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1365 m.texcombinergb[0] = GL_REPLACE;
1366 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1367 m.pointer_texcoord[0] = texcoord2f;
1368 m.pointer_texcoord[1] = varray_texcoord3f[1];
1369 R_Mesh_State_Texture(&m);
1370 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1371 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1372 R_Mesh_Draw(numverts, numtriangles, elements);
1374 c_rt_lighttris += numtriangles;
1376 memset(&m, 0, sizeof(m));
1377 m.tex[0] = R_GetTexture(basetexture);
1378 m.pointer_texcoord[0] = texcoord2f;
1381 m.texcubemap[1] = R_GetTexture(lightcubemap);
1382 m.pointer_texcoord[1] = varray_texcoord3f[1];
1383 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1385 R_Mesh_State_Texture(&m);
1386 qglColorMask(1,1,1,0);
1387 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1388 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1389 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1391 color[0] = bound(0, color2[0], 1);
1392 color[1] = bound(0, color2[1], 1);
1393 color[2] = bound(0, color2[2], 1);
1394 GL_Color(color[0], color[1], color[2], 1);
1395 R_Mesh_Draw(numverts, numtriangles, elements);
1397 c_rt_lighttris += numtriangles;
1400 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1402 // 2/2 3D combine path (original Radeon)
1403 memset(&m, 0, sizeof(m));
1404 m.tex[0] = R_GetTexture(bumptexture);
1405 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1406 m.texcombinergb[0] = GL_REPLACE;
1407 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1408 m.pointer_texcoord[0] = texcoord2f;
1409 m.pointer_texcoord[1] = varray_texcoord3f[1];
1410 R_Mesh_State_Texture(&m);
1411 qglColorMask(0,0,0,1);
1412 GL_BlendFunc(GL_ONE, GL_ZERO);
1413 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1414 R_Mesh_Draw(numverts, numtriangles, elements);
1416 c_rt_lighttris += numtriangles;
1418 memset(&m, 0, sizeof(m));
1419 m.tex[0] = R_GetTexture(basetexture);
1420 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1421 m.pointer_texcoord[0] = texcoord2f;
1422 m.pointer_texcoord[1] = varray_texcoord3f[1];
1423 R_Mesh_State_Texture(&m);
1424 qglColorMask(1,1,1,0);
1425 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1426 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1427 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1428 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1430 color[0] = bound(0, color2[0], 1);
1431 color[1] = bound(0, color2[1], 1);
1432 color[2] = bound(0, color2[2], 1);
1433 GL_Color(color[0], color[1], color[2], 1);
1434 R_Mesh_Draw(numverts, numtriangles, elements);
1436 c_rt_lighttris += numtriangles;
1439 else if (r_textureunits.integer >= 4)
1441 // 4/2 2D combine path (Geforce3, Radeon 8500)
1442 memset(&m, 0, sizeof(m));
1443 m.tex[0] = R_GetTexture(bumptexture);
1444 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1445 m.texcombinergb[0] = GL_REPLACE;
1446 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1447 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1448 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1449 m.pointer_texcoord[0] = texcoord2f;
1450 m.pointer_texcoord[1] = varray_texcoord3f[1];
1451 m.pointer_texcoord[2] = varray_texcoord2f[2];
1452 m.pointer_texcoord[3] = varray_texcoord2f[3];
1453 R_Mesh_State_Texture(&m);
1454 qglColorMask(0,0,0,1);
1455 GL_BlendFunc(GL_ONE, GL_ZERO);
1456 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1457 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1458 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1459 R_Mesh_Draw(numverts, numtriangles, elements);
1461 c_rt_lighttris += numtriangles;
1463 memset(&m, 0, sizeof(m));
1464 m.tex[0] = R_GetTexture(basetexture);
1465 m.pointer_texcoord[0] = texcoord2f;
1468 m.texcubemap[1] = R_GetTexture(lightcubemap);
1469 m.pointer_texcoord[1] = varray_texcoord3f[1];
1470 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1472 R_Mesh_State_Texture(&m);
1473 qglColorMask(1,1,1,0);
1474 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1475 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1476 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1478 color[0] = bound(0, color2[0], 1);
1479 color[1] = bound(0, color2[1], 1);
1480 color[2] = bound(0, color2[2], 1);
1481 GL_Color(color[0], color[1], color[2], 1);
1482 R_Mesh_Draw(numverts, numtriangles, elements);
1484 c_rt_lighttris += numtriangles;
1489 // 2/2/2 2D combine path (any dot3 card)
1490 memset(&m, 0, sizeof(m));
1491 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1492 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1493 m.pointer_texcoord[0] = varray_texcoord2f[0];
1494 m.pointer_texcoord[1] = varray_texcoord2f[1];
1495 R_Mesh_State_Texture(&m);
1496 qglColorMask(0,0,0,1);
1497 GL_BlendFunc(GL_ONE, GL_ZERO);
1498 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1499 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1500 R_Mesh_Draw(numverts, numtriangles, elements);
1502 c_rt_lighttris += numtriangles;
1504 memset(&m, 0, sizeof(m));
1505 m.tex[0] = R_GetTexture(bumptexture);
1506 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1507 m.texcombinergb[0] = GL_REPLACE;
1508 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1509 m.pointer_texcoord[0] = texcoord2f;
1510 m.pointer_texcoord[1] = varray_texcoord3f[1];
1511 R_Mesh_State_Texture(&m);
1512 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1513 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1514 R_Mesh_Draw(numverts, numtriangles, elements);
1516 c_rt_lighttris += numtriangles;
1518 memset(&m, 0, sizeof(m));
1519 m.tex[0] = R_GetTexture(basetexture);
1520 m.pointer_texcoord[0] = texcoord2f;
1523 m.texcubemap[1] = R_GetTexture(lightcubemap);
1524 m.pointer_texcoord[1] = varray_texcoord3f[1];
1525 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1527 R_Mesh_State_Texture(&m);
1528 qglColorMask(1,1,1,0);
1529 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1530 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1531 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1533 color[0] = bound(0, color2[0], 1);
1534 color[1] = bound(0, color2[1], 1);
1535 color[2] = bound(0, color2[2], 1);
1536 GL_Color(color[0], color[1], color[2], 1);
1537 R_Mesh_Draw(numverts, numtriangles, elements);
1539 c_rt_lighttris += numtriangles;
1545 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1546 GL_DepthMask(false);
1548 GL_ColorPointer(varray_color4f);
1549 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1550 memset(&m, 0, sizeof(m));
1551 m.tex[0] = R_GetTexture(basetexture);
1552 m.pointer_texcoord[0] = texcoord2f;
1553 if (r_textureunits.integer >= 2)
1556 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1557 m.pointer_texcoord[1] = varray_texcoord2f[1];
1558 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1560 R_Mesh_State_Texture(&m);
1561 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1563 color[0] = bound(0, color2[0], 1);
1564 color[1] = bound(0, color2[1], 1);
1565 color[2] = bound(0, color2[2], 1);
1566 if (r_textureunits.integer >= 2)
1567 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1569 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1570 R_Mesh_Draw(numverts, numtriangles, elements);
1572 c_rt_lighttris += numtriangles;
1577 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)
1580 float color[3], color2[3], colorscale;
1582 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1585 glosstexture = r_shadow_blankglosstexture;
1586 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1588 colorscale = r_shadow_glossintensity.value;
1590 bumptexture = r_shadow_blankbumptexture;
1591 if (glosstexture == r_shadow_blankglosstexture)
1592 colorscale *= r_shadow_gloss2intensity.value;
1593 GL_VertexPointer(vertex3f);
1595 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1597 // 2/0/0/1/2 3D combine blendsquare path
1598 memset(&m, 0, sizeof(m));
1599 m.tex[0] = R_GetTexture(bumptexture);
1600 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1601 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1602 m.pointer_texcoord[0] = texcoord2f;
1603 m.pointer_texcoord[1] = varray_texcoord3f[1];
1604 R_Mesh_State_Texture(&m);
1605 qglColorMask(0,0,0,1);
1606 // this squares the result
1607 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1608 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1609 R_Mesh_Draw(numverts, numtriangles, elements);
1611 c_rt_lighttris += numtriangles;
1613 memset(&m, 0, sizeof(m));
1614 R_Mesh_State_Texture(&m);
1615 // square alpha in framebuffer a few times to make it shiny
1616 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1617 // these comments are a test run through this math for intensity 0.5
1618 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1619 // 0.25 * 0.25 = 0.0625 (this is another pass)
1620 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1621 R_Mesh_Draw(numverts, numtriangles, elements);
1623 c_rt_lighttris += numtriangles;
1624 R_Mesh_Draw(numverts, numtriangles, elements);
1626 c_rt_lighttris += numtriangles;
1628 memset(&m, 0, sizeof(m));
1629 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1630 m.pointer_texcoord[0] = varray_texcoord3f[0];
1631 R_Mesh_State_Texture(&m);
1632 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1633 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1634 R_Mesh_Draw(numverts, numtriangles, elements);
1636 c_rt_lighttris += numtriangles;
1638 memset(&m, 0, sizeof(m));
1639 m.tex[0] = R_GetTexture(glosstexture);
1642 m.texcubemap[1] = R_GetTexture(lightcubemap);
1643 m.pointer_texcoord[1] = varray_texcoord3f[1];
1644 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1646 m.pointer_texcoord[0] = texcoord2f;
1647 R_Mesh_State_Texture(&m);
1648 qglColorMask(1,1,1,0);
1649 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1650 VectorScale(lightcolor, colorscale, color2);
1651 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1653 color[0] = bound(0, color2[0], 1);
1654 color[1] = bound(0, color2[1], 1);
1655 color[2] = bound(0, color2[2], 1);
1656 GL_Color(color[0], color[1], color[2], 1);
1657 R_Mesh_Draw(numverts, numtriangles, elements);
1659 c_rt_lighttris += numtriangles;
1662 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1664 // 2/0/0/2 3D combine blendsquare path
1665 memset(&m, 0, sizeof(m));
1666 m.tex[0] = R_GetTexture(bumptexture);
1667 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1668 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1669 m.pointer_texcoord[0] = texcoord2f;
1670 m.pointer_texcoord[1] = varray_texcoord3f[1];
1671 R_Mesh_State_Texture(&m);
1672 qglColorMask(0,0,0,1);
1673 // this squares the result
1674 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1675 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1676 R_Mesh_Draw(numverts, numtriangles, elements);
1678 c_rt_lighttris += numtriangles;
1680 memset(&m, 0, sizeof(m));
1681 R_Mesh_State_Texture(&m);
1682 // square alpha in framebuffer a few times to make it shiny
1683 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1684 // these comments are a test run through this math for intensity 0.5
1685 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1686 // 0.25 * 0.25 = 0.0625 (this is another pass)
1687 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1688 R_Mesh_Draw(numverts, numtriangles, elements);
1690 c_rt_lighttris += numtriangles;
1691 R_Mesh_Draw(numverts, numtriangles, elements);
1693 c_rt_lighttris += numtriangles;
1695 memset(&m, 0, sizeof(m));
1696 m.tex[0] = R_GetTexture(glosstexture);
1697 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1698 m.pointer_texcoord[0] = texcoord2f;
1699 m.pointer_texcoord[1] = varray_texcoord3f[1];
1700 R_Mesh_State_Texture(&m);
1701 qglColorMask(1,1,1,0);
1702 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1703 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1704 VectorScale(lightcolor, colorscale, color2);
1705 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1707 color[0] = bound(0, color2[0], 1);
1708 color[1] = bound(0, color2[1], 1);
1709 color[2] = bound(0, color2[2], 1);
1710 GL_Color(color[0], color[1], color[2], 1);
1711 R_Mesh_Draw(numverts, numtriangles, elements);
1713 c_rt_lighttris += numtriangles;
1716 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1718 // 2/0/0/2/2 2D combine blendsquare path
1719 memset(&m, 0, sizeof(m));
1720 m.tex[0] = R_GetTexture(bumptexture);
1721 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1722 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1723 m.pointer_texcoord[0] = texcoord2f;
1724 m.pointer_texcoord[1] = varray_texcoord3f[1];
1725 R_Mesh_State_Texture(&m);
1726 qglColorMask(0,0,0,1);
1727 // this squares the result
1728 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1729 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1730 R_Mesh_Draw(numverts, numtriangles, elements);
1732 c_rt_lighttris += numtriangles;
1734 memset(&m, 0, sizeof(m));
1735 R_Mesh_State_Texture(&m);
1736 // square alpha in framebuffer a few times to make it shiny
1737 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1738 // these comments are a test run through this math for intensity 0.5
1739 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1740 // 0.25 * 0.25 = 0.0625 (this is another pass)
1741 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1742 R_Mesh_Draw(numverts, numtriangles, elements);
1744 c_rt_lighttris += numtriangles;
1745 R_Mesh_Draw(numverts, numtriangles, elements);
1747 c_rt_lighttris += numtriangles;
1749 memset(&m, 0, sizeof(m));
1750 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1751 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1752 m.pointer_texcoord[0] = varray_texcoord2f[0];
1753 m.pointer_texcoord[1] = varray_texcoord2f[1];
1754 R_Mesh_State_Texture(&m);
1755 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1756 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1757 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1758 R_Mesh_Draw(numverts, numtriangles, elements);
1760 c_rt_lighttris += numtriangles;
1762 memset(&m, 0, sizeof(m));
1763 m.tex[0] = R_GetTexture(glosstexture);
1766 m.texcubemap[1] = R_GetTexture(lightcubemap);
1767 m.pointer_texcoord[1] = varray_texcoord3f[1];
1768 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1770 m.pointer_texcoord[0] = texcoord2f;
1771 R_Mesh_State_Texture(&m);
1772 qglColorMask(1,1,1,0);
1773 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1774 VectorScale(lightcolor, colorscale, color2);
1775 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1777 color[0] = bound(0, color2[0], 1);
1778 color[1] = bound(0, color2[1], 1);
1779 color[2] = bound(0, color2[2], 1);
1780 GL_Color(color[0], color[1], color[2], 1);
1781 R_Mesh_Draw(numverts, numtriangles, elements);
1783 c_rt_lighttris += numtriangles;
1789 void R_Shadow_DrawStaticWorldLight_Shadow(worldlight_t *light, matrix4x4_t *matrix)
1791 R_Mesh_Matrix(matrix);
1792 if (r_shadow_showtris.integer)
1796 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1797 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1798 qglDisable(GL_DEPTH_TEST);
1799 qglDisable(GL_STENCIL_TEST);
1800 //qglDisable(GL_CULL_FACE);
1801 qglColorMask(1,1,1,1);
1802 memset(&m, 0, sizeof(m));
1803 R_Mesh_State_Texture(&m);
1804 GL_Color(0,0.1,0,1);
1805 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1806 for (mesh = light->meshchain_shadow;mesh;mesh = mesh->next)
1808 GL_VertexPointer(mesh->vertex3f);
1809 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1811 //qglEnable(GL_CULL_FACE);
1813 qglEnable(GL_DEPTH_TEST);
1816 qglEnable(GL_STENCIL_TEST);
1817 qglColorMask(0,0,0,0);
1820 R_Shadow_RenderShadowMeshVolume(light->meshchain_shadow);
1823 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)
1826 R_Mesh_Matrix(matrix);
1827 if (r_shadow_showtris.integer)
1830 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
1831 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
1832 qglDisable(GL_DEPTH_TEST);
1833 qglDisable(GL_STENCIL_TEST);
1834 //qglDisable(GL_CULL_FACE);
1835 memset(&m, 0, sizeof(m));
1836 R_Mesh_State_Texture(&m);
1837 GL_Color(0.2,0,0,1);
1838 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1839 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1841 GL_VertexPointer(mesh->vertex3f);
1842 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
1844 //qglEnable(GL_CULL_FACE);
1846 qglEnable(GL_DEPTH_TEST);
1848 qglEnable(GL_STENCIL_TEST);
1850 for (mesh = light->meshchain_light;mesh;mesh = mesh->next)
1852 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);
1853 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);
1857 cvar_t r_editlights = {0, "r_editlights", "0"};
1858 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1859 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1860 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1861 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1862 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1863 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1864 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1865 worldlight_t *r_shadow_worldlightchain;
1866 worldlight_t *r_shadow_selectedlight;
1867 vec3_t r_editlights_cursorlocation;
1869 static int lightpvsbytes;
1870 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1871 static qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1873 typedef struct cubemapinfo_s
1876 rtexture_t *texture;
1880 #define MAX_CUBEMAPS 128
1881 static int numcubemaps;
1882 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
1884 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
1885 typedef struct suffixinfo_s
1888 int flipx, flipy, flipdiagonal;
1891 static suffixinfo_t suffix[3][6] =
1894 {"posx", false, false, false},
1895 {"negx", false, false, false},
1896 {"posy", false, false, false},
1897 {"negy", false, false, false},
1898 {"posz", false, false, false},
1899 {"negz", false, false, false}
1902 {"px", false, false, false},
1903 {"nx", false, false, false},
1904 {"py", false, false, false},
1905 {"ny", false, false, false},
1906 {"pz", false, false, false},
1907 {"nz", false, false, false}
1910 {"ft", true, false, true},
1911 {"bk", false, true, true},
1912 {"lf", true, true, false},
1913 {"rt", false, false, false},
1914 {"up", false, false, false},
1915 {"dn", false, false, false}
1919 static int componentorder[4] = {0, 1, 2, 3};
1921 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
1923 int i, j, cubemapsize;
1924 qbyte *cubemappixels, *image_rgba;
1925 rtexture_t *cubemaptexture;
1927 // must start 0 so the first loadimagepixels has no requested width/height
1929 cubemappixels = NULL;
1930 cubemaptexture = NULL;
1931 for (j = 0;j < 3 && !cubemappixels;j++)
1933 for (i = 0;i < 6;i++)
1935 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
1936 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
1938 if (image_width == image_height)
1940 if (!cubemappixels && image_width >= 1)
1942 cubemapsize = image_width;
1943 // note this clears to black, so unavailable sizes are black
1944 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
1947 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);
1950 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
1951 Mem_Free(image_rgba);
1957 if (!r_shadow_filters_texturepool)
1958 r_shadow_filters_texturepool = R_AllocTexturePool();
1959 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
1960 Mem_Free(cubemappixels);
1964 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
1965 for (j = 0;j < 3;j++)
1966 for (i = 0;i < 6;i++)
1967 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
1968 Con_Printf(" and was unable to find any of them.\n");
1970 return cubemaptexture;
1973 rtexture_t *R_Shadow_Cubemap(const char *basename)
1976 for (i = 0;i < numcubemaps;i++)
1977 if (!strcasecmp(cubemaps[i].basename, basename))
1978 return cubemaps[i].texture;
1979 if (i >= MAX_CUBEMAPS)
1982 strcpy(cubemaps[i].basename, basename);
1983 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
1984 return cubemaps[i].texture;
1987 void R_Shadow_FreeCubemaps(void)
1990 R_FreeTexturePool(&r_shadow_filters_texturepool);
1993 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)
1995 int i, j, k, l, maxverts = 256, tris;
1996 float *vertex3f = NULL, mins[3], maxs[3];
1998 shadowmesh_t *mesh, *castmesh = NULL;
2000 if (radius < 15 || DotProduct(color, color) < 0.03)
2002 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2006 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
2007 VectorCopy(origin, e->origin);
2008 VectorCopy(angles, e->angles);
2009 VectorCopy(color, e->color);
2012 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
2014 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
2017 e->drawshadows = shadowenable;
2020 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);
2021 Matrix4x4_Invert_Simple(&e->matrix_worldtolight, &e->matrix_lighttoworld);
2022 Matrix4x4_Concat(&e->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &e->matrix_worldtolight);
2023 Matrix4x4_Concat(&e->matrix_worldtoattenuationz, &matrix_attenuationz, &e->matrix_worldtolight);
2025 e->cullradius = e->radius;
2026 for (k = 0;k < 3;k++)
2028 mins[k] = e->origin[k] - e->radius;
2029 maxs[k] = e->origin[k] + e->radius;
2032 e->next = r_shadow_worldlightchain;
2033 r_shadow_worldlightchain = e;
2034 if (cubemapname && cubemapname[0])
2036 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
2037 strcpy(e->cubemapname, cubemapname);
2038 e->cubemap = R_Shadow_Cubemap(e->cubemapname);
2040 // FIXME: rewrite this to store ALL geometry into a cache in the light
2042 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2043 e->meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2046 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightfullpvs, sizeof(lightfullpvs));
2047 memset(lightpvs, 0, lightpvsbytes);
2048 if (cl.worldmodel->brushq3.num_leafs)
2053 // make a pvs that only includes things within the box
2054 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2055 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2056 SETPVSBIT(lightpvs, leaf->clusterindex);
2058 // make a cluster list for fast visibility checking during rendering
2059 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2060 if (CHECKPVSBIT(lightpvs, i))
2062 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2063 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2064 if (CHECKPVSBIT(lightpvs, i))
2065 e->clusterindices[e->numclusters++] = i;
2067 VectorCopy(e->origin, e->mins);
2068 VectorCopy(e->origin, e->maxs);
2069 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2070 face->lighttemp_castshadow = false;
2071 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
2073 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2075 for (k = 0;k < 3;k++)
2077 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2078 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2080 for (j = 0;j < leaf->numleaffaces;j++)
2082 face = leaf->firstleafface[j];
2083 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
2084 face->lighttemp_castshadow = true;
2089 // add surfaces to shadow casting mesh and light mesh
2090 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
2092 if (face->lighttemp_castshadow)
2094 face->lighttemp_castshadow = false;
2095 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
2098 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
2099 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
2100 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
2101 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);
2106 else if (cl.worldmodel->brushq1.num_leafs)
2110 VectorCopy(e->origin, e->mins);
2111 VectorCopy(e->origin, e->maxs);
2112 i = CL_PointQ1Contents(e->origin);
2114 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2115 surf->lighttemp_castshadow = false;
2117 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
2120 qbyte *bytesurfacepvs;
2122 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
2123 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
2125 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
2127 // make a pvs that only includes things within the box
2128 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2130 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2132 SETPVSBIT(lightpvs, leaf->clusterindex);
2133 for (k = 0;k < 3;k++)
2135 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2136 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2141 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
2142 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2143 surf->lighttemp_castshadow = true;
2145 Mem_Free(byteleafpvs);
2146 Mem_Free(bytesurfacepvs);
2148 // make a cluster list for fast visibility checking during rendering
2149 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2150 if (CHECKPVSBIT(lightpvs, i))
2152 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2153 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2154 if (CHECKPVSBIT(lightpvs, i))
2155 e->clusterindices[e->numclusters++] = i;
2159 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2161 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2163 // make a pvs that only includes things within the box
2164 SETPVSBIT(lightpvs, leaf->clusterindex);
2165 for (k = 0;k < 3;k++)
2167 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
2168 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
2170 for (j = 0;j < leaf->nummarksurfaces;j++)
2172 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
2173 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
2174 surf->lighttemp_castshadow = true;
2179 // make a pvs that only includes things within the box
2180 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
2181 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
2182 SETPVSBIT(lightpvs, leaf->clusterindex);
2184 // make a cluster list for fast visibility checking during rendering
2185 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2186 if (CHECKPVSBIT(lightpvs, i))
2188 e->clusterindices = Mem_Alloc(r_shadow_mempool, e->numclusters * sizeof(int));
2189 for (i = 0, e->numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
2190 if (CHECKPVSBIT(lightpvs, i))
2191 e->clusterindices[e->numclusters++] = i;
2194 // add surfaces to shadow casting mesh and light mesh
2195 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
2197 if (surf->lighttemp_castshadow)
2199 surf->lighttemp_castshadow = false;
2200 if (e->drawshadows && (surf->flags & SURF_SHADOWCAST))
2201 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);
2202 if (!(surf->flags & SURF_DRAWSKY))
2203 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);
2209 // limit box to light bounds (in case it grew larger)
2210 for (k = 0;k < 3;k++)
2212 if (e->mins[k] < e->origin[k] - e->radius) e->mins[k] = e->origin[k] - e->radius;
2213 if (e->maxs[k] > e->origin[k] + e->radius) e->maxs[k] = e->origin[k] + e->radius;
2215 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
2217 // cast shadow volume from castmesh
2218 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
2222 for (mesh = castmesh;mesh;mesh = mesh->next)
2224 R_Shadow_ResizeShadowElements(mesh->numtriangles);
2225 maxverts = max(maxverts, mesh->numverts * 2);
2230 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2231 // now that we have the buffers big enough, construct and add
2232 // the shadow volume mesh
2234 e->meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2235 for (mesh = castmesh;mesh;mesh = mesh->next)
2237 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2238 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)))
2239 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2244 // we're done with castmesh now
2245 Mod_ShadowMesh_Free(castmesh);
2248 e->meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_shadow, false, false);
2249 e->meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, e->meshchain_light, true, false);
2252 if (e->meshchain_shadow)
2253 for (mesh = e->meshchain_shadow;mesh;mesh = mesh->next)
2254 k += mesh->numtriangles;
2256 if (e->meshchain_light)
2257 for (mesh = e->meshchain_light;mesh;mesh = mesh->next)
2258 l += mesh->numtriangles;
2259 Con_DPrintf("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);
2262 void R_Shadow_FreeWorldLight(worldlight_t *light)
2264 worldlight_t **lightpointer;
2265 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2266 if (*lightpointer != light)
2267 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2268 *lightpointer = light->next;
2269 if (light->cubemapname)
2270 Mem_Free(light->cubemapname);
2271 if (light->meshchain_shadow)
2272 Mod_ShadowMesh_Free(light->meshchain_shadow);
2273 if (light->meshchain_light)
2274 Mod_ShadowMesh_Free(light->meshchain_light);
2278 void R_Shadow_ClearWorldLights(void)
2280 while (r_shadow_worldlightchain)
2281 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2282 r_shadow_selectedlight = NULL;
2283 R_Shadow_FreeCubemaps();
2286 void R_Shadow_SelectLight(worldlight_t *light)
2288 if (r_shadow_selectedlight)
2289 r_shadow_selectedlight->selected = false;
2290 r_shadow_selectedlight = light;
2291 if (r_shadow_selectedlight)
2292 r_shadow_selectedlight->selected = true;
2295 rtexture_t *lighttextures[5];
2297 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2299 float scale = r_editlights_cursorgrid.value * 0.5f;
2300 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);
2303 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2306 const worldlight_t *light;
2309 if (light->selected)
2310 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2311 if (!light->meshchain_shadow)
2313 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);
2316 void R_Shadow_DrawLightSprites(void)
2320 worldlight_t *light;
2322 for (i = 0;i < 5;i++)
2324 lighttextures[i] = NULL;
2325 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2326 lighttextures[i] = pic->tex;
2329 for (light = r_shadow_worldlightchain;light;light = light->next)
2330 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2331 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2334 void R_Shadow_SelectLightInView(void)
2336 float bestrating, rating, temp[3];
2337 worldlight_t *best, *light;
2340 for (light = r_shadow_worldlightchain;light;light = light->next)
2342 VectorSubtract(light->origin, r_vieworigin, temp);
2343 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2346 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2347 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2349 bestrating = rating;
2354 R_Shadow_SelectLight(best);
2357 void R_Shadow_LoadWorldLights(void)
2359 int n, a, style, shadow;
2360 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2361 float origin[3], radius, color[3], angles[3], corona;
2362 if (cl.worldmodel == NULL)
2364 Con_Printf("No map loaded.\n");
2367 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2368 strlcat (name, ".rtlights", sizeof (name));
2369 lightsstring = FS_LoadFile(name, false);
2377 while (*s && *s != '\n')
2383 // check for modifier flags
2389 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]);
2393 VectorClear(angles);
2400 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);
2403 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2404 radius *= r_editlights_rtlightssizescale.value;
2405 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2410 Con_Printf("invalid rtlights file \"%s\"\n", name);
2411 Mem_Free(lightsstring);
2415 void R_Shadow_SaveWorldLights(void)
2417 worldlight_t *light;
2418 int bufchars, bufmaxchars;
2420 char name[MAX_QPATH];
2422 if (!r_shadow_worldlightchain)
2424 if (cl.worldmodel == NULL)
2426 Con_Printf("No map loaded.\n");
2429 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2430 strlcat (name, ".rtlights", sizeof (name));
2431 bufchars = bufmaxchars = 0;
2433 for (light = r_shadow_worldlightchain;light;light = light->next)
2435 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 : "");
2436 if (bufchars + (int) strlen(line) > bufmaxchars)
2438 bufmaxchars = bufchars + strlen(line) + 2048;
2440 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2444 memcpy(buf, oldbuf, bufchars);
2450 memcpy(buf + bufchars, line, strlen(line));
2451 bufchars += strlen(line);
2455 FS_WriteFile(name, buf, bufchars);
2460 void R_Shadow_LoadLightsFile(void)
2463 char name[MAX_QPATH], *lightsstring, *s, *t;
2464 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2465 if (cl.worldmodel == NULL)
2467 Con_Printf("No map loaded.\n");
2470 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2471 strlcat (name, ".lights", sizeof (name));
2472 lightsstring = FS_LoadFile(name, false);
2480 while (*s && *s != '\n')
2485 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);
2489 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);
2492 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2493 radius = bound(15, radius, 4096);
2494 VectorScale(color, (2.0f / (8388608.0f)), color);
2495 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2500 Con_Printf("invalid lights file \"%s\"\n", name);
2501 Mem_Free(lightsstring);
2505 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2507 int entnum, style, islight, skin, pflags;
2508 char key[256], value[1024];
2509 float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2512 if (cl.worldmodel == NULL)
2514 Con_Printf("No map loaded.\n");
2517 data = cl.worldmodel->brush.entities;
2520 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2523 origin[0] = origin[1] = origin[2] = 0;
2524 originhack[0] = originhack[1] = originhack[2] = 0;
2525 angles[0] = angles[1] = angles[2] = 0;
2526 color[0] = color[1] = color[2] = 1;
2527 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2536 if (!COM_ParseToken(&data, false))
2538 if (com_token[0] == '}')
2539 break; // end of entity
2540 if (com_token[0] == '_')
2541 strcpy(key, com_token + 1);
2543 strcpy(key, com_token);
2544 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2545 key[strlen(key)-1] = 0;
2546 if (!COM_ParseToken(&data, false))
2548 strcpy(value, com_token);
2550 // now that we have the key pair worked out...
2551 if (!strcmp("light", key))
2552 light = atof(value);
2553 else if (!strcmp("origin", key))
2554 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2555 else if (!strcmp("angle", key))
2556 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2557 else if (!strcmp("angles", key))
2558 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2559 else if (!strcmp("color", key))
2560 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2561 else if (!strcmp("wait", key))
2562 fadescale = atof(value);
2563 else if (!strcmp("classname", key))
2565 if (!strncmp(value, "light", 5))
2568 if (!strcmp(value, "light_fluoro"))
2573 overridecolor[0] = 1;
2574 overridecolor[1] = 1;
2575 overridecolor[2] = 1;
2577 if (!strcmp(value, "light_fluorospark"))
2582 overridecolor[0] = 1;
2583 overridecolor[1] = 1;
2584 overridecolor[2] = 1;
2586 if (!strcmp(value, "light_globe"))
2591 overridecolor[0] = 1;
2592 overridecolor[1] = 0.8;
2593 overridecolor[2] = 0.4;
2595 if (!strcmp(value, "light_flame_large_yellow"))
2600 overridecolor[0] = 1;
2601 overridecolor[1] = 0.5;
2602 overridecolor[2] = 0.1;
2604 if (!strcmp(value, "light_flame_small_yellow"))
2609 overridecolor[0] = 1;
2610 overridecolor[1] = 0.5;
2611 overridecolor[2] = 0.1;
2613 if (!strcmp(value, "light_torch_small_white"))
2618 overridecolor[0] = 1;
2619 overridecolor[1] = 0.5;
2620 overridecolor[2] = 0.1;
2622 if (!strcmp(value, "light_torch_small_walltorch"))
2627 overridecolor[0] = 1;
2628 overridecolor[1] = 0.5;
2629 overridecolor[2] = 0.1;
2633 else if (!strcmp("style", key))
2634 style = atoi(value);
2635 else if (cl.worldmodel->type == mod_brushq3)
2637 if (!strcmp("scale", key))
2638 lightscale = atof(value);
2639 if (!strcmp("fade", key))
2640 fadescale = atof(value);
2642 else if (!strcmp("skin", key))
2643 skin = (int)atof(value);
2644 else if (!strcmp("pflags", key))
2645 pflags = (int)atof(value);
2647 if (light <= 0 && islight)
2649 if (lightscale <= 0)
2653 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2654 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2655 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2656 VectorCopy(overridecolor, color);
2657 VectorScale(color, light, color);
2658 VectorAdd(origin, originhack, origin);
2660 R_Shadow_NewWorldLight(origin, angles, color, radius, !!(pflags & 2), style, !(pflags & 1), skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2665 void R_Shadow_SetCursorLocationForView(void)
2667 vec_t dist, push, frac;
2668 vec3_t dest, endpos, normal;
2669 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2670 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2673 dist = frac * r_editlights_cursordistance.value;
2674 push = r_editlights_cursorpushback.value;
2678 VectorMA(endpos, push, r_viewforward, endpos);
2679 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2681 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2682 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2683 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2686 void R_Shadow_UpdateWorldLightSelection(void)
2688 if (r_editlights.integer)
2690 R_Shadow_SetCursorLocationForView();
2691 R_Shadow_SelectLightInView();
2692 R_Shadow_DrawLightSprites();
2695 R_Shadow_SelectLight(NULL);
2698 void R_Shadow_EditLights_Clear_f(void)
2700 R_Shadow_ClearWorldLights();
2703 void R_Shadow_EditLights_Reload_f(void)
2705 r_shadow_reloadlights = true;
2708 void R_Shadow_EditLights_Save_f(void)
2711 R_Shadow_SaveWorldLights();
2714 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2716 R_Shadow_ClearWorldLights();
2717 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2720 void R_Shadow_EditLights_ImportLightsFile_f(void)
2722 R_Shadow_ClearWorldLights();
2723 R_Shadow_LoadLightsFile();
2726 void R_Shadow_EditLights_Spawn_f(void)
2729 if (!r_editlights.integer)
2731 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2734 if (Cmd_Argc() != 1)
2736 Con_Printf("r_editlights_spawn does not take parameters\n");
2739 color[0] = color[1] = color[2] = 1;
2740 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2743 void R_Shadow_EditLights_Edit_f(void)
2745 vec3_t origin, angles, color;
2746 vec_t radius, corona;
2748 char cubemapname[1024];
2749 if (!r_editlights.integer)
2751 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2754 if (!r_shadow_selectedlight)
2756 Con_Printf("No selected light.\n");
2759 VectorCopy(r_shadow_selectedlight->origin, origin);
2760 VectorCopy(r_shadow_selectedlight->angles, angles);
2761 VectorCopy(r_shadow_selectedlight->color, color);
2762 radius = r_shadow_selectedlight->radius;
2763 style = r_shadow_selectedlight->style;
2764 if (r_shadow_selectedlight->cubemapname)
2765 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2768 shadows = r_shadow_selectedlight->drawshadows;
2769 corona = r_shadow_selectedlight->corona;
2770 if (!strcmp(Cmd_Argv(1), "origin"))
2772 if (Cmd_Argc() != 5)
2774 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2777 origin[0] = atof(Cmd_Argv(2));
2778 origin[1] = atof(Cmd_Argv(3));
2779 origin[2] = atof(Cmd_Argv(4));
2781 else if (!strcmp(Cmd_Argv(1), "originx"))
2783 if (Cmd_Argc() != 3)
2785 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2788 origin[0] = atof(Cmd_Argv(2));
2790 else if (!strcmp(Cmd_Argv(1), "originy"))
2792 if (Cmd_Argc() != 3)
2794 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2797 origin[1] = atof(Cmd_Argv(2));
2799 else if (!strcmp(Cmd_Argv(1), "originz"))
2801 if (Cmd_Argc() != 3)
2803 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2806 origin[2] = atof(Cmd_Argv(2));
2808 else if (!strcmp(Cmd_Argv(1), "move"))
2810 if (Cmd_Argc() != 5)
2812 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2815 origin[0] += atof(Cmd_Argv(2));
2816 origin[1] += atof(Cmd_Argv(3));
2817 origin[2] += atof(Cmd_Argv(4));
2819 else if (!strcmp(Cmd_Argv(1), "movex"))
2821 if (Cmd_Argc() != 3)
2823 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2826 origin[0] += atof(Cmd_Argv(2));
2828 else if (!strcmp(Cmd_Argv(1), "movey"))
2830 if (Cmd_Argc() != 3)
2832 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2835 origin[1] += atof(Cmd_Argv(2));
2837 else if (!strcmp(Cmd_Argv(1), "movez"))
2839 if (Cmd_Argc() != 3)
2841 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2844 origin[2] += atof(Cmd_Argv(2));
2846 if (!strcmp(Cmd_Argv(1), "angles"))
2848 if (Cmd_Argc() != 5)
2850 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2853 angles[0] = atof(Cmd_Argv(2));
2854 angles[1] = atof(Cmd_Argv(3));
2855 angles[2] = atof(Cmd_Argv(4));
2857 else if (!strcmp(Cmd_Argv(1), "anglesx"))
2859 if (Cmd_Argc() != 3)
2861 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2864 angles[0] = atof(Cmd_Argv(2));
2866 else if (!strcmp(Cmd_Argv(1), "anglesy"))
2868 if (Cmd_Argc() != 3)
2870 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2873 angles[1] = atof(Cmd_Argv(2));
2875 else if (!strcmp(Cmd_Argv(1), "anglesz"))
2877 if (Cmd_Argc() != 3)
2879 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2882 angles[2] = atof(Cmd_Argv(2));
2884 else if (!strcmp(Cmd_Argv(1), "color"))
2886 if (Cmd_Argc() != 5)
2888 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
2891 color[0] = atof(Cmd_Argv(2));
2892 color[1] = atof(Cmd_Argv(3));
2893 color[2] = atof(Cmd_Argv(4));
2895 else if (!strcmp(Cmd_Argv(1), "radius"))
2897 if (Cmd_Argc() != 3)
2899 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2902 radius = atof(Cmd_Argv(2));
2904 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2906 if (Cmd_Argc() != 3)
2908 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2911 style = atoi(Cmd_Argv(2));
2913 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2917 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2920 if (Cmd_Argc() == 3)
2921 strcpy(cubemapname, Cmd_Argv(2));
2925 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2927 if (Cmd_Argc() != 3)
2929 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2932 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2934 else if (!strcmp(Cmd_Argv(1), "corona"))
2936 if (Cmd_Argc() != 3)
2938 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2941 corona = atof(Cmd_Argv(2));
2945 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2946 Con_Printf("Selected light's properties:\n");
2947 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2948 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
2949 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
2950 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
2951 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
2952 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
2953 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->drawshadows ? "yes" : "no");
2954 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2957 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2958 r_shadow_selectedlight = NULL;
2959 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
2962 extern int con_vislines;
2963 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2967 if (r_shadow_selectedlight == NULL)
2971 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2972 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;
2973 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;
2974 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;
2975 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2976 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2977 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2978 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;
2979 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2982 void R_Shadow_EditLights_ToggleShadow_f(void)
2984 if (!r_editlights.integer)
2986 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2989 if (!r_shadow_selectedlight)
2991 Con_Printf("No selected light.\n");
2994 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);
2995 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2996 r_shadow_selectedlight = NULL;
2999 void R_Shadow_EditLights_ToggleCorona_f(void)
3001 if (!r_editlights.integer)
3003 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3006 if (!r_shadow_selectedlight)
3008 Con_Printf("No selected light.\n");
3011 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);
3012 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3013 r_shadow_selectedlight = NULL;
3016 void R_Shadow_EditLights_Remove_f(void)
3018 if (!r_editlights.integer)
3020 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3023 if (!r_shadow_selectedlight)
3025 Con_Printf("No selected light.\n");
3028 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3029 r_shadow_selectedlight = NULL;
3032 void R_Shadow_EditLights_Help_f(void)
3035 "Documentation on r_editlights system:\n"
3037 "r_editlights : enable/disable editing mode\n"
3038 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3039 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3040 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3041 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3042 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3043 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3044 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3046 "r_editlights_help : this help\n"
3047 "r_editlights_clear : remove all lights\n"
3048 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3049 "r_editlights_save : save to .rtlights file\n"
3050 "r_editlights_spawn : create a light with default settings\n"
3051 "r_editlights_edit command : edit selected light - more documentation below\n"
3052 "r_editlights_remove : remove selected light\n"
3053 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3054 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3055 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3057 "origin x y z : set light location\n"
3058 "originx x: set x component of light location\n"
3059 "originy y: set y component of light location\n"
3060 "originz z: set z component of light location\n"
3061 "move x y z : adjust light location\n"
3062 "movex x: adjust x component of light location\n"
3063 "movey y: adjust y component of light location\n"
3064 "movez z: adjust z component of light location\n"
3065 "angles x y z : set light angles\n"
3066 "anglesx x: set x component of light angles\n"
3067 "anglesy y: set y component of light angles\n"
3068 "anglesz z: set z component of light angles\n"
3069 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3070 "radius radius : set radius (size) of light\n"
3071 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3072 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3073 "shadows 1/0 : turn on/off shadows\n"
3074 "corona n : set corona intensity\n"
3075 "<nothing> : print light properties to console\n"
3079 void R_Shadow_EditLights_Init(void)
3081 Cvar_RegisterVariable(&r_editlights);
3082 Cvar_RegisterVariable(&r_editlights_cursordistance);
3083 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3084 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3085 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3086 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3087 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3088 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3089 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3090 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3091 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3092 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3093 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3094 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3095 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3096 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3097 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3098 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3099 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);