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).
21 Terminology: Stencil Light Volume (sometimes called Light Volumes)
22 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
23 areas in shadow it contanis the areas in light, this can only be built
24 quickly for certain limited cases (such as portal visibility from a point),
25 but is quite useful for some effects (sunlight coming from sky polygons is
26 one possible example, translucent occluders is another example).
30 Terminology: Optimized Stencil Shadow Volume
31 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
32 no duplicate coverage of areas (no need to shadow an area twice), often this
33 greatly improves performance but is an operation too costly to use on moving
34 lights (however completely optimal Stencil Light Volumes can be constructed
39 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
40 Per pixel evaluation of lighting equations, at a bare minimum this involves
41 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
42 vector and surface normal, using a texture of the surface bumps, called a
43 NormalMap) if supported by hardware; in our case there is support for cards
44 which are incapable of DOT3, the quality is quite poor however. Additionally
45 it is desirable to have specular evaluation per pixel, per vertex
46 normalization of specular halfangle vectors causes noticable distortion but
47 is unavoidable on hardware without GL_ARB_fragment_program.
51 Terminology: Normalization CubeMap
52 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
53 encoded as RGB colors) for any possible direction, this technique allows per
54 pixel calculation of incidence vector for per pixel lighting purposes, which
55 would not otherwise be possible per pixel without GL_ARB_fragment_program.
59 Terminology: 2D Attenuation Texturing
60 A very crude approximation of light attenuation with distance which results
61 in cylindrical light shapes which fade vertically as a streak (some games
62 such as Doom3 allow this to be rotated to be less noticable in specific
63 cases), the technique is simply modulating lighting by two 2D textures (which
64 can be the same) on different axes of projection (XY and Z, typically), this
65 is the best technique available without 3D Attenuation Texturing or
66 GL_ARB_fragment_program technology.
70 Terminology: 3D Attenuation Texturing
71 A slightly crude approximation of light attenuation with distance, its flaws
72 are limited radius and resolution (performance tradeoffs).
76 Terminology: 3D Attenuation-Normalization Texturing
77 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
78 vectors shorter the lighting becomes darker, a very effective optimization of
79 diffuse lighting if 3D Attenuation Textures are already used.
83 Terminology: Light Cubemap Filtering
84 A technique for modeling non-uniform light distribution according to
85 direction, for example projecting a stained glass window image onto a wall,
86 this is done by texturing the lighting with a cubemap.
90 Terminology: Light Projection Filtering
91 A technique for modeling shadowing of light passing through translucent
92 surfaces, allowing stained glass windows and other effects to be done more
93 elegantly than possible with Light Cubemap Filtering by applying an occluder
94 texture to the lighting combined with a stencil light volume to limit the lit
95 area (this allows evaluating multiple translucent occluders in a scene).
99 Terminology: Doom3 Lighting
100 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
101 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
102 the (currently upcoming) game Doom3.
105 #include "quakedef.h"
106 #include "r_shadow.h"
107 #include "cl_collision.h"
110 extern void R_Shadow_EditLights_Init(void);
112 #define SHADOWSTAGE_NONE 0
113 #define SHADOWSTAGE_STENCIL 1
114 #define SHADOWSTAGE_LIGHT 2
115 #define SHADOWSTAGE_ERASESTENCIL 3
117 int r_shadowstage = SHADOWSTAGE_NONE;
118 int r_shadow_reloadlights = false;
120 mempool_t *r_shadow_mempool;
122 int maxshadowelements;
124 int maxtrianglefacinglight;
125 qbyte *trianglefacinglight;
126 int *trianglefacinglightlist;
133 rtexturepool_t *r_shadow_texturepool;
134 rtexture_t *r_shadow_normalcubetexture;
135 rtexture_t *r_shadow_attenuation2dtexture;
136 rtexture_t *r_shadow_attenuation3dtexture;
137 rtexture_t *r_shadow_blankbumptexture;
138 rtexture_t *r_shadow_blankglosstexture;
139 rtexture_t *r_shadow_blankwhitetexture;
141 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
142 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
143 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
144 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
145 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
146 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
147 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
148 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
149 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
150 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
151 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
152 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
153 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
154 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
155 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
156 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
158 int c_rt_lights, c_rt_clears, c_rt_scissored;
159 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
160 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
162 void R_Shadow_ClearWorldLights(void);
163 void R_Shadow_SaveWorldLights(void);
164 void R_Shadow_LoadWorldLights(void);
165 void R_Shadow_LoadLightsFile(void);
166 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
168 void r_shadow_start(void)
170 // allocate vertex processing arrays
171 r_shadow_mempool = Mem_AllocPool("R_Shadow");
172 maxshadowelements = 0;
173 shadowelements = NULL;
178 maxtrianglefacinglight = 0;
179 trianglefacinglight = NULL;
180 trianglefacinglightlist = NULL;
181 r_shadow_normalcubetexture = NULL;
182 r_shadow_attenuation2dtexture = NULL;
183 r_shadow_attenuation3dtexture = NULL;
184 r_shadow_blankbumptexture = NULL;
185 r_shadow_blankglosstexture = NULL;
186 r_shadow_blankwhitetexture = NULL;
187 r_shadow_texturepool = NULL;
188 R_Shadow_ClearWorldLights();
189 r_shadow_reloadlights = true;
192 void r_shadow_shutdown(void)
194 R_Shadow_ClearWorldLights();
195 r_shadow_reloadlights = true;
196 r_shadow_normalcubetexture = NULL;
197 r_shadow_attenuation2dtexture = NULL;
198 r_shadow_attenuation3dtexture = NULL;
199 r_shadow_blankbumptexture = NULL;
200 r_shadow_blankglosstexture = NULL;
201 r_shadow_blankwhitetexture = NULL;
202 R_FreeTexturePool(&r_shadow_texturepool);
203 maxshadowelements = 0;
204 shadowelements = NULL;
209 maxtrianglefacinglight = 0;
210 trianglefacinglight = NULL;
211 trianglefacinglightlist = NULL;
212 Mem_FreePool(&r_shadow_mempool);
215 void r_shadow_newmap(void)
217 R_Shadow_ClearWorldLights();
218 r_shadow_reloadlights = true;
221 void R_Shadow_Init(void)
223 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
224 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
225 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
226 Cvar_RegisterVariable(&r_shadow_realtime_world);
227 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
228 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
229 Cvar_RegisterVariable(&r_shadow_gloss);
230 Cvar_RegisterVariable(&r_shadow_debuglight);
231 Cvar_RegisterVariable(&r_shadow_scissor);
232 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
233 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
234 Cvar_RegisterVariable(&r_shadow_polygonoffset);
235 Cvar_RegisterVariable(&r_shadow_portallight);
236 Cvar_RegisterVariable(&r_shadow_projectdistance);
237 Cvar_RegisterVariable(&r_shadow_texture3d);
238 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
239 R_Shadow_EditLights_Init();
240 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
243 void R_Shadow_ResizeTriangleFacingLight(int numtris)
245 // make sure trianglefacinglight is big enough for this volume
246 // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
247 // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
248 if (maxtrianglefacinglight < numtris)
250 maxtrianglefacinglight = numtris;
251 if (trianglefacinglight)
252 Mem_Free(trianglefacinglight);
253 if (trianglefacinglightlist)
254 Mem_Free(trianglefacinglightlist);
255 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
256 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
260 int *R_Shadow_ResizeShadowElements(int numtris)
262 // make sure shadowelements is big enough for this volume
263 if (maxshadowelements < numtris * 24)
265 maxshadowelements = numtris * 24;
267 Mem_Free(shadowelements);
268 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
270 return shadowelements;
274 // readable version of some code found below
275 int checkcastshadowfromedge(int t, int i)
279 if (t >= trianglerange_start && t < trianglerange_end)
281 if (t < i && !trianglefacinglight[t])
292 te = inelement3i + t * 3;
293 v[0] = invertex3f + te[0] * 3;
294 v[1] = invertex3f + te[1] * 3;
295 v[2] = invertex3f + te[2] * 3;
296 if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
305 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)
307 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
309 const int *e, *n, *te;
312 // make sure trianglefacinglight is big enough for this volume
313 if (maxtrianglefacinglight < trianglerange_end)
314 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
316 if (maxvertexupdate < innumvertices)
318 maxvertexupdate = innumvertices;
320 Mem_Free(vertexupdate);
322 Mem_Free(vertexremap);
323 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
324 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
328 if (r_shadow_singlepassvolumegeneration.integer)
330 // one pass approach (identify lit/dark faces and generate sides while doing so)
331 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
333 // calculate triangle facing flag
334 v[0] = invertex3f + e[0] * 3;
335 v[1] = invertex3f + e[1] * 3;
336 v[2] = invertex3f + e[2] * 3;
337 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
339 // make sure the vertices are created
340 for (j = 0;j < 3;j++)
342 if (vertexupdate[e[j]] != vertexupdatenum)
344 vertexupdate[e[j]] = vertexupdatenum;
345 vertexremap[e[j]] = outvertices;
346 VectorCopy(v[j], outvertex3f);
347 VectorSubtract(v[j], relativelightorigin, temp);
348 f = projectdistance / VectorLength(temp);
349 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
354 // output the front and back triangles
355 vr[0] = vertexremap[e[0]];
356 vr[1] = vertexremap[e[1]];
357 vr[2] = vertexremap[e[2]];
358 outelement3i[0] = vr[0];
359 outelement3i[1] = vr[1];
360 outelement3i[2] = vr[2];
361 outelement3i[3] = vr[2] + 1;
362 outelement3i[4] = vr[1] + 1;
363 outelement3i[5] = vr[0] + 1;
366 // output the sides (facing outward from this triangle)
368 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]))))
370 outelement3i[0] = vr[1];
371 outelement3i[1] = vr[0];
372 outelement3i[2] = vr[0] + 1;
373 outelement3i[3] = vr[1];
374 outelement3i[4] = vr[0] + 1;
375 outelement3i[5] = vr[1] + 1;
380 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]))))
382 outelement3i[0] = vr[2];
383 outelement3i[1] = vr[1];
384 outelement3i[2] = vr[1] + 1;
385 outelement3i[3] = vr[2];
386 outelement3i[4] = vr[1] + 1;
387 outelement3i[5] = vr[2] + 1;
392 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]))))
394 outelement3i[0] = vr[0];
395 outelement3i[1] = vr[2];
396 outelement3i[2] = vr[2] + 1;
397 outelement3i[3] = vr[0];
398 outelement3i[4] = vr[2] + 1;
399 outelement3i[5] = vr[0] + 1;
406 // this triangle is not facing the light
407 // output the sides (facing inward to this triangle)
409 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
411 vr[0] = vertexremap[e[0]];
412 vr[1] = vertexremap[e[1]];
413 outelement3i[0] = vr[1];
414 outelement3i[1] = vr[0] + 1;
415 outelement3i[2] = vr[0];
416 outelement3i[3] = vr[1];
417 outelement3i[4] = vr[1] + 1;
418 outelement3i[5] = vr[0] + 1;
423 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
425 vr[1] = vertexremap[e[1]];
426 vr[2] = vertexremap[e[2]];
427 outelement3i[0] = vr[2];
428 outelement3i[1] = vr[1] + 1;
429 outelement3i[2] = vr[1];
430 outelement3i[3] = vr[2];
431 outelement3i[4] = vr[2] + 1;
432 outelement3i[5] = vr[1] + 1;
437 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
439 vr[0] = vertexremap[e[0]];
440 vr[2] = vertexremap[e[2]];
441 outelement3i[0] = vr[0];
442 outelement3i[1] = vr[2] + 1;
443 outelement3i[2] = vr[2];
444 outelement3i[3] = vr[0];
445 outelement3i[4] = vr[0] + 1;
446 outelement3i[5] = vr[2] + 1;
455 // two pass approach (identify lit/dark faces and then generate sides)
456 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
458 // calculate triangle facing flag
459 v[0] = invertex3f + e[0] * 3;
460 v[1] = invertex3f + e[1] * 3;
461 v[2] = invertex3f + e[2] * 3;
462 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
464 trianglefacinglightlist[numfacing++] = i;
465 // make sure the vertices are created
466 for (j = 0;j < 3;j++)
468 if (vertexupdate[e[j]] != vertexupdatenum)
470 vertexupdate[e[j]] = vertexupdatenum;
471 vertexremap[e[j]] = outvertices;
472 VectorSubtract(v[j], relativelightorigin, temp);
473 f = projectdistance / VectorLength(temp);
474 VectorCopy(v[j], outvertex3f);
475 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
480 // output the front and back triangles
481 outelement3i[0] = vertexremap[e[0]];
482 outelement3i[1] = vertexremap[e[1]];
483 outelement3i[2] = vertexremap[e[2]];
484 outelement3i[3] = vertexremap[e[2]] + 1;
485 outelement3i[4] = vertexremap[e[1]] + 1;
486 outelement3i[5] = vertexremap[e[0]] + 1;
491 for (i = 0;i < numfacing;i++)
493 t = trianglefacinglightlist[i];
494 e = inelement3i + t * 3;
495 n = inneighbor3i + t * 3;
496 // output the sides (facing outward from this triangle)
498 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]))))
500 vr[0] = vertexremap[e[0]];
501 vr[1] = vertexremap[e[1]];
502 outelement3i[0] = vr[1];
503 outelement3i[1] = vr[0];
504 outelement3i[2] = vr[0] + 1;
505 outelement3i[3] = vr[1];
506 outelement3i[4] = vr[0] + 1;
507 outelement3i[5] = vr[1] + 1;
512 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]))))
514 vr[1] = vertexremap[e[1]];
515 vr[2] = vertexremap[e[2]];
516 outelement3i[0] = vr[2];
517 outelement3i[1] = vr[1];
518 outelement3i[2] = vr[1] + 1;
519 outelement3i[3] = vr[2];
520 outelement3i[4] = vr[1] + 1;
521 outelement3i[5] = vr[2] + 1;
526 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]))))
528 vr[0] = vertexremap[e[0]];
529 vr[2] = vertexremap[e[2]];
530 outelement3i[0] = vr[0];
531 outelement3i[1] = vr[2];
532 outelement3i[2] = vr[2] + 1;
533 outelement3i[3] = vr[0];
534 outelement3i[4] = vr[2] + 1;
535 outelement3i[5] = vr[0] + 1;
542 *outnumvertices = outvertices;
546 float varray_vertex3f2[65536*3];
548 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
551 if (projectdistance < 0.1)
553 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
559 // make sure shadowelements is big enough for this volume
560 if (maxshadowelements < numtris * 24)
561 R_Shadow_ResizeShadowElements(numtris);
563 // check which triangles are facing the light, and then output
564 // triangle elements and vertices... by clever use of elements we
565 // can construct the whole shadow from the unprojected vertices and
566 // the projected vertices
567 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
569 GL_VertexPointer(varray_vertex3f2);
570 if (r_shadowstage == SHADOWSTAGE_STENCIL)
572 // increment stencil if backface is behind depthbuffer
573 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
574 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
575 R_Mesh_Draw(outverts, tris, shadowelements);
577 c_rt_shadowtris += numtris;
578 // decrement stencil if frontface is behind depthbuffer
579 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
580 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
582 R_Mesh_Draw(outverts, tris, shadowelements);
584 c_rt_shadowtris += numtris;
588 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
591 if (r_shadowstage == SHADOWSTAGE_STENCIL)
593 // increment stencil if backface is behind depthbuffer
594 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
595 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
596 for (mesh = firstmesh;mesh;mesh = mesh->next)
598 GL_VertexPointer(mesh->vertex3f);
599 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
600 c_rtcached_shadowmeshes++;
601 c_rtcached_shadowtris += mesh->numtriangles;
603 // decrement stencil if frontface is behind depthbuffer
604 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
605 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
607 for (mesh = firstmesh;mesh;mesh = mesh->next)
609 GL_VertexPointer(mesh->vertex3f);
610 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
611 c_rtcached_shadowmeshes++;
612 c_rtcached_shadowtris += mesh->numtriangles;
616 float r_shadow_attenpower, r_shadow_attenscale;
617 static void R_Shadow_MakeTextures(void)
619 int x, y, z, d, side;
620 float v[3], s, t, intensity;
622 R_FreeTexturePool(&r_shadow_texturepool);
623 r_shadow_texturepool = R_AllocTexturePool();
624 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
625 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
627 #define ATTEN2DSIZE 64
628 #define ATTEN3DSIZE 32
629 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
634 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
639 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
644 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
645 if (gl_texturecubemap)
647 for (side = 0;side < 6;side++)
649 for (y = 0;y < NORMSIZE;y++)
651 for (x = 0;x < NORMSIZE;x++)
653 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
654 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
688 intensity = 127.0f / sqrt(DotProduct(v, v));
689 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
690 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
691 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
692 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
696 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
699 r_shadow_normalcubetexture = NULL;
700 for (y = 0;y < ATTEN2DSIZE;y++)
702 for (x = 0;x < ATTEN2DSIZE;x++)
704 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
705 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
707 intensity = 1.0f - sqrt(DotProduct(v, v));
709 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
710 d = bound(0, intensity, 255);
711 data[(y*ATTEN2DSIZE+x)*4+0] = d;
712 data[(y*ATTEN2DSIZE+x)*4+1] = d;
713 data[(y*ATTEN2DSIZE+x)*4+2] = d;
714 data[(y*ATTEN2DSIZE+x)*4+3] = d;
717 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
718 if (r_shadow_texture3d.integer)
720 for (z = 0;z < ATTEN3DSIZE;z++)
722 for (y = 0;y < ATTEN3DSIZE;y++)
724 for (x = 0;x < ATTEN3DSIZE;x++)
726 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
727 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
728 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
729 intensity = 1.0f - sqrt(DotProduct(v, v));
731 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
732 d = bound(0, intensity, 255);
733 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
734 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
735 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
736 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
740 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
745 void R_Shadow_Stage_Begin(void)
749 if (r_shadow_texture3d.integer && !gl_texture3d)
750 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
752 //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
753 if (!r_shadow_attenuation2dtexture
754 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
755 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
756 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
757 R_Shadow_MakeTextures();
759 memset(&m, 0, sizeof(m));
760 GL_BlendFunc(GL_ONE, GL_ZERO);
763 R_Mesh_State_Texture(&m);
764 GL_Color(0, 0, 0, 1);
765 qglDisable(GL_SCISSOR_TEST);
766 r_shadowstage = SHADOWSTAGE_NONE;
768 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
769 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
770 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
773 void R_Shadow_LoadWorldLightsIfNeeded(void)
775 if (r_shadow_reloadlights && cl.worldmodel)
777 R_Shadow_ClearWorldLights();
778 r_shadow_reloadlights = false;
779 R_Shadow_LoadWorldLights();
780 if (r_shadow_worldlightchain == NULL)
782 R_Shadow_LoadLightsFile();
783 if (r_shadow_worldlightchain == NULL)
784 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
789 void R_Shadow_Stage_ShadowVolumes(void)
792 memset(&m, 0, sizeof(m));
793 R_Mesh_State_Texture(&m);
794 GL_Color(1, 1, 1, 1);
795 qglColorMask(0, 0, 0, 0);
796 GL_BlendFunc(GL_ONE, GL_ZERO);
799 if (r_shadow_polygonoffset.value != 0)
801 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
802 qglEnable(GL_POLYGON_OFFSET_FILL);
805 qglDisable(GL_POLYGON_OFFSET_FILL);
806 qglDepthFunc(GL_LESS);
807 qglEnable(GL_STENCIL_TEST);
808 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
809 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
810 r_shadowstage = SHADOWSTAGE_STENCIL;
811 qglClear(GL_STENCIL_BUFFER_BIT);
813 // LordHavoc note: many shadow volumes reside entirely inside the world
814 // (that is to say they are entirely bounded by their lit surfaces),
815 // which can be optimized by handling things as an inverted light volume,
816 // with the shadow boundaries of the world being simulated by an altered
817 // (129) bias to stencil clearing on such lights
818 // FIXME: generate inverted light volumes for use as shadow volumes and
819 // optimize for them as noted above
822 void R_Shadow_Stage_LightWithoutShadows(void)
825 memset(&m, 0, sizeof(m));
826 R_Mesh_State_Texture(&m);
827 GL_BlendFunc(GL_ONE, GL_ONE);
830 qglDisable(GL_POLYGON_OFFSET_FILL);
831 GL_Color(1, 1, 1, 1);
832 qglColorMask(1, 1, 1, 1);
833 qglDepthFunc(GL_EQUAL);
834 qglDisable(GL_STENCIL_TEST);
835 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
836 qglStencilFunc(GL_EQUAL, 128, 0xFF);
837 r_shadowstage = SHADOWSTAGE_LIGHT;
841 void R_Shadow_Stage_LightWithShadows(void)
844 memset(&m, 0, sizeof(m));
845 R_Mesh_State_Texture(&m);
846 GL_BlendFunc(GL_ONE, GL_ONE);
849 qglDisable(GL_POLYGON_OFFSET_FILL);
850 GL_Color(1, 1, 1, 1);
851 qglColorMask(1, 1, 1, 1);
852 qglDepthFunc(GL_EQUAL);
853 qglEnable(GL_STENCIL_TEST);
854 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
855 // only draw light where this geometry was already rendered AND the
856 // stencil is 128 (values other than this mean shadow)
857 qglStencilFunc(GL_EQUAL, 128, 0xFF);
858 r_shadowstage = SHADOWSTAGE_LIGHT;
862 void R_Shadow_Stage_End(void)
865 memset(&m, 0, sizeof(m));
866 R_Mesh_State_Texture(&m);
867 GL_BlendFunc(GL_ONE, GL_ZERO);
870 qglDisable(GL_POLYGON_OFFSET_FILL);
871 GL_Color(1, 1, 1, 1);
872 qglColorMask(1, 1, 1, 1);
873 qglDisable(GL_SCISSOR_TEST);
874 qglDepthFunc(GL_LEQUAL);
875 qglDisable(GL_STENCIL_TEST);
876 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
877 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
878 r_shadowstage = SHADOWSTAGE_NONE;
882 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
884 int i, ix1, iy1, ix2, iy2;
885 float x1, y1, x2, y2, x, y;
888 if (!r_shadow_scissor.integer)
890 // if view is inside the box, just say yes it's visible
891 if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
892 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
893 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
895 qglDisable(GL_SCISSOR_TEST);
898 VectorSubtract(r_origin, origin, v);
899 if (DotProduct(v, v) < radius * radius)
901 qglDisable(GL_SCISSOR_TEST);
904 // create viewspace bbox
905 for (i = 0;i < 8;i++)
907 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
908 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
909 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
910 v2[0] = DotProduct(v, vright);
911 v2[1] = DotProduct(v, vup);
912 v2[2] = DotProduct(v, vpn);
915 if (smins[0] > v2[0]) smins[0] = v2[0];
916 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
917 if (smins[1] > v2[1]) smins[1] = v2[1];
918 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
919 if (smins[2] > v2[2]) smins[2] = v2[2];
920 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
924 smins[0] = smaxs[0] = v2[0];
925 smins[1] = smaxs[1] = v2[1];
926 smins[2] = smaxs[2] = v2[2];
929 // now we have a bbox in viewspace
930 // clip it to the viewspace version of the sphere
931 v[0] = origin[0] - r_origin[0];
932 v[1] = origin[1] - r_origin[1];
933 v[2] = origin[2] - r_origin[2];
934 v2[0] = DotProduct(v, vright);
935 v2[1] = DotProduct(v, vup);
936 v2[2] = DotProduct(v, vpn);
937 if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
938 if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
939 if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
940 if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
941 if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
942 if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
943 // clip it to the view plane
946 // return true if that culled the box
947 if (smins[2] >= smaxs[2])
949 // ok some of it is infront of the view, transform each corner back to
950 // worldspace and then to screenspace and make screen rect
951 // initialize these variables just to avoid compiler warnings
952 x1 = y1 = x2 = y2 = 0;
953 for (i = 0;i < 8;i++)
955 v2[0] = (i & 1) ? smins[0] : smaxs[0];
956 v2[1] = (i & 2) ? smins[1] : smaxs[1];
957 v2[2] = (i & 4) ? smins[2] : smaxs[2];
958 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
959 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
960 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
962 GL_TransformToScreen(v, v2);
963 //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]);
980 // this code doesn't handle boxes with any points behind view properly
981 x1 = 1000;x2 = -1000;
982 y1 = 1000;y2 = -1000;
983 for (i = 0;i < 8;i++)
985 v[0] = (i & 1) ? mins[0] : maxs[0];
986 v[1] = (i & 2) ? mins[1] : maxs[1];
987 v[2] = (i & 4) ? mins[2] : maxs[2];
989 GL_TransformToScreen(v, v2);
990 //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]);
1007 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1008 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1009 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1010 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1011 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1012 if (ix2 <= ix1 || iy2 <= iy1)
1014 // set up the scissor rectangle
1015 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1016 qglEnable(GL_SCISSOR_TEST);
1022 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1024 int i, ix1, iy1, ix2, iy2;
1025 float x1, y1, x2, y2, x, y, f;
1026 vec3_t smins, smaxs;
1028 if (!r_shadow_scissor.integer)
1030 // if view is inside the box, just say yes it's visible
1031 // LordHavoc: for some odd reason scissor seems broken without stencil
1032 // (?!? seems like a driver bug) so abort if gl_stencil is false
1033 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1035 qglDisable(GL_SCISSOR_TEST);
1038 for (i = 0;i < 3;i++)
1051 f = DotProduct(vpn, r_origin) + 1;
1052 if (DotProduct(vpn, v2) <= f)
1054 // entirely behind nearclip plane
1057 if (DotProduct(vpn, v) >= f)
1059 // entirely infront of nearclip plane
1060 x1 = y1 = x2 = y2 = 0;
1061 for (i = 0;i < 8;i++)
1063 v[0] = (i & 1) ? mins[0] : maxs[0];
1064 v[1] = (i & 2) ? mins[1] : maxs[1];
1065 v[2] = (i & 4) ? mins[2] : maxs[2];
1067 GL_TransformToScreen(v, v2);
1068 //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]);
1087 // clipped by nearclip plane
1088 // this is nasty and crude...
1089 // create viewspace bbox
1090 for (i = 0;i < 8;i++)
1092 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1093 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1094 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1095 v2[0] = DotProduct(v, vright);
1096 v2[1] = DotProduct(v, vup);
1097 v2[2] = DotProduct(v, vpn);
1100 if (smins[0] > v2[0]) smins[0] = v2[0];
1101 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1102 if (smins[1] > v2[1]) smins[1] = v2[1];
1103 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1104 if (smins[2] > v2[2]) smins[2] = v2[2];
1105 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1109 smins[0] = smaxs[0] = v2[0];
1110 smins[1] = smaxs[1] = v2[1];
1111 smins[2] = smaxs[2] = v2[2];
1114 // now we have a bbox in viewspace
1115 // clip it to the view plane
1118 // return true if that culled the box
1119 if (smins[2] >= smaxs[2])
1121 // ok some of it is infront of the view, transform each corner back to
1122 // worldspace and then to screenspace and make screen rect
1123 // initialize these variables just to avoid compiler warnings
1124 x1 = y1 = x2 = y2 = 0;
1125 for (i = 0;i < 8;i++)
1127 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1128 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1129 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1130 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1131 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1132 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[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]);
1152 // this code doesn't handle boxes with any points behind view properly
1153 x1 = 1000;x2 = -1000;
1154 y1 = 1000;y2 = -1000;
1155 for (i = 0;i < 8;i++)
1157 v[0] = (i & 1) ? mins[0] : maxs[0];
1158 v[1] = (i & 2) ? mins[1] : maxs[1];
1159 v[2] = (i & 4) ? mins[2] : maxs[2];
1161 GL_TransformToScreen(v, v2);
1162 //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]);
1180 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1181 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1182 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1183 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1184 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1185 if (ix2 <= ix1 || iy2 <= iy1)
1187 // set up the scissor rectangle
1188 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1189 qglEnable(GL_SCISSOR_TEST);
1194 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1196 float *color4f = varray_color4f;
1197 float dist, dot, intensity, v[3], n[3];
1198 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1200 Matrix4x4_Transform(m, vertex3f, v);
1201 if ((dist = DotProduct(v, v)) < 1)
1203 Matrix4x4_Transform3x3(m, normal3f, n);
1204 if ((dot = DotProduct(n, v)) > 0)
1207 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1208 VectorScale(lightcolor, intensity, color4f);
1213 VectorClear(color4f);
1219 VectorClear(color4f);
1225 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1227 float *color4f = varray_color4f;
1228 float dist, dot, intensity, v[3], n[3];
1229 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1231 Matrix4x4_Transform(m, vertex3f, v);
1232 if ((dist = fabs(v[2])) < 1)
1234 Matrix4x4_Transform3x3(m, normal3f, n);
1235 if ((dot = DotProduct(n, v)) > 0)
1237 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1238 VectorScale(lightcolor, intensity, color4f);
1243 VectorClear(color4f);
1249 VectorClear(color4f);
1255 // FIXME: this should be done in a vertex program when possible
1256 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1257 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1261 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1262 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1263 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1270 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1274 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1275 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1282 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)
1286 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1288 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1289 // the cubemap normalizes this for us
1290 out3f[0] = DotProduct(svector3f, lightdir);
1291 out3f[1] = DotProduct(tvector3f, lightdir);
1292 out3f[2] = DotProduct(normal3f, lightdir);
1296 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)
1299 float lightdir[3], eyedir[3], halfdir[3];
1300 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1302 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1303 VectorNormalizeFast(lightdir);
1304 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1305 VectorNormalizeFast(eyedir);
1306 VectorAdd(lightdir, eyedir, halfdir);
1307 // the cubemap normalizes this for us
1308 out3f[0] = DotProduct(svector3f, halfdir);
1309 out3f[1] = DotProduct(tvector3f, halfdir);
1310 out3f[2] = DotProduct(normal3f, halfdir);
1314 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1317 float color[3], color2[3];
1319 GL_VertexPointer(vertex3f);
1320 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1323 bumptexture = r_shadow_blankbumptexture;
1325 // colorscale accounts for how much we multiply the brightness during combine
1326 // mult is how many times the final pass of the lighting will be
1327 // performed to get more brightness than otherwise possible
1328 // limit mult to 64 for sanity sake
1329 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1331 // 3/2 3D combine path (Geforce3, Radeon 8500)
1332 memset(&m, 0, sizeof(m));
1333 m.tex[0] = R_GetTexture(bumptexture);
1334 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1335 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1336 m.texcombinergb[0] = GL_REPLACE;
1337 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1338 m.pointer_texcoord[0] = texcoord2f;
1339 m.pointer_texcoord[1] = varray_texcoord3f[1];
1340 m.pointer_texcoord[2] = varray_texcoord3f[2];
1341 R_Mesh_State_Texture(&m);
1342 qglColorMask(0,0,0,1);
1343 GL_BlendFunc(GL_ONE, GL_ZERO);
1344 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1345 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1346 R_Mesh_Draw(numverts, numtriangles, elements);
1348 c_rt_lighttris += numtriangles;
1350 memset(&m, 0, sizeof(m));
1351 m.tex[0] = R_GetTexture(basetexture);
1352 m.texcubemap[1] = R_GetTexture(lightcubemap);
1353 m.pointer_texcoord[0] = texcoord2f;
1354 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1355 R_Mesh_State_Texture(&m);
1356 qglColorMask(1,1,1,0);
1357 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1359 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1360 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1361 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1363 color[0] = bound(0, color2[0], 1);
1364 color[1] = bound(0, color2[1], 1);
1365 color[2] = bound(0, color2[2], 1);
1366 GL_Color(color[0], color[1], color[2], 1);
1367 R_Mesh_Draw(numverts, numtriangles, elements);
1369 c_rt_lighttris += numtriangles;
1372 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1374 // 1/2/2 3D combine path (original Radeon)
1375 memset(&m, 0, sizeof(m));
1376 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1377 m.pointer_texcoord[0] = varray_texcoord3f[0];
1378 R_Mesh_State_Texture(&m);
1379 qglColorMask(0,0,0,1);
1380 GL_BlendFunc(GL_ONE, GL_ZERO);
1381 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1382 R_Mesh_Draw(numverts, numtriangles, elements);
1384 c_rt_lighttris += numtriangles;
1386 memset(&m, 0, sizeof(m));
1387 m.tex[0] = R_GetTexture(bumptexture);
1388 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1389 m.texcombinergb[0] = GL_REPLACE;
1390 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1391 m.pointer_texcoord[0] = texcoord2f;
1392 m.pointer_texcoord[1] = varray_texcoord3f[1];
1393 R_Mesh_State_Texture(&m);
1394 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1395 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1396 R_Mesh_Draw(numverts, numtriangles, elements);
1398 c_rt_lighttris += numtriangles;
1400 memset(&m, 0, sizeof(m));
1401 m.tex[0] = R_GetTexture(basetexture);
1402 m.texcubemap[1] = R_GetTexture(lightcubemap);
1403 m.pointer_texcoord[0] = texcoord2f;
1404 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1405 R_Mesh_State_Texture(&m);
1406 qglColorMask(1,1,1,0);
1407 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1409 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1410 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1411 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1413 color[0] = bound(0, color2[0], 1);
1414 color[1] = bound(0, color2[1], 1);
1415 color[2] = bound(0, color2[2], 1);
1416 GL_Color(color[0], color[1], color[2], 1);
1417 R_Mesh_Draw(numverts, numtriangles, elements);
1419 c_rt_lighttris += numtriangles;
1422 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1424 // 2/2 3D combine path (original Radeon)
1425 memset(&m, 0, sizeof(m));
1426 m.tex[0] = R_GetTexture(bumptexture);
1427 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1428 m.texcombinergb[0] = GL_REPLACE;
1429 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1430 m.pointer_texcoord[0] = texcoord2f;
1431 m.pointer_texcoord[1] = varray_texcoord3f[1];
1432 R_Mesh_State_Texture(&m);
1433 qglColorMask(0,0,0,1);
1434 GL_BlendFunc(GL_ONE, GL_ZERO);
1435 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1436 R_Mesh_Draw(numverts, numtriangles, elements);
1438 c_rt_lighttris += numtriangles;
1440 memset(&m, 0, sizeof(m));
1441 m.tex[0] = R_GetTexture(basetexture);
1442 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1443 m.pointer_texcoord[0] = texcoord2f;
1444 m.pointer_texcoord[1] = varray_texcoord3f[1];
1445 R_Mesh_State_Texture(&m);
1446 qglColorMask(1,1,1,0);
1447 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1448 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1449 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1450 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1452 color[0] = bound(0, color2[0], 1);
1453 color[1] = bound(0, color2[1], 1);
1454 color[2] = bound(0, color2[2], 1);
1455 GL_Color(color[0], color[1], color[2], 1);
1456 R_Mesh_Draw(numverts, numtriangles, elements);
1458 c_rt_lighttris += numtriangles;
1461 else if (r_textureunits.integer >= 4)
1463 // 4/2 2D combine path (Geforce3, Radeon 8500)
1464 memset(&m, 0, sizeof(m));
1465 m.tex[0] = R_GetTexture(bumptexture);
1466 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1467 m.texcombinergb[0] = GL_REPLACE;
1468 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1469 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1470 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1471 m.pointer_texcoord[0] = texcoord2f;
1472 m.pointer_texcoord[1] = varray_texcoord3f[1];
1473 m.pointer_texcoord[2] = varray_texcoord2f[2];
1474 m.pointer_texcoord[3] = varray_texcoord2f[3];
1475 R_Mesh_State_Texture(&m);
1476 qglColorMask(0,0,0,1);
1477 GL_BlendFunc(GL_ONE, GL_ZERO);
1478 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1479 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1480 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1481 R_Mesh_Draw(numverts, numtriangles, elements);
1483 c_rt_lighttris += numtriangles;
1485 memset(&m, 0, sizeof(m));
1486 m.tex[0] = R_GetTexture(basetexture);
1487 m.texcubemap[1] = R_GetTexture(lightcubemap);
1488 m.pointer_texcoord[0] = texcoord2f;
1489 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1490 R_Mesh_State_Texture(&m);
1491 qglColorMask(1,1,1,0);
1492 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1494 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1495 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1496 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1498 color[0] = bound(0, color2[0], 1);
1499 color[1] = bound(0, color2[1], 1);
1500 color[2] = bound(0, color2[2], 1);
1501 GL_Color(color[0], color[1], color[2], 1);
1502 R_Mesh_Draw(numverts, numtriangles, elements);
1504 c_rt_lighttris += numtriangles;
1509 // 2/2/2 2D combine path (any dot3 card)
1510 memset(&m, 0, sizeof(m));
1511 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1512 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1513 m.pointer_texcoord[0] = varray_texcoord2f[0];
1514 m.pointer_texcoord[1] = varray_texcoord2f[1];
1515 R_Mesh_State_Texture(&m);
1516 qglColorMask(0,0,0,1);
1517 GL_BlendFunc(GL_ONE, GL_ZERO);
1518 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1519 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1520 R_Mesh_Draw(numverts, numtriangles, elements);
1522 c_rt_lighttris += numtriangles;
1524 memset(&m, 0, sizeof(m));
1525 m.tex[0] = R_GetTexture(bumptexture);
1526 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1527 m.texcombinergb[0] = GL_REPLACE;
1528 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1529 m.pointer_texcoord[0] = texcoord2f;
1530 m.pointer_texcoord[1] = varray_texcoord3f[1];
1531 R_Mesh_State_Texture(&m);
1532 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1533 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1534 R_Mesh_Draw(numverts, numtriangles, elements);
1536 c_rt_lighttris += numtriangles;
1538 memset(&m, 0, sizeof(m));
1539 m.tex[0] = R_GetTexture(basetexture);
1540 m.texcubemap[1] = R_GetTexture(lightcubemap);
1541 m.pointer_texcoord[0] = texcoord2f;
1542 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1543 R_Mesh_State_Texture(&m);
1544 qglColorMask(1,1,1,0);
1545 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1547 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1548 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1549 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1551 color[0] = bound(0, color2[0], 1);
1552 color[1] = bound(0, color2[1], 1);
1553 color[2] = bound(0, color2[2], 1);
1554 GL_Color(color[0], color[1], color[2], 1);
1555 R_Mesh_Draw(numverts, numtriangles, elements);
1557 c_rt_lighttris += numtriangles;
1563 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1564 GL_DepthMask(false);
1566 GL_ColorPointer(varray_color4f);
1567 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1568 memset(&m, 0, sizeof(m));
1569 m.tex[0] = R_GetTexture(basetexture);
1570 m.pointer_texcoord[0] = texcoord2f;
1571 if (r_textureunits.integer >= 2)
1574 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1575 m.pointer_texcoord[1] = varray_texcoord2f[1];
1576 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1578 R_Mesh_State_Texture(&m);
1579 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1581 color[0] = bound(0, color2[0], 1);
1582 color[1] = bound(0, color2[1], 1);
1583 color[2] = bound(0, color2[2], 1);
1584 if (r_textureunits.integer >= 2)
1585 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1587 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1588 R_Mesh_Draw(numverts, numtriangles, elements);
1590 c_rt_lighttris += numtriangles;
1595 void R_Shadow_SpecularLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1598 float color[3], color2[3];
1600 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1603 bumptexture = r_shadow_blankbumptexture;
1605 glosstexture = r_shadow_blankglosstexture;
1606 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1608 GL_VertexPointer(vertex3f);
1610 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1612 // 2/0/0/1/2 3D combine blendsquare path
1613 memset(&m, 0, sizeof(m));
1614 m.tex[0] = R_GetTexture(bumptexture);
1615 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1616 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1617 m.pointer_texcoord[0] = texcoord2f;
1618 m.pointer_texcoord[1] = varray_texcoord3f[1];
1619 R_Mesh_State_Texture(&m);
1620 qglColorMask(0,0,0,1);
1621 // this squares the result
1622 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1623 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1624 R_Mesh_Draw(numverts, numtriangles, elements);
1626 c_rt_lighttris += numtriangles;
1628 memset(&m, 0, sizeof(m));
1629 R_Mesh_State_Texture(&m);
1630 // square alpha in framebuffer a few times to make it shiny
1631 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1632 // these comments are a test run through this math for intensity 0.5
1633 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1634 // 0.25 * 0.25 = 0.0625 (this is another pass)
1635 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1636 R_Mesh_Draw(numverts, numtriangles, elements);
1638 c_rt_lighttris += numtriangles;
1639 R_Mesh_Draw(numverts, numtriangles, elements);
1641 c_rt_lighttris += numtriangles;
1643 memset(&m, 0, sizeof(m));
1644 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1645 m.pointer_texcoord[0] = varray_texcoord3f[0];
1646 R_Mesh_State_Texture(&m);
1647 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1648 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1649 R_Mesh_Draw(numverts, numtriangles, elements);
1651 c_rt_lighttris += numtriangles;
1653 memset(&m, 0, sizeof(m));
1654 m.tex[0] = R_GetTexture(glosstexture);
1655 m.texcubemap[1] = R_GetTexture(lightcubemap);
1656 m.pointer_texcoord[0] = texcoord2f;
1657 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1658 R_Mesh_State_Texture(&m);
1659 qglColorMask(1,1,1,0);
1660 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1662 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1663 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1664 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1666 color[0] = bound(0, color2[0], 1);
1667 color[1] = bound(0, color2[1], 1);
1668 color[2] = bound(0, color2[2], 1);
1669 GL_Color(color[0], color[1], color[2], 1);
1670 R_Mesh_Draw(numverts, numtriangles, elements);
1672 c_rt_lighttris += numtriangles;
1675 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1677 // 2/0/0/2 3D combine blendsquare path
1678 memset(&m, 0, sizeof(m));
1679 m.tex[0] = R_GetTexture(bumptexture);
1680 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1681 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1682 m.pointer_texcoord[0] = texcoord2f;
1683 m.pointer_texcoord[1] = varray_texcoord3f[1];
1684 R_Mesh_State_Texture(&m);
1685 qglColorMask(0,0,0,1);
1686 // this squares the result
1687 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1688 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1689 R_Mesh_Draw(numverts, numtriangles, elements);
1691 c_rt_lighttris += numtriangles;
1693 memset(&m, 0, sizeof(m));
1694 R_Mesh_State_Texture(&m);
1695 // square alpha in framebuffer a few times to make it shiny
1696 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1697 // these comments are a test run through this math for intensity 0.5
1698 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1699 // 0.25 * 0.25 = 0.0625 (this is another pass)
1700 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1701 R_Mesh_Draw(numverts, numtriangles, elements);
1703 c_rt_lighttris += numtriangles;
1704 R_Mesh_Draw(numverts, numtriangles, elements);
1706 c_rt_lighttris += numtriangles;
1708 memset(&m, 0, sizeof(m));
1709 m.tex[0] = R_GetTexture(glosstexture);
1710 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1711 m.pointer_texcoord[0] = texcoord2f;
1712 m.pointer_texcoord[1] = varray_texcoord3f[1];
1713 R_Mesh_State_Texture(&m);
1714 qglColorMask(1,1,1,0);
1715 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1716 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1717 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1718 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1720 color[0] = bound(0, color2[0], 1);
1721 color[1] = bound(0, color2[1], 1);
1722 color[2] = bound(0, color2[2], 1);
1723 GL_Color(color[0], color[1], color[2], 1);
1724 R_Mesh_Draw(numverts, numtriangles, elements);
1726 c_rt_lighttris += numtriangles;
1729 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1731 // 2/0/0/2/2 2D combine blendsquare path
1732 memset(&m, 0, sizeof(m));
1733 m.tex[0] = R_GetTexture(bumptexture);
1734 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1735 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1736 m.pointer_texcoord[0] = texcoord2f;
1737 m.pointer_texcoord[1] = varray_texcoord3f[1];
1738 R_Mesh_State_Texture(&m);
1739 qglColorMask(0,0,0,1);
1740 // this squares the result
1741 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1742 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1743 R_Mesh_Draw(numverts, numtriangles, elements);
1745 c_rt_lighttris += numtriangles;
1747 memset(&m, 0, sizeof(m));
1748 R_Mesh_State_Texture(&m);
1749 // square alpha in framebuffer a few times to make it shiny
1750 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1751 // these comments are a test run through this math for intensity 0.5
1752 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1753 // 0.25 * 0.25 = 0.0625 (this is another pass)
1754 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1755 R_Mesh_Draw(numverts, numtriangles, elements);
1757 c_rt_lighttris += numtriangles;
1758 R_Mesh_Draw(numverts, numtriangles, elements);
1760 c_rt_lighttris += numtriangles;
1762 memset(&m, 0, sizeof(m));
1763 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1764 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1765 m.pointer_texcoord[0] = varray_texcoord2f[0];
1766 m.pointer_texcoord[1] = varray_texcoord2f[1];
1767 R_Mesh_State_Texture(&m);
1768 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1769 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1770 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1771 R_Mesh_Draw(numverts, numtriangles, elements);
1773 c_rt_lighttris += numtriangles;
1775 memset(&m, 0, sizeof(m));
1776 m.tex[0] = R_GetTexture(glosstexture);
1777 m.texcubemap[1] = R_GetTexture(lightcubemap);
1778 m.pointer_texcoord[0] = texcoord2f;
1779 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1780 R_Mesh_State_Texture(&m);
1781 qglColorMask(1,1,1,0);
1782 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1784 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1785 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1786 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1788 color[0] = bound(0, color2[0], 1);
1789 color[1] = bound(0, color2[1], 1);
1790 color[2] = bound(0, color2[2], 1);
1791 GL_Color(color[0], color[1], color[2], 1);
1792 R_Mesh_Draw(numverts, numtriangles, elements);
1794 c_rt_lighttris += numtriangles;
1800 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1802 R_Mesh_Matrix(matrix);
1803 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1806 cvar_t r_editlights = {0, "r_editlights", "0"};
1807 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1808 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1809 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1810 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1811 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1812 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1813 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1814 worldlight_t *r_shadow_worldlightchain;
1815 worldlight_t *r_shadow_selectedlight;
1816 vec3_t r_editlights_cursorlocation;
1818 static int castshadowcount = 1;
1819 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1821 int i, j, k, l, maxverts = 256, *mark, tris;
1822 float *vertex3f = NULL;
1824 shadowmesh_t *mesh, *castmesh;
1828 surfmesh_t *surfmesh;
1830 if (radius < 15 || DotProduct(color, color) < 0.03)
1832 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1836 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1837 VectorCopy(origin, e->origin);
1838 VectorCopy(color, e->light);
1839 e->lightradius = radius;
1841 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1843 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1846 e->castshadows = castshadow;
1848 e->cullradius = e->lightradius;
1849 for (k = 0;k < 3;k++)
1851 e->mins[k] = e->origin[k] - e->lightradius;
1852 e->maxs[k] = e->origin[k] + e->lightradius;
1855 e->next = r_shadow_worldlightchain;
1856 r_shadow_worldlightchain = e;
1857 if (cubemapname && cubemapname[0])
1859 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1860 strcpy(e->cubemapname, cubemapname);
1861 // FIXME: add cubemap loading (and don't load a cubemap twice)
1866 i = cl.worldmodel->PointContents(cl.worldmodel, e->origin);
1867 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1870 qbyte *bytesurfacepvs;
1872 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1873 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1875 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1877 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1878 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1879 leaf->worldnodeframe = castshadowcount;
1881 for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1882 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1883 surf->castshadow = castshadowcount;
1885 Mem_Free(byteleafpvs);
1886 Mem_Free(bytesurfacepvs);
1890 leaf = cl.worldmodel->PointInLeaf(cl.worldmodel, origin);
1891 pvs = cl.worldmodel->LeafPVS(cl.worldmodel, leaf);
1892 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1894 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1896 leaf->worldnodeframe = castshadowcount;
1897 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1899 surf = cl.worldmodel->surfaces + *mark;
1900 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1901 surf->castshadow = castshadowcount;
1908 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1909 if (leaf->worldnodeframe == castshadowcount)
1912 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1913 if (surf->castshadow == castshadowcount)
1917 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1919 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1921 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1922 if (leaf->worldnodeframe == castshadowcount)
1923 e->leafs[e->numleafs++] = leaf;
1925 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1926 if (surf->castshadow == castshadowcount)
1927 e->surfaces[e->numsurfaces++] = surf;
1929 // find bounding box of lit leafs
1930 VectorCopy(e->origin, e->mins);
1931 VectorCopy(e->origin, e->maxs);
1932 for (j = 0;j < e->numleafs;j++)
1935 for (k = 0;k < 3;k++)
1937 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1938 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1942 for (k = 0;k < 3;k++)
1944 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1945 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1947 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1952 for (j = 0;j < e->numsurfaces;j++)
1954 surf = e->surfaces[j];
1955 if (surf->flags & SURF_SHADOWCAST)
1957 surf->castshadow = castshadowcount;
1958 if (maxverts < surf->poly_numverts)
1959 maxverts = surf->poly_numverts;
1962 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1963 // make a mesh to cast a shadow volume from
1964 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1965 for (j = 0;j < e->numsurfaces;j++)
1966 if (e->surfaces[j]->castshadow == castshadowcount)
1967 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1968 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1969 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1971 // cast shadow volume from castmesh
1972 for (mesh = castmesh;mesh;mesh = mesh->next)
1974 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1976 if (maxverts < castmesh->numverts * 2)
1978 maxverts = castmesh->numverts * 2;
1983 if (vertex3f == NULL && maxverts > 0)
1984 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1986 // now that we have the buffers big enough, construct and add
1987 // the shadow volume mesh
1988 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)))
1989 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1994 // we're done with castmesh now
1995 Mod_ShadowMesh_Free(castmesh);
1996 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1997 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1998 l += mesh->numtriangles;
1999 Con_Printf("static shadow volume built containing %i triangles\n", l);
2002 Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
2005 void R_Shadow_FreeWorldLight(worldlight_t *light)
2007 worldlight_t **lightpointer;
2008 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2009 if (*lightpointer != light)
2010 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2011 *lightpointer = light->next;
2012 if (light->cubemapname)
2013 Mem_Free(light->cubemapname);
2014 if (light->shadowvolume)
2015 Mod_ShadowMesh_Free(light->shadowvolume);
2016 if (light->surfaces)
2017 Mem_Free(light->surfaces);
2019 Mem_Free(light->leafs);
2023 void R_Shadow_ClearWorldLights(void)
2025 while (r_shadow_worldlightchain)
2026 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2027 r_shadow_selectedlight = NULL;
2030 void R_Shadow_SelectLight(worldlight_t *light)
2032 if (r_shadow_selectedlight)
2033 r_shadow_selectedlight->selected = false;
2034 r_shadow_selectedlight = light;
2035 if (r_shadow_selectedlight)
2036 r_shadow_selectedlight->selected = true;
2039 rtexture_t *lighttextures[5];
2041 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2043 float scale = r_editlights_cursorgrid.value * 0.5f;
2044 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2047 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2050 const worldlight_t *light;
2053 if (light->selected)
2054 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2055 if (!light->shadowvolume)
2057 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2060 void R_Shadow_DrawLightSprites(void)
2064 worldlight_t *light;
2066 for (i = 0;i < 5;i++)
2068 lighttextures[i] = NULL;
2069 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2070 lighttextures[i] = pic->tex;
2073 for (light = r_shadow_worldlightchain;light;light = light->next)
2074 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2075 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2078 void R_Shadow_SelectLightInView(void)
2080 float bestrating, rating, temp[3];
2081 worldlight_t *best, *light;
2084 for (light = r_shadow_worldlightchain;light;light = light->next)
2086 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2087 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2090 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2091 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2093 bestrating = rating;
2098 R_Shadow_SelectLight(best);
2101 void R_Shadow_LoadWorldLights(void)
2103 int n, a, style, shadow;
2104 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2105 float origin[3], radius, color[3];
2106 if (cl.worldmodel == NULL)
2108 Con_Printf("No map loaded.\n");
2111 FS_StripExtension(cl.worldmodel->name, name);
2112 strcat(name, ".rtlights");
2113 lightsstring = FS_LoadFile(name, false);
2121 while (*s && *s != '\n')
2127 // check for modifier flags
2133 a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname);
2139 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);
2142 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2143 radius *= r_editlights_rtlightssizescale.value;
2144 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2149 Con_Printf("invalid rtlights file \"%s\"\n", name);
2150 Mem_Free(lightsstring);
2154 void R_Shadow_SaveWorldLights(void)
2156 worldlight_t *light;
2157 int bufchars, bufmaxchars;
2159 char name[MAX_QPATH];
2161 if (!r_shadow_worldlightchain)
2163 if (cl.worldmodel == NULL)
2165 Con_Printf("No map loaded.\n");
2168 FS_StripExtension(cl.worldmodel->name, name);
2169 strcat(name, ".rtlights");
2170 bufchars = bufmaxchars = 0;
2172 for (light = r_shadow_worldlightchain;light;light = light->next)
2174 sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2175 if (bufchars + (int) strlen(line) > bufmaxchars)
2177 bufmaxchars = bufchars + strlen(line) + 2048;
2179 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2183 memcpy(buf, oldbuf, bufchars);
2189 memcpy(buf + bufchars, line, strlen(line));
2190 bufchars += strlen(line);
2194 FS_WriteFile(name, buf, bufchars);
2199 void R_Shadow_LoadLightsFile(void)
2202 char name[MAX_QPATH], *lightsstring, *s, *t;
2203 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2204 if (cl.worldmodel == NULL)
2206 Con_Printf("No map loaded.\n");
2209 FS_StripExtension(cl.worldmodel->name, name);
2210 strcat(name, ".lights");
2211 lightsstring = FS_LoadFile(name, false);
2219 while (*s && *s != '\n')
2224 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);
2228 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);
2231 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2232 radius = bound(15, radius, 4096);
2233 VectorScale(color, (2.0f / (8388608.0f)), color);
2234 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2239 Con_Printf("invalid lights file \"%s\"\n", name);
2240 Mem_Free(lightsstring);
2244 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2246 int entnum, style, islight;
2247 char key[256], value[1024];
2248 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2251 if (cl.worldmodel == NULL)
2253 Con_Printf("No map loaded.\n");
2256 data = cl.worldmodel->entities;
2259 for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2262 origin[0] = origin[1] = origin[2] = 0;
2263 originhack[0] = originhack[1] = originhack[2] = 0;
2264 color[0] = color[1] = color[2] = 1;
2265 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2271 if (!COM_ParseToken(&data))
2273 if (com_token[0] == '}')
2274 break; // end of entity
2275 if (com_token[0] == '_')
2276 strcpy(key, com_token + 1);
2278 strcpy(key, com_token);
2279 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2280 key[strlen(key)-1] = 0;
2281 if (!COM_ParseToken(&data))
2283 strcpy(value, com_token);
2285 // now that we have the key pair worked out...
2286 if (!strcmp("light", key))
2287 light = atof(value);
2288 else if (!strcmp("origin", key))
2289 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2290 else if (!strcmp("color", key))
2291 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2292 else if (!strcmp("wait", key))
2293 scale = atof(value);
2294 else if (!strcmp("classname", key))
2296 if (!strncmp(value, "light", 5))
2299 if (!strcmp(value, "light_fluoro"))
2304 overridecolor[0] = 1;
2305 overridecolor[1] = 1;
2306 overridecolor[2] = 1;
2308 if (!strcmp(value, "light_fluorospark"))
2313 overridecolor[0] = 1;
2314 overridecolor[1] = 1;
2315 overridecolor[2] = 1;
2317 if (!strcmp(value, "light_globe"))
2322 overridecolor[0] = 1;
2323 overridecolor[1] = 0.8;
2324 overridecolor[2] = 0.4;
2326 if (!strcmp(value, "light_flame_large_yellow"))
2331 overridecolor[0] = 1;
2332 overridecolor[1] = 0.5;
2333 overridecolor[2] = 0.1;
2335 if (!strcmp(value, "light_flame_small_yellow"))
2340 overridecolor[0] = 1;
2341 overridecolor[1] = 0.5;
2342 overridecolor[2] = 0.1;
2344 if (!strcmp(value, "light_torch_small_white"))
2349 overridecolor[0] = 1;
2350 overridecolor[1] = 0.5;
2351 overridecolor[2] = 0.1;
2353 if (!strcmp(value, "light_torch_small_walltorch"))
2358 overridecolor[0] = 1;
2359 overridecolor[1] = 0.5;
2360 overridecolor[2] = 0.1;
2364 else if (!strcmp("style", key))
2365 style = atoi(value);
2367 if (light <= 0 && islight)
2369 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2370 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2371 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2372 VectorCopy(overridecolor, color);
2373 VectorScale(color, light, color);
2374 VectorAdd(origin, originhack, origin);
2376 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2381 void R_Shadow_SetCursorLocationForView(void)
2383 vec_t dist, push, frac;
2384 vec3_t dest, endpos, normal;
2385 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2386 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2389 dist = frac * r_editlights_cursordistance.value;
2390 push = r_editlights_cursorpushback.value;
2394 VectorMA(endpos, push, vpn, endpos);
2395 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2397 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2398 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2399 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2402 void R_Shadow_UpdateWorldLightSelection(void)
2404 R_Shadow_SetCursorLocationForView();
2405 if (r_editlights.integer)
2407 R_Shadow_SelectLightInView();
2408 R_Shadow_DrawLightSprites();
2411 R_Shadow_SelectLight(NULL);
2414 void R_Shadow_EditLights_Clear_f(void)
2416 R_Shadow_ClearWorldLights();
2419 void R_Shadow_EditLights_Reload_f(void)
2421 r_shadow_reloadlights = true;
2424 void R_Shadow_EditLights_Save_f(void)
2427 R_Shadow_SaveWorldLights();
2430 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2432 R_Shadow_ClearWorldLights();
2433 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2436 void R_Shadow_EditLights_ImportLightsFile_f(void)
2438 R_Shadow_ClearWorldLights();
2439 R_Shadow_LoadLightsFile();
2442 void R_Shadow_EditLights_Spawn_f(void)
2445 if (!r_editlights.integer)
2447 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2450 if (Cmd_Argc() != 1)
2452 Con_Printf("r_editlights_spawn does not take parameters\n");
2455 color[0] = color[1] = color[2] = 1;
2456 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2459 void R_Shadow_EditLights_Edit_f(void)
2461 vec3_t origin, color;
2464 char cubemapname[1024];
2465 if (!r_editlights.integer)
2467 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2470 if (!r_shadow_selectedlight)
2472 Con_Printf("No selected light.\n");
2475 VectorCopy(r_shadow_selectedlight->origin, origin);
2476 radius = r_shadow_selectedlight->lightradius;
2477 VectorCopy(r_shadow_selectedlight->light, color);
2478 style = r_shadow_selectedlight->style;
2479 if (r_shadow_selectedlight->cubemapname)
2480 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2483 shadows = r_shadow_selectedlight->castshadows;
2484 if (!strcmp(Cmd_Argv(1), "origin"))
2486 if (Cmd_Argc() != 5)
2488 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2491 origin[0] = atof(Cmd_Argv(2));
2492 origin[1] = atof(Cmd_Argv(3));
2493 origin[2] = atof(Cmd_Argv(4));
2495 else if (!strcmp(Cmd_Argv(1), "originx"))
2497 if (Cmd_Argc() != 3)
2499 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2502 origin[0] = atof(Cmd_Argv(2));
2504 else if (!strcmp(Cmd_Argv(1), "originy"))
2506 if (Cmd_Argc() != 3)
2508 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2511 origin[1] = atof(Cmd_Argv(2));
2513 else if (!strcmp(Cmd_Argv(1), "originz"))
2515 if (Cmd_Argc() != 3)
2517 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2520 origin[2] = atof(Cmd_Argv(2));
2522 else if (!strcmp(Cmd_Argv(1), "move"))
2524 if (Cmd_Argc() != 5)
2526 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2529 origin[0] += atof(Cmd_Argv(2));
2530 origin[1] += atof(Cmd_Argv(3));
2531 origin[2] += atof(Cmd_Argv(4));
2533 else if (!strcmp(Cmd_Argv(1), "movex"))
2535 if (Cmd_Argc() != 3)
2537 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2540 origin[0] += atof(Cmd_Argv(2));
2542 else if (!strcmp(Cmd_Argv(1), "movey"))
2544 if (Cmd_Argc() != 3)
2546 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2549 origin[1] += atof(Cmd_Argv(2));
2551 else if (!strcmp(Cmd_Argv(1), "movez"))
2553 if (Cmd_Argc() != 3)
2555 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2558 origin[2] += atof(Cmd_Argv(2));
2560 else if (!strcmp(Cmd_Argv(1), "color"))
2562 if (Cmd_Argc() != 5)
2564 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2567 color[0] = atof(Cmd_Argv(2));
2568 color[1] = atof(Cmd_Argv(3));
2569 color[2] = atof(Cmd_Argv(4));
2571 else if (!strcmp(Cmd_Argv(1), "radius"))
2573 if (Cmd_Argc() != 3)
2575 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2578 radius = atof(Cmd_Argv(2));
2580 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2582 if (Cmd_Argc() != 3)
2584 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2587 style = atoi(Cmd_Argv(2));
2589 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2593 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2596 if (Cmd_Argc() == 3)
2597 strcpy(cubemapname, Cmd_Argv(2));
2601 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2603 if (Cmd_Argc() != 3)
2605 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2608 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2612 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2613 Con_Printf("Selected light's properties:\n");
2614 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2615 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2616 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2617 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2618 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2619 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2622 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2623 r_shadow_selectedlight = NULL;
2624 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2627 extern int con_vislines;
2628 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2632 if (r_shadow_selectedlight == NULL)
2636 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2637 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;
2638 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2639 sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2640 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2641 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2642 sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2645 void R_Shadow_EditLights_ToggleShadow_f(void)
2647 if (!r_editlights.integer)
2649 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2652 if (!r_shadow_selectedlight)
2654 Con_Printf("No selected light.\n");
2657 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
2658 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2659 r_shadow_selectedlight = NULL;
2662 void R_Shadow_EditLights_Remove_f(void)
2664 if (!r_editlights.integer)
2666 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2669 if (!r_shadow_selectedlight)
2671 Con_Printf("No selected light.\n");
2674 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2675 r_shadow_selectedlight = NULL;
2678 void R_Shadow_EditLights_Init(void)
2680 Cvar_RegisterVariable(&r_editlights);
2681 Cvar_RegisterVariable(&r_editlights_cursordistance);
2682 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2683 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2684 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2685 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2686 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2687 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2688 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2689 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2690 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2691 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2692 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2693 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2694 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2695 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2696 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);