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;
273 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)
275 int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
277 const int *e, *n, *te;
280 // make sure trianglefacinglight is big enough for this volume
281 if (maxtrianglefacinglight < trianglerange_end)
282 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
284 if (maxvertexupdate < innumvertices)
286 maxvertexupdate = innumvertices;
288 Mem_Free(vertexupdate);
290 Mem_Free(vertexremap);
291 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
292 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
296 if (r_shadow_singlepassvolumegeneration.integer)
298 // one pass approach (identify lit/dark faces and generate sides while doing so)
299 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
301 // calculate triangle facing flag
302 v[0] = invertex3f + e[0] * 3;
303 v[1] = invertex3f + e[1] * 3;
304 v[2] = invertex3f + e[2] * 3;
305 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
307 // make sure the vertices are created
308 for (j = 0;j < 3;j++)
310 if (vertexupdate[e[j]] != vertexupdatenum)
312 vertexupdate[e[j]] = vertexupdatenum;
313 vertexremap[e[j]] = outvertices;
314 VectorCopy(v[j], outvertex3f);
315 VectorSubtract(v[j], relativelightorigin, temp);
316 f = projectdistance / VectorLength(temp);
317 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
322 // output the front and back triangles
323 vr[0] = vertexremap[e[0]];
324 vr[1] = vertexremap[e[1]];
325 vr[2] = vertexremap[e[2]];
326 outelement3i[0] = vr[0];
327 outelement3i[1] = vr[1];
328 outelement3i[2] = vr[2];
329 outelement3i[3] = vr[2] + 1;
330 outelement3i[4] = vr[1] + 1;
331 outelement3i[5] = vr[0] + 1;
334 // output the sides (facing outward from this triangle)
336 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
338 outelement3i[0] = vr[1];
339 outelement3i[1] = vr[0];
340 outelement3i[2] = vr[0] + 1;
341 outelement3i[3] = vr[1];
342 outelement3i[4] = vr[0] + 1;
343 outelement3i[5] = vr[1] + 1;
348 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]))))
350 outelement3i[0] = vr[2];
351 outelement3i[1] = vr[1];
352 outelement3i[2] = vr[1] + 1;
353 outelement3i[3] = vr[2];
354 outelement3i[4] = vr[1] + 1;
355 outelement3i[5] = vr[2] + 1;
360 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]))))
362 outelement3i[0] = vr[0];
363 outelement3i[1] = vr[2];
364 outelement3i[2] = vr[2] + 1;
365 outelement3i[3] = vr[0];
366 outelement3i[4] = vr[2] + 1;
367 outelement3i[5] = vr[0] + 1;
374 // this triangle is not facing the light
375 // output the sides (facing inward to this triangle)
377 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
379 vr[0] = vertexremap[e[0]];
380 vr[1] = vertexremap[e[1]];
381 outelement3i[0] = vr[1];
382 outelement3i[1] = vr[0] + 1;
383 outelement3i[2] = vr[0];
384 outelement3i[3] = vr[1];
385 outelement3i[4] = vr[1] + 1;
386 outelement3i[5] = vr[0] + 1;
391 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
393 vr[1] = vertexremap[e[1]];
394 vr[2] = vertexremap[e[2]];
395 outelement3i[0] = vr[2];
396 outelement3i[1] = vr[1] + 1;
397 outelement3i[2] = vr[1];
398 outelement3i[3] = vr[2];
399 outelement3i[4] = vr[2] + 1;
400 outelement3i[5] = vr[1] + 1;
405 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
407 vr[0] = vertexremap[e[0]];
408 vr[2] = vertexremap[e[2]];
409 outelement3i[0] = vr[0];
410 outelement3i[1] = vr[2] + 1;
411 outelement3i[2] = vr[2];
412 outelement3i[3] = vr[0];
413 outelement3i[4] = vr[0] + 1;
414 outelement3i[5] = vr[2] + 1;
423 // two pass approach (identify lit/dark faces and then generate sides)
424 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
426 // calculate triangle facing flag
427 v[0] = invertex3f + e[0] * 3;
428 v[1] = invertex3f + e[1] * 3;
429 v[2] = invertex3f + e[2] * 3;
430 if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
432 trianglefacinglightlist[numfacing++] = i;
433 // make sure the vertices are created
434 for (j = 0;j < 3;j++)
436 if (vertexupdate[e[j]] != vertexupdatenum)
438 vertexupdate[e[j]] = vertexupdatenum;
439 vertexremap[e[j]] = outvertices;
440 VectorSubtract(v[j], relativelightorigin, temp);
441 f = projectdistance / VectorLength(temp);
442 VectorCopy(v[j], outvertex3f);
443 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
448 // output the front and back triangles
449 outelement3i[0] = vertexremap[e[0]];
450 outelement3i[1] = vertexremap[e[1]];
451 outelement3i[2] = vertexremap[e[2]];
452 outelement3i[3] = vertexremap[e[2]] + 1;
453 outelement3i[4] = vertexremap[e[1]] + 1;
454 outelement3i[5] = vertexremap[e[0]] + 1;
459 for (i = 0;i < numfacing;i++)
461 t = trianglefacinglightlist[i];
462 e = inelement3i + t * 3;
463 n = inneighbor3i + t * 3;
464 // output the sides (facing outward from this triangle)
466 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]))))
468 vr[0] = vertexremap[e[0]];
469 vr[1] = vertexremap[e[1]];
470 outelement3i[0] = vr[1];
471 outelement3i[1] = vr[0];
472 outelement3i[2] = vr[0] + 1;
473 outelement3i[3] = vr[1];
474 outelement3i[4] = vr[0] + 1;
475 outelement3i[5] = vr[1] + 1;
480 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]))))
482 vr[1] = vertexremap[e[1]];
483 vr[2] = vertexremap[e[2]];
484 outelement3i[0] = vr[2];
485 outelement3i[1] = vr[1];
486 outelement3i[2] = vr[1] + 1;
487 outelement3i[3] = vr[2];
488 outelement3i[4] = vr[1] + 1;
489 outelement3i[5] = vr[2] + 1;
494 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]))))
496 vr[0] = vertexremap[e[0]];
497 vr[2] = vertexremap[e[2]];
498 outelement3i[0] = vr[0];
499 outelement3i[1] = vr[2];
500 outelement3i[2] = vr[2] + 1;
501 outelement3i[3] = vr[0];
502 outelement3i[4] = vr[2] + 1;
503 outelement3i[5] = vr[0] + 1;
510 *outnumvertices = outvertices;
514 float varray_vertex3f2[65536*3];
516 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
519 if (projectdistance < 0.1)
521 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
527 // make sure shadowelements is big enough for this volume
528 if (maxshadowelements < numtris * 24)
529 R_Shadow_ResizeShadowElements(numtris);
531 // check which triangles are facing the light, and then output
532 // triangle elements and vertices... by clever use of elements we
533 // can construct the whole shadow from the unprojected vertices and
534 // the projected vertices
535 if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
537 GL_VertexPointer(varray_vertex3f2);
538 if (r_shadowstage == SHADOWSTAGE_STENCIL)
540 // increment stencil if backface is behind depthbuffer
541 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
542 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
543 R_Mesh_Draw(outverts, tris, shadowelements);
545 c_rt_shadowtris += numtris;
546 // decrement stencil if frontface is behind depthbuffer
547 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
548 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
550 R_Mesh_Draw(outverts, tris, shadowelements);
552 c_rt_shadowtris += numtris;
556 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
559 if (r_shadowstage == SHADOWSTAGE_STENCIL)
561 // increment stencil if backface is behind depthbuffer
562 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
563 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
564 for (mesh = firstmesh;mesh;mesh = mesh->next)
566 GL_VertexPointer(mesh->vertex3f);
567 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
568 c_rtcached_shadowmeshes++;
569 c_rtcached_shadowtris += mesh->numtriangles;
571 // decrement stencil if frontface is behind depthbuffer
572 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
573 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
575 for (mesh = firstmesh;mesh;mesh = mesh->next)
577 GL_VertexPointer(mesh->vertex3f);
578 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
579 c_rtcached_shadowmeshes++;
580 c_rtcached_shadowtris += mesh->numtriangles;
584 float r_shadow_attenpower, r_shadow_attenscale;
585 static void R_Shadow_MakeTextures(void)
587 int x, y, z, d, side;
588 float v[3], s, t, intensity;
590 R_FreeTexturePool(&r_shadow_texturepool);
591 r_shadow_texturepool = R_AllocTexturePool();
592 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
593 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
595 #define ATTEN2DSIZE 64
596 #define ATTEN3DSIZE 32
597 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
602 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
607 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
612 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
613 if (gl_texturecubemap)
615 for (side = 0;side < 6;side++)
617 for (y = 0;y < NORMSIZE;y++)
619 for (x = 0;x < NORMSIZE;x++)
621 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
622 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
656 intensity = 127.0f / sqrt(DotProduct(v, v));
657 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
658 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
659 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
660 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
664 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
667 r_shadow_normalcubetexture = NULL;
668 for (y = 0;y < ATTEN2DSIZE;y++)
670 for (x = 0;x < ATTEN2DSIZE;x++)
672 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
673 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
675 intensity = 1.0f - sqrt(DotProduct(v, v));
677 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
678 d = bound(0, intensity, 255);
679 data[(y*ATTEN2DSIZE+x)*4+0] = d;
680 data[(y*ATTEN2DSIZE+x)*4+1] = d;
681 data[(y*ATTEN2DSIZE+x)*4+2] = d;
682 data[(y*ATTEN2DSIZE+x)*4+3] = d;
685 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
686 if (r_shadow_texture3d.integer)
688 for (z = 0;z < ATTEN3DSIZE;z++)
690 for (y = 0;y < ATTEN3DSIZE;y++)
692 for (x = 0;x < ATTEN3DSIZE;x++)
694 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
695 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
696 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
697 intensity = 1.0f - sqrt(DotProduct(v, v));
699 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
700 d = bound(0, intensity, 255);
701 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
702 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
703 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
704 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
708 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
713 void R_Shadow_Stage_Begin(void)
717 if (r_shadow_texture3d.integer && !gl_texture3d)
718 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
720 //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
721 if (!r_shadow_attenuation2dtexture
722 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
723 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
724 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
725 R_Shadow_MakeTextures();
727 memset(&m, 0, sizeof(m));
728 GL_BlendFunc(GL_ONE, GL_ZERO);
731 R_Mesh_State_Texture(&m);
732 GL_Color(0, 0, 0, 1);
733 qglDisable(GL_SCISSOR_TEST);
734 r_shadowstage = SHADOWSTAGE_NONE;
736 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
737 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
738 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
741 void R_Shadow_LoadWorldLightsIfNeeded(void)
743 if (r_shadow_reloadlights && cl.worldmodel)
745 R_Shadow_ClearWorldLights();
746 r_shadow_reloadlights = false;
747 R_Shadow_LoadWorldLights();
748 if (r_shadow_worldlightchain == NULL)
750 R_Shadow_LoadLightsFile();
751 if (r_shadow_worldlightchain == NULL)
752 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
757 void R_Shadow_Stage_ShadowVolumes(void)
760 memset(&m, 0, sizeof(m));
761 R_Mesh_State_Texture(&m);
762 GL_Color(1, 1, 1, 1);
763 qglColorMask(0, 0, 0, 0);
764 GL_BlendFunc(GL_ONE, GL_ZERO);
767 if (r_shadow_polygonoffset.value != 0)
769 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
770 qglEnable(GL_POLYGON_OFFSET_FILL);
773 qglDisable(GL_POLYGON_OFFSET_FILL);
774 qglDepthFunc(GL_LESS);
775 qglEnable(GL_STENCIL_TEST);
776 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
777 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
778 r_shadowstage = SHADOWSTAGE_STENCIL;
779 qglClear(GL_STENCIL_BUFFER_BIT);
781 // LordHavoc note: many shadow volumes reside entirely inside the world
782 // (that is to say they are entirely bounded by their lit surfaces),
783 // which can be optimized by handling things as an inverted light volume,
784 // with the shadow boundaries of the world being simulated by an altered
785 // (129) bias to stencil clearing on such lights
786 // FIXME: generate inverted light volumes for use as shadow volumes and
787 // optimize for them as noted above
790 void R_Shadow_Stage_LightWithoutShadows(void)
793 memset(&m, 0, sizeof(m));
794 R_Mesh_State_Texture(&m);
795 GL_BlendFunc(GL_ONE, GL_ONE);
798 qglDisable(GL_POLYGON_OFFSET_FILL);
799 GL_Color(1, 1, 1, 1);
800 qglColorMask(1, 1, 1, 1);
801 qglDepthFunc(GL_EQUAL);
802 qglDisable(GL_STENCIL_TEST);
803 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
804 qglStencilFunc(GL_EQUAL, 128, 0xFF);
805 r_shadowstage = SHADOWSTAGE_LIGHT;
809 void R_Shadow_Stage_LightWithShadows(void)
812 memset(&m, 0, sizeof(m));
813 R_Mesh_State_Texture(&m);
814 GL_BlendFunc(GL_ONE, GL_ONE);
817 qglDisable(GL_POLYGON_OFFSET_FILL);
818 GL_Color(1, 1, 1, 1);
819 qglColorMask(1, 1, 1, 1);
820 qglDepthFunc(GL_EQUAL);
821 qglEnable(GL_STENCIL_TEST);
822 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
823 // only draw light where this geometry was already rendered AND the
824 // stencil is 128 (values other than this mean shadow)
825 qglStencilFunc(GL_EQUAL, 128, 0xFF);
826 r_shadowstage = SHADOWSTAGE_LIGHT;
830 void R_Shadow_Stage_End(void)
833 memset(&m, 0, sizeof(m));
834 R_Mesh_State_Texture(&m);
835 GL_BlendFunc(GL_ONE, GL_ZERO);
838 qglDisable(GL_POLYGON_OFFSET_FILL);
839 GL_Color(1, 1, 1, 1);
840 qglColorMask(1, 1, 1, 1);
841 qglDisable(GL_SCISSOR_TEST);
842 qglDepthFunc(GL_LEQUAL);
843 qglDisable(GL_STENCIL_TEST);
844 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
845 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
846 r_shadowstage = SHADOWSTAGE_NONE;
850 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
852 int i, ix1, iy1, ix2, iy2;
853 float x1, y1, x2, y2, x, y;
856 if (!r_shadow_scissor.integer)
858 // if view is inside the box, just say yes it's visible
859 if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
860 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
861 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
863 qglDisable(GL_SCISSOR_TEST);
866 VectorSubtract(r_origin, origin, v);
867 if (DotProduct(v, v) < radius * radius)
869 qglDisable(GL_SCISSOR_TEST);
872 // create viewspace bbox
873 for (i = 0;i < 8;i++)
875 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
876 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
877 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
878 v2[0] = DotProduct(v, vright);
879 v2[1] = DotProduct(v, vup);
880 v2[2] = DotProduct(v, vpn);
883 if (smins[0] > v2[0]) smins[0] = v2[0];
884 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
885 if (smins[1] > v2[1]) smins[1] = v2[1];
886 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
887 if (smins[2] > v2[2]) smins[2] = v2[2];
888 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
892 smins[0] = smaxs[0] = v2[0];
893 smins[1] = smaxs[1] = v2[1];
894 smins[2] = smaxs[2] = v2[2];
897 // now we have a bbox in viewspace
898 // clip it to the viewspace version of the sphere
899 v[0] = origin[0] - r_origin[0];
900 v[1] = origin[1] - r_origin[1];
901 v[2] = origin[2] - r_origin[2];
902 v2[0] = DotProduct(v, vright);
903 v2[1] = DotProduct(v, vup);
904 v2[2] = DotProduct(v, vpn);
905 if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
906 if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
907 if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
908 if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
909 if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
910 if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
911 // clip it to the view plane
914 // return true if that culled the box
915 if (smins[2] >= smaxs[2])
917 // ok some of it is infront of the view, transform each corner back to
918 // worldspace and then to screenspace and make screen rect
919 // initialize these variables just to avoid compiler warnings
920 x1 = y1 = x2 = y2 = 0;
921 for (i = 0;i < 8;i++)
923 v2[0] = (i & 1) ? smins[0] : smaxs[0];
924 v2[1] = (i & 2) ? smins[1] : smaxs[1];
925 v2[2] = (i & 4) ? smins[2] : smaxs[2];
926 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
927 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
928 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
930 GL_TransformToScreen(v, v2);
931 //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]);
948 // this code doesn't handle boxes with any points behind view properly
949 x1 = 1000;x2 = -1000;
950 y1 = 1000;y2 = -1000;
951 for (i = 0;i < 8;i++)
953 v[0] = (i & 1) ? mins[0] : maxs[0];
954 v[1] = (i & 2) ? mins[1] : maxs[1];
955 v[2] = (i & 4) ? mins[2] : maxs[2];
957 GL_TransformToScreen(v, v2);
958 //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]);
975 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
976 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
977 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
978 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
979 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
980 if (ix2 <= ix1 || iy2 <= iy1)
982 // set up the scissor rectangle
983 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
984 qglEnable(GL_SCISSOR_TEST);
990 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
992 int i, ix1, iy1, ix2, iy2;
993 float x1, y1, x2, y2, x, y, f;
996 if (!r_shadow_scissor.integer)
998 // if view is inside the box, just say yes it's visible
999 // LordHavoc: for some odd reason scissor seems broken without stencil
1000 // (?!? seems like a driver bug) so abort if gl_stencil is false
1001 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1003 qglDisable(GL_SCISSOR_TEST);
1006 for (i = 0;i < 3;i++)
1019 f = DotProduct(vpn, r_origin) + 1;
1020 if (DotProduct(vpn, v2) <= f)
1022 // entirely behind nearclip plane
1025 if (DotProduct(vpn, v) >= f)
1027 // entirely infront of nearclip plane
1028 x1 = y1 = x2 = y2 = 0;
1029 for (i = 0;i < 8;i++)
1031 v[0] = (i & 1) ? mins[0] : maxs[0];
1032 v[1] = (i & 2) ? mins[1] : maxs[1];
1033 v[2] = (i & 4) ? mins[2] : maxs[2];
1035 GL_TransformToScreen(v, v2);
1036 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1055 // clipped by nearclip plane
1056 // this is nasty and crude...
1057 // create viewspace bbox
1058 for (i = 0;i < 8;i++)
1060 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1061 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1062 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1063 v2[0] = DotProduct(v, vright);
1064 v2[1] = DotProduct(v, vup);
1065 v2[2] = DotProduct(v, vpn);
1068 if (smins[0] > v2[0]) smins[0] = v2[0];
1069 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1070 if (smins[1] > v2[1]) smins[1] = v2[1];
1071 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1072 if (smins[2] > v2[2]) smins[2] = v2[2];
1073 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1077 smins[0] = smaxs[0] = v2[0];
1078 smins[1] = smaxs[1] = v2[1];
1079 smins[2] = smaxs[2] = v2[2];
1082 // now we have a bbox in viewspace
1083 // clip it to the view plane
1086 // return true if that culled the box
1087 if (smins[2] >= smaxs[2])
1089 // ok some of it is infront of the view, transform each corner back to
1090 // worldspace and then to screenspace and make screen rect
1091 // initialize these variables just to avoid compiler warnings
1092 x1 = y1 = x2 = y2 = 0;
1093 for (i = 0;i < 8;i++)
1095 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1096 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1097 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1098 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1099 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1100 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1102 GL_TransformToScreen(v, v2);
1103 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1120 // this code doesn't handle boxes with any points behind view properly
1121 x1 = 1000;x2 = -1000;
1122 y1 = 1000;y2 = -1000;
1123 for (i = 0;i < 8;i++)
1125 v[0] = (i & 1) ? mins[0] : maxs[0];
1126 v[1] = (i & 2) ? mins[1] : maxs[1];
1127 v[2] = (i & 4) ? mins[2] : maxs[2];
1129 GL_TransformToScreen(v, v2);
1130 //Con_Printf("%.3f %.3f %.3f %.3f transformed to %.3f %.3f %.3f %.3f\n", v[0], v[1], v[2], v[3], v2[0], v2[1], v2[2], v2[3]);
1148 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1149 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1150 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1151 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1152 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1153 if (ix2 <= ix1 || iy2 <= iy1)
1155 // set up the scissor rectangle
1156 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1157 qglEnable(GL_SCISSOR_TEST);
1162 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1164 float *color4f = varray_color4f;
1165 float dist, dot, intensity, v[3], n[3];
1166 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1168 Matrix4x4_Transform(m, vertex3f, v);
1169 if ((dist = DotProduct(v, v)) < 1)
1171 Matrix4x4_Transform3x3(m, normal3f, n);
1172 if ((dot = DotProduct(n, v)) > 0)
1175 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1176 VectorScale(lightcolor, intensity, color4f);
1181 VectorClear(color4f);
1187 VectorClear(color4f);
1193 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1195 float *color4f = varray_color4f;
1196 float dist, dot, intensity, v[3], n[3];
1197 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1199 Matrix4x4_Transform(m, vertex3f, v);
1200 if ((dist = fabs(v[2])) < 1)
1202 Matrix4x4_Transform3x3(m, normal3f, n);
1203 if ((dot = DotProduct(n, v)) > 0)
1205 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1206 VectorScale(lightcolor, intensity, color4f);
1211 VectorClear(color4f);
1217 VectorClear(color4f);
1223 // FIXME: this should be done in a vertex program when possible
1224 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1225 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1229 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1230 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1231 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1238 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1242 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1243 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1250 void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1254 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1256 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1257 // the cubemap normalizes this for us
1258 out3f[0] = DotProduct(svector3f, lightdir);
1259 out3f[1] = DotProduct(tvector3f, lightdir);
1260 out3f[2] = DotProduct(normal3f, lightdir);
1264 void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1267 float lightdir[3], eyedir[3], halfdir[3];
1268 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1270 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1271 VectorNormalizeFast(lightdir);
1272 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1273 VectorNormalizeFast(eyedir);
1274 VectorAdd(lightdir, eyedir, halfdir);
1275 // the cubemap normalizes this for us
1276 out3f[0] = DotProduct(svector3f, halfdir);
1277 out3f[1] = DotProduct(tvector3f, halfdir);
1278 out3f[2] = DotProduct(normal3f, halfdir);
1282 void R_Shadow_DiffuseLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1285 float color[3], color2[3];
1287 GL_VertexPointer(vertex3f);
1288 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1291 bumptexture = r_shadow_blankbumptexture;
1293 // colorscale accounts for how much we multiply the brightness during combine
1294 // mult is how many times the final pass of the lighting will be
1295 // performed to get more brightness than otherwise possible
1296 // limit mult to 64 for sanity sake
1297 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1299 // 3/2 3D combine path (Geforce3, Radeon 8500)
1300 memset(&m, 0, sizeof(m));
1301 m.tex[0] = R_GetTexture(bumptexture);
1302 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1303 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1304 m.texcombinergb[0] = GL_REPLACE;
1305 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1306 m.pointer_texcoord[0] = texcoord2f;
1307 m.pointer_texcoord[1] = varray_texcoord3f[1];
1308 m.pointer_texcoord[2] = varray_texcoord3f[2];
1309 R_Mesh_State_Texture(&m);
1310 qglColorMask(0,0,0,1);
1311 GL_BlendFunc(GL_ONE, GL_ZERO);
1312 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1313 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1314 R_Mesh_Draw(numverts, numtriangles, elements);
1316 c_rt_lighttris += numtriangles;
1318 memset(&m, 0, sizeof(m));
1319 m.tex[0] = R_GetTexture(basetexture);
1320 m.texcubemap[1] = R_GetTexture(lightcubemap);
1321 m.pointer_texcoord[0] = texcoord2f;
1322 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1323 R_Mesh_State_Texture(&m);
1324 qglColorMask(1,1,1,0);
1325 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1327 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1328 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1329 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1331 color[0] = bound(0, color2[0], 1);
1332 color[1] = bound(0, color2[1], 1);
1333 color[2] = bound(0, color2[2], 1);
1334 GL_Color(color[0], color[1], color[2], 1);
1335 R_Mesh_Draw(numverts, numtriangles, elements);
1337 c_rt_lighttris += numtriangles;
1340 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1342 // 1/2/2 3D combine path (original Radeon)
1343 memset(&m, 0, sizeof(m));
1344 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1345 m.pointer_texcoord[0] = varray_texcoord3f[0];
1346 R_Mesh_State_Texture(&m);
1347 qglColorMask(0,0,0,1);
1348 GL_BlendFunc(GL_ONE, GL_ZERO);
1349 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1350 R_Mesh_Draw(numverts, numtriangles, elements);
1352 c_rt_lighttris += numtriangles;
1354 memset(&m, 0, sizeof(m));
1355 m.tex[0] = R_GetTexture(bumptexture);
1356 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1357 m.texcombinergb[0] = GL_REPLACE;
1358 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1359 m.pointer_texcoord[0] = texcoord2f;
1360 m.pointer_texcoord[1] = varray_texcoord3f[1];
1361 R_Mesh_State_Texture(&m);
1362 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1363 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1364 R_Mesh_Draw(numverts, numtriangles, elements);
1366 c_rt_lighttris += numtriangles;
1368 memset(&m, 0, sizeof(m));
1369 m.tex[0] = R_GetTexture(basetexture);
1370 m.texcubemap[1] = R_GetTexture(lightcubemap);
1371 m.pointer_texcoord[0] = texcoord2f;
1372 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1373 R_Mesh_State_Texture(&m);
1374 qglColorMask(1,1,1,0);
1375 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1377 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1378 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1379 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1381 color[0] = bound(0, color2[0], 1);
1382 color[1] = bound(0, color2[1], 1);
1383 color[2] = bound(0, color2[2], 1);
1384 GL_Color(color[0], color[1], color[2], 1);
1385 R_Mesh_Draw(numverts, numtriangles, elements);
1387 c_rt_lighttris += numtriangles;
1390 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1392 // 2/2 3D combine path (original Radeon)
1393 memset(&m, 0, sizeof(m));
1394 m.tex[0] = R_GetTexture(bumptexture);
1395 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1396 m.texcombinergb[0] = GL_REPLACE;
1397 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1398 m.pointer_texcoord[0] = texcoord2f;
1399 m.pointer_texcoord[1] = varray_texcoord3f[1];
1400 R_Mesh_State_Texture(&m);
1401 qglColorMask(0,0,0,1);
1402 GL_BlendFunc(GL_ONE, GL_ZERO);
1403 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1404 R_Mesh_Draw(numverts, numtriangles, elements);
1406 c_rt_lighttris += numtriangles;
1408 memset(&m, 0, sizeof(m));
1409 m.tex[0] = R_GetTexture(basetexture);
1410 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1411 m.pointer_texcoord[0] = texcoord2f;
1412 m.pointer_texcoord[1] = varray_texcoord3f[1];
1413 R_Mesh_State_Texture(&m);
1414 qglColorMask(1,1,1,0);
1415 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1416 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1417 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1418 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1420 color[0] = bound(0, color2[0], 1);
1421 color[1] = bound(0, color2[1], 1);
1422 color[2] = bound(0, color2[2], 1);
1423 GL_Color(color[0], color[1], color[2], 1);
1424 R_Mesh_Draw(numverts, numtriangles, elements);
1426 c_rt_lighttris += numtriangles;
1429 else if (r_textureunits.integer >= 4)
1431 // 4/2 2D combine path (Geforce3, Radeon 8500)
1432 memset(&m, 0, sizeof(m));
1433 m.tex[0] = R_GetTexture(bumptexture);
1434 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1435 m.texcombinergb[0] = GL_REPLACE;
1436 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1437 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1438 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1439 m.pointer_texcoord[0] = texcoord2f;
1440 m.pointer_texcoord[1] = varray_texcoord3f[1];
1441 m.pointer_texcoord[2] = varray_texcoord2f[2];
1442 m.pointer_texcoord[3] = varray_texcoord2f[3];
1443 R_Mesh_State_Texture(&m);
1444 qglColorMask(0,0,0,1);
1445 GL_BlendFunc(GL_ONE, GL_ZERO);
1446 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1447 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1448 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1449 R_Mesh_Draw(numverts, numtriangles, elements);
1451 c_rt_lighttris += numtriangles;
1453 memset(&m, 0, sizeof(m));
1454 m.tex[0] = R_GetTexture(basetexture);
1455 m.texcubemap[1] = R_GetTexture(lightcubemap);
1456 m.pointer_texcoord[0] = texcoord2f;
1457 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1458 R_Mesh_State_Texture(&m);
1459 qglColorMask(1,1,1,0);
1460 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1462 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1463 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1464 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1466 color[0] = bound(0, color2[0], 1);
1467 color[1] = bound(0, color2[1], 1);
1468 color[2] = bound(0, color2[2], 1);
1469 GL_Color(color[0], color[1], color[2], 1);
1470 R_Mesh_Draw(numverts, numtriangles, elements);
1472 c_rt_lighttris += numtriangles;
1477 // 2/2/2 2D combine path (any dot3 card)
1478 memset(&m, 0, sizeof(m));
1479 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1480 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1481 m.pointer_texcoord[0] = varray_texcoord2f[0];
1482 m.pointer_texcoord[1] = varray_texcoord2f[1];
1483 R_Mesh_State_Texture(&m);
1484 qglColorMask(0,0,0,1);
1485 GL_BlendFunc(GL_ONE, GL_ZERO);
1486 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1487 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1488 R_Mesh_Draw(numverts, numtriangles, elements);
1490 c_rt_lighttris += numtriangles;
1492 memset(&m, 0, sizeof(m));
1493 m.tex[0] = R_GetTexture(bumptexture);
1494 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1495 m.texcombinergb[0] = GL_REPLACE;
1496 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1497 m.pointer_texcoord[0] = texcoord2f;
1498 m.pointer_texcoord[1] = varray_texcoord3f[1];
1499 R_Mesh_State_Texture(&m);
1500 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1501 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1502 R_Mesh_Draw(numverts, numtriangles, elements);
1504 c_rt_lighttris += numtriangles;
1506 memset(&m, 0, sizeof(m));
1507 m.tex[0] = R_GetTexture(basetexture);
1508 m.texcubemap[1] = R_GetTexture(lightcubemap);
1509 m.pointer_texcoord[0] = texcoord2f;
1510 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1511 R_Mesh_State_Texture(&m);
1512 qglColorMask(1,1,1,0);
1513 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1515 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1516 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1517 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1519 color[0] = bound(0, color2[0], 1);
1520 color[1] = bound(0, color2[1], 1);
1521 color[2] = bound(0, color2[2], 1);
1522 GL_Color(color[0], color[1], color[2], 1);
1523 R_Mesh_Draw(numverts, numtriangles, elements);
1525 c_rt_lighttris += numtriangles;
1531 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1532 GL_DepthMask(false);
1534 GL_ColorPointer(varray_color4f);
1535 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1536 memset(&m, 0, sizeof(m));
1537 m.tex[0] = R_GetTexture(basetexture);
1538 m.pointer_texcoord[0] = texcoord2f;
1539 if (r_textureunits.integer >= 2)
1542 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1543 m.pointer_texcoord[1] = varray_texcoord2f[1];
1544 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1546 R_Mesh_State_Texture(&m);
1547 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1549 color[0] = bound(0, color2[0], 1);
1550 color[1] = bound(0, color2[1], 1);
1551 color[2] = bound(0, color2[2], 1);
1552 if (r_textureunits.integer >= 2)
1553 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1555 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1556 R_Mesh_Draw(numverts, numtriangles, elements);
1558 c_rt_lighttris += numtriangles;
1563 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)
1566 float color[3], color2[3];
1568 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1571 bumptexture = r_shadow_blankbumptexture;
1573 glosstexture = r_shadow_blankglosstexture;
1574 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1576 GL_VertexPointer(vertex3f);
1578 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1580 // 2/0/0/1/2 3D combine blendsquare path
1581 memset(&m, 0, sizeof(m));
1582 m.tex[0] = R_GetTexture(bumptexture);
1583 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1584 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1585 m.pointer_texcoord[0] = texcoord2f;
1586 m.pointer_texcoord[1] = varray_texcoord3f[1];
1587 R_Mesh_State_Texture(&m);
1588 qglColorMask(0,0,0,1);
1589 // this squares the result
1590 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1591 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1592 R_Mesh_Draw(numverts, numtriangles, elements);
1594 c_rt_lighttris += numtriangles;
1596 memset(&m, 0, sizeof(m));
1597 R_Mesh_State_Texture(&m);
1598 // square alpha in framebuffer a few times to make it shiny
1599 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1600 // these comments are a test run through this math for intensity 0.5
1601 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1602 // 0.25 * 0.25 = 0.0625 (this is another pass)
1603 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1604 R_Mesh_Draw(numverts, numtriangles, elements);
1606 c_rt_lighttris += numtriangles;
1607 R_Mesh_Draw(numverts, numtriangles, elements);
1609 c_rt_lighttris += numtriangles;
1611 memset(&m, 0, sizeof(m));
1612 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1613 m.pointer_texcoord[0] = varray_texcoord3f[0];
1614 R_Mesh_State_Texture(&m);
1615 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1616 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1617 R_Mesh_Draw(numverts, numtriangles, elements);
1619 c_rt_lighttris += numtriangles;
1621 memset(&m, 0, sizeof(m));
1622 m.tex[0] = R_GetTexture(glosstexture);
1623 m.texcubemap[1] = R_GetTexture(lightcubemap);
1624 m.pointer_texcoord[0] = texcoord2f;
1625 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1626 R_Mesh_State_Texture(&m);
1627 qglColorMask(1,1,1,0);
1628 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1630 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1631 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1632 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1634 color[0] = bound(0, color2[0], 1);
1635 color[1] = bound(0, color2[1], 1);
1636 color[2] = bound(0, color2[2], 1);
1637 GL_Color(color[0], color[1], color[2], 1);
1638 R_Mesh_Draw(numverts, numtriangles, elements);
1640 c_rt_lighttris += numtriangles;
1643 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1645 // 2/0/0/2 3D combine blendsquare path
1646 memset(&m, 0, sizeof(m));
1647 m.tex[0] = R_GetTexture(bumptexture);
1648 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1649 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1650 m.pointer_texcoord[0] = texcoord2f;
1651 m.pointer_texcoord[1] = varray_texcoord3f[1];
1652 R_Mesh_State_Texture(&m);
1653 qglColorMask(0,0,0,1);
1654 // this squares the result
1655 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1656 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1657 R_Mesh_Draw(numverts, numtriangles, elements);
1659 c_rt_lighttris += numtriangles;
1661 memset(&m, 0, sizeof(m));
1662 R_Mesh_State_Texture(&m);
1663 // square alpha in framebuffer a few times to make it shiny
1664 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1665 // these comments are a test run through this math for intensity 0.5
1666 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1667 // 0.25 * 0.25 = 0.0625 (this is another pass)
1668 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1669 R_Mesh_Draw(numverts, numtriangles, elements);
1671 c_rt_lighttris += numtriangles;
1672 R_Mesh_Draw(numverts, numtriangles, elements);
1674 c_rt_lighttris += numtriangles;
1676 memset(&m, 0, sizeof(m));
1677 m.tex[0] = R_GetTexture(glosstexture);
1678 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1679 m.pointer_texcoord[0] = texcoord2f;
1680 m.pointer_texcoord[1] = varray_texcoord3f[1];
1681 R_Mesh_State_Texture(&m);
1682 qglColorMask(1,1,1,0);
1683 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1684 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1685 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1686 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1688 color[0] = bound(0, color2[0], 1);
1689 color[1] = bound(0, color2[1], 1);
1690 color[2] = bound(0, color2[2], 1);
1691 GL_Color(color[0], color[1], color[2], 1);
1692 R_Mesh_Draw(numverts, numtriangles, elements);
1694 c_rt_lighttris += numtriangles;
1697 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1699 // 2/0/0/2/2 2D combine blendsquare path
1700 memset(&m, 0, sizeof(m));
1701 m.tex[0] = R_GetTexture(bumptexture);
1702 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1703 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1704 m.pointer_texcoord[0] = texcoord2f;
1705 m.pointer_texcoord[1] = varray_texcoord3f[1];
1706 R_Mesh_State_Texture(&m);
1707 qglColorMask(0,0,0,1);
1708 // this squares the result
1709 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1710 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1711 R_Mesh_Draw(numverts, numtriangles, elements);
1713 c_rt_lighttris += numtriangles;
1715 memset(&m, 0, sizeof(m));
1716 R_Mesh_State_Texture(&m);
1717 // square alpha in framebuffer a few times to make it shiny
1718 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1719 // these comments are a test run through this math for intensity 0.5
1720 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1721 // 0.25 * 0.25 = 0.0625 (this is another pass)
1722 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1723 R_Mesh_Draw(numverts, numtriangles, elements);
1725 c_rt_lighttris += numtriangles;
1726 R_Mesh_Draw(numverts, numtriangles, elements);
1728 c_rt_lighttris += numtriangles;
1730 memset(&m, 0, sizeof(m));
1731 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1732 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1733 m.pointer_texcoord[0] = varray_texcoord2f[0];
1734 m.pointer_texcoord[1] = varray_texcoord2f[1];
1735 R_Mesh_State_Texture(&m);
1736 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1737 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1738 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1739 R_Mesh_Draw(numverts, numtriangles, elements);
1741 c_rt_lighttris += numtriangles;
1743 memset(&m, 0, sizeof(m));
1744 m.tex[0] = R_GetTexture(glosstexture);
1745 m.texcubemap[1] = R_GetTexture(lightcubemap);
1746 m.pointer_texcoord[0] = texcoord2f;
1747 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1748 R_Mesh_State_Texture(&m);
1749 qglColorMask(1,1,1,0);
1750 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1752 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1753 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1754 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1756 color[0] = bound(0, color2[0], 1);
1757 color[1] = bound(0, color2[1], 1);
1758 color[2] = bound(0, color2[2], 1);
1759 GL_Color(color[0], color[1], color[2], 1);
1760 R_Mesh_Draw(numverts, numtriangles, elements);
1762 c_rt_lighttris += numtriangles;
1768 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1770 R_Mesh_Matrix(matrix);
1771 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1774 cvar_t r_editlights = {0, "r_editlights", "0"};
1775 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1776 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1777 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1778 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1779 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1780 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1781 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1782 worldlight_t *r_shadow_worldlightchain;
1783 worldlight_t *r_shadow_selectedlight;
1784 vec3_t r_editlights_cursorlocation;
1786 static int castshadowcount = 1;
1787 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1789 int i, j, k, l, maxverts = 256, *mark, tris;
1790 float *vertex3f = NULL;
1792 shadowmesh_t *mesh, *castmesh;
1796 surfmesh_t *surfmesh;
1798 if (radius < 15 || DotProduct(color, color) < 0.03)
1800 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1804 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1805 VectorCopy(origin, e->origin);
1806 VectorCopy(color, e->light);
1807 e->lightradius = radius;
1809 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1811 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1814 e->castshadows = castshadow;
1816 e->cullradius = e->lightradius;
1817 for (k = 0;k < 3;k++)
1819 e->mins[k] = e->origin[k] - e->lightradius;
1820 e->maxs[k] = e->origin[k] + e->lightradius;
1823 e->next = r_shadow_worldlightchain;
1824 r_shadow_worldlightchain = e;
1825 if (cubemapname && cubemapname[0])
1827 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1828 strcpy(e->cubemapname, cubemapname);
1829 // FIXME: add cubemap loading (and don't load a cubemap twice)
1834 i = cl.worldmodel->PointContents(cl.worldmodel, e->origin);
1835 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1838 qbyte *bytesurfacepvs;
1840 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1841 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1843 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1845 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1846 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1847 leaf->worldnodeframe = castshadowcount;
1849 for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1850 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1851 surf->castshadow = castshadowcount;
1853 Mem_Free(byteleafpvs);
1854 Mem_Free(bytesurfacepvs);
1858 leaf = cl.worldmodel->PointInLeaf(cl.worldmodel, origin);
1859 pvs = cl.worldmodel->LeafPVS(cl.worldmodel, leaf);
1860 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1862 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1864 leaf->worldnodeframe = castshadowcount;
1865 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1867 surf = cl.worldmodel->surfaces + *mark;
1868 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1869 surf->castshadow = castshadowcount;
1876 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1877 if (leaf->worldnodeframe == castshadowcount)
1880 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1881 if (surf->castshadow == castshadowcount)
1885 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1887 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1889 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1890 if (leaf->worldnodeframe == castshadowcount)
1891 e->leafs[e->numleafs++] = leaf;
1893 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1894 if (surf->castshadow == castshadowcount)
1895 e->surfaces[e->numsurfaces++] = surf;
1897 // find bounding box of lit leafs
1898 VectorCopy(e->origin, e->mins);
1899 VectorCopy(e->origin, e->maxs);
1900 for (j = 0;j < e->numleafs;j++)
1903 for (k = 0;k < 3;k++)
1905 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1906 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1910 for (k = 0;k < 3;k++)
1912 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1913 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1915 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1920 for (j = 0;j < e->numsurfaces;j++)
1922 surf = e->surfaces[j];
1923 if (surf->flags & SURF_SHADOWCAST)
1925 surf->castshadow = castshadowcount;
1926 if (maxverts < surf->poly_numverts)
1927 maxverts = surf->poly_numverts;
1930 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1931 // make a mesh to cast a shadow volume from
1932 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1933 for (j = 0;j < e->numsurfaces;j++)
1934 if (e->surfaces[j]->castshadow == castshadowcount)
1935 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1936 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1937 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1939 // cast shadow volume from castmesh
1940 for (mesh = castmesh;mesh;mesh = mesh->next)
1942 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1944 if (maxverts < castmesh->numverts * 2)
1946 maxverts = castmesh->numverts * 2;
1951 if (vertex3f == NULL && maxverts > 0)
1952 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1954 // now that we have the buffers big enough, construct and add
1955 // the shadow volume mesh
1956 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)))
1957 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1962 // we're done with castmesh now
1963 Mod_ShadowMesh_Free(castmesh);
1964 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1965 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1966 l += mesh->numtriangles;
1967 Con_Printf("static shadow volume built containing %i triangles\n", l);
1970 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);
1973 void R_Shadow_FreeWorldLight(worldlight_t *light)
1975 worldlight_t **lightpointer;
1976 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1977 if (*lightpointer != light)
1978 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1979 *lightpointer = light->next;
1980 if (light->cubemapname)
1981 Mem_Free(light->cubemapname);
1982 if (light->shadowvolume)
1983 Mod_ShadowMesh_Free(light->shadowvolume);
1984 if (light->surfaces)
1985 Mem_Free(light->surfaces);
1987 Mem_Free(light->leafs);
1991 void R_Shadow_ClearWorldLights(void)
1993 while (r_shadow_worldlightchain)
1994 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1995 r_shadow_selectedlight = NULL;
1998 void R_Shadow_SelectLight(worldlight_t *light)
2000 if (r_shadow_selectedlight)
2001 r_shadow_selectedlight->selected = false;
2002 r_shadow_selectedlight = light;
2003 if (r_shadow_selectedlight)
2004 r_shadow_selectedlight->selected = true;
2007 rtexture_t *lighttextures[5];
2009 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2011 float scale = r_editlights_cursorgrid.value * 0.5f;
2012 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2015 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2018 const worldlight_t *light;
2021 if (light->selected)
2022 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2023 if (!light->shadowvolume)
2025 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2028 void R_Shadow_DrawLightSprites(void)
2032 worldlight_t *light;
2034 for (i = 0;i < 5;i++)
2036 lighttextures[i] = NULL;
2037 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2038 lighttextures[i] = pic->tex;
2041 for (light = r_shadow_worldlightchain;light;light = light->next)
2042 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2043 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2046 void R_Shadow_SelectLightInView(void)
2048 float bestrating, rating, temp[3];
2049 worldlight_t *best, *light;
2052 for (light = r_shadow_worldlightchain;light;light = light->next)
2054 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2055 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2058 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2059 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2061 bestrating = rating;
2066 R_Shadow_SelectLight(best);
2069 void R_Shadow_LoadWorldLights(void)
2071 int n, a, style, shadow;
2072 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2073 float origin[3], radius, color[3];
2074 if (cl.worldmodel == NULL)
2076 Con_Printf("No map loaded.\n");
2079 FS_StripExtension(cl.worldmodel->name, name);
2080 strcat(name, ".rtlights");
2081 lightsstring = FS_LoadFile(name, false);
2089 while (*s && *s != '\n')
2095 // check for modifier flags
2101 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);
2107 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);
2110 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2111 radius *= r_editlights_rtlightssizescale.value;
2112 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2117 Con_Printf("invalid rtlights file \"%s\"\n", name);
2118 Mem_Free(lightsstring);
2122 void R_Shadow_SaveWorldLights(void)
2124 worldlight_t *light;
2125 int bufchars, bufmaxchars;
2127 char name[MAX_QPATH];
2129 if (!r_shadow_worldlightchain)
2131 if (cl.worldmodel == NULL)
2133 Con_Printf("No map loaded.\n");
2136 FS_StripExtension(cl.worldmodel->name, name);
2137 strcat(name, ".rtlights");
2138 bufchars = bufmaxchars = 0;
2140 for (light = r_shadow_worldlightchain;light;light = light->next)
2142 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 : "");
2143 if (bufchars + (int) strlen(line) > bufmaxchars)
2145 bufmaxchars = bufchars + strlen(line) + 2048;
2147 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2151 memcpy(buf, oldbuf, bufchars);
2157 memcpy(buf + bufchars, line, strlen(line));
2158 bufchars += strlen(line);
2162 FS_WriteFile(name, buf, bufchars);
2167 void R_Shadow_LoadLightsFile(void)
2170 char name[MAX_QPATH], *lightsstring, *s, *t;
2171 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2172 if (cl.worldmodel == NULL)
2174 Con_Printf("No map loaded.\n");
2177 FS_StripExtension(cl.worldmodel->name, name);
2178 strcat(name, ".lights");
2179 lightsstring = FS_LoadFile(name, false);
2187 while (*s && *s != '\n')
2192 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);
2196 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);
2199 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2200 radius = bound(15, radius, 4096);
2201 VectorScale(color, (2.0f / (8388608.0f)), color);
2202 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2207 Con_Printf("invalid lights file \"%s\"\n", name);
2208 Mem_Free(lightsstring);
2212 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2214 int entnum, style, islight;
2215 char key[256], value[1024];
2216 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2219 if (cl.worldmodel == NULL)
2221 Con_Printf("No map loaded.\n");
2224 data = cl.worldmodel->entities;
2227 for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2230 origin[0] = origin[1] = origin[2] = 0;
2231 originhack[0] = originhack[1] = originhack[2] = 0;
2232 color[0] = color[1] = color[2] = 1;
2233 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2239 if (!COM_ParseToken(&data))
2241 if (com_token[0] == '}')
2242 break; // end of entity
2243 if (com_token[0] == '_')
2244 strcpy(key, com_token + 1);
2246 strcpy(key, com_token);
2247 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2248 key[strlen(key)-1] = 0;
2249 if (!COM_ParseToken(&data))
2251 strcpy(value, com_token);
2253 // now that we have the key pair worked out...
2254 if (!strcmp("light", key))
2255 light = atof(value);
2256 else if (!strcmp("origin", key))
2257 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2258 else if (!strcmp("color", key))
2259 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2260 else if (!strcmp("wait", key))
2261 scale = atof(value);
2262 else if (!strcmp("classname", key))
2264 if (!strncmp(value, "light", 5))
2267 if (!strcmp(value, "light_fluoro"))
2272 overridecolor[0] = 1;
2273 overridecolor[1] = 1;
2274 overridecolor[2] = 1;
2276 if (!strcmp(value, "light_fluorospark"))
2281 overridecolor[0] = 1;
2282 overridecolor[1] = 1;
2283 overridecolor[2] = 1;
2285 if (!strcmp(value, "light_globe"))
2290 overridecolor[0] = 1;
2291 overridecolor[1] = 0.8;
2292 overridecolor[2] = 0.4;
2294 if (!strcmp(value, "light_flame_large_yellow"))
2299 overridecolor[0] = 1;
2300 overridecolor[1] = 0.5;
2301 overridecolor[2] = 0.1;
2303 if (!strcmp(value, "light_flame_small_yellow"))
2308 overridecolor[0] = 1;
2309 overridecolor[1] = 0.5;
2310 overridecolor[2] = 0.1;
2312 if (!strcmp(value, "light_torch_small_white"))
2317 overridecolor[0] = 1;
2318 overridecolor[1] = 0.5;
2319 overridecolor[2] = 0.1;
2321 if (!strcmp(value, "light_torch_small_walltorch"))
2326 overridecolor[0] = 1;
2327 overridecolor[1] = 0.5;
2328 overridecolor[2] = 0.1;
2332 else if (!strcmp("style", key))
2333 style = atoi(value);
2335 if (light <= 0 && islight)
2337 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2338 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2339 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2340 VectorCopy(overridecolor, color);
2341 VectorScale(color, light, color);
2342 VectorAdd(origin, originhack, origin);
2344 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2349 void R_Shadow_SetCursorLocationForView(void)
2351 vec_t dist, push, frac;
2352 vec3_t dest, endpos, normal;
2353 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2354 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2357 dist = frac * r_editlights_cursordistance.value;
2358 push = r_editlights_cursorpushback.value;
2362 VectorMA(endpos, push, vpn, endpos);
2363 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2365 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2366 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2367 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2370 void R_Shadow_UpdateWorldLightSelection(void)
2372 R_Shadow_SetCursorLocationForView();
2373 if (r_editlights.integer)
2375 R_Shadow_SelectLightInView();
2376 R_Shadow_DrawLightSprites();
2379 R_Shadow_SelectLight(NULL);
2382 void R_Shadow_EditLights_Clear_f(void)
2384 R_Shadow_ClearWorldLights();
2387 void R_Shadow_EditLights_Reload_f(void)
2389 r_shadow_reloadlights = true;
2392 void R_Shadow_EditLights_Save_f(void)
2395 R_Shadow_SaveWorldLights();
2398 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2400 R_Shadow_ClearWorldLights();
2401 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2404 void R_Shadow_EditLights_ImportLightsFile_f(void)
2406 R_Shadow_ClearWorldLights();
2407 R_Shadow_LoadLightsFile();
2410 void R_Shadow_EditLights_Spawn_f(void)
2413 if (!r_editlights.integer)
2415 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2418 if (Cmd_Argc() != 1)
2420 Con_Printf("r_editlights_spawn does not take parameters\n");
2423 color[0] = color[1] = color[2] = 1;
2424 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2427 void R_Shadow_EditLights_Edit_f(void)
2429 vec3_t origin, color;
2432 char cubemapname[1024];
2433 if (!r_editlights.integer)
2435 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2438 if (!r_shadow_selectedlight)
2440 Con_Printf("No selected light.\n");
2443 VectorCopy(r_shadow_selectedlight->origin, origin);
2444 radius = r_shadow_selectedlight->lightradius;
2445 VectorCopy(r_shadow_selectedlight->light, color);
2446 style = r_shadow_selectedlight->style;
2447 if (r_shadow_selectedlight->cubemapname)
2448 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2451 shadows = r_shadow_selectedlight->castshadows;
2452 if (!strcmp(Cmd_Argv(1), "origin"))
2454 if (Cmd_Argc() != 5)
2456 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2459 origin[0] = atof(Cmd_Argv(2));
2460 origin[1] = atof(Cmd_Argv(3));
2461 origin[2] = atof(Cmd_Argv(4));
2463 else if (!strcmp(Cmd_Argv(1), "originx"))
2465 if (Cmd_Argc() != 3)
2467 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2470 origin[0] = atof(Cmd_Argv(2));
2472 else if (!strcmp(Cmd_Argv(1), "originy"))
2474 if (Cmd_Argc() != 3)
2476 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2479 origin[1] = atof(Cmd_Argv(2));
2481 else if (!strcmp(Cmd_Argv(1), "originz"))
2483 if (Cmd_Argc() != 3)
2485 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2488 origin[2] = atof(Cmd_Argv(2));
2490 else if (!strcmp(Cmd_Argv(1), "move"))
2492 if (Cmd_Argc() != 5)
2494 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2497 origin[0] += atof(Cmd_Argv(2));
2498 origin[1] += atof(Cmd_Argv(3));
2499 origin[2] += atof(Cmd_Argv(4));
2501 else if (!strcmp(Cmd_Argv(1), "movex"))
2503 if (Cmd_Argc() != 3)
2505 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2508 origin[0] += atof(Cmd_Argv(2));
2510 else if (!strcmp(Cmd_Argv(1), "movey"))
2512 if (Cmd_Argc() != 3)
2514 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2517 origin[1] += atof(Cmd_Argv(2));
2519 else if (!strcmp(Cmd_Argv(1), "movez"))
2521 if (Cmd_Argc() != 3)
2523 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2526 origin[2] += atof(Cmd_Argv(2));
2528 else if (!strcmp(Cmd_Argv(1), "color"))
2530 if (Cmd_Argc() != 5)
2532 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2535 color[0] = atof(Cmd_Argv(2));
2536 color[1] = atof(Cmd_Argv(3));
2537 color[2] = atof(Cmd_Argv(4));
2539 else if (!strcmp(Cmd_Argv(1), "radius"))
2541 if (Cmd_Argc() != 3)
2543 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2546 radius = atof(Cmd_Argv(2));
2548 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2550 if (Cmd_Argc() != 3)
2552 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2555 style = atoi(Cmd_Argv(2));
2557 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2561 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2564 if (Cmd_Argc() == 3)
2565 strcpy(cubemapname, Cmd_Argv(2));
2569 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2571 if (Cmd_Argc() != 3)
2573 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2576 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2580 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2581 Con_Printf("Selected light's properties:\n");
2582 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2583 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2584 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2585 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2586 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2587 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2590 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2591 r_shadow_selectedlight = NULL;
2592 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2595 extern int con_vislines;
2596 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2600 if (r_shadow_selectedlight == NULL)
2604 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2605 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;
2606 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2607 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;
2608 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2609 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2610 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;
2613 void R_Shadow_EditLights_ToggleShadow_f(void)
2615 if (!r_editlights.integer)
2617 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2620 if (!r_shadow_selectedlight)
2622 Con_Printf("No selected light.\n");
2625 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);
2626 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2627 r_shadow_selectedlight = NULL;
2630 void R_Shadow_EditLights_Remove_f(void)
2632 if (!r_editlights.integer)
2634 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2637 if (!r_shadow_selectedlight)
2639 Con_Printf("No selected light.\n");
2642 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2643 r_shadow_selectedlight = NULL;
2646 void R_Shadow_EditLights_Init(void)
2648 Cvar_RegisterVariable(&r_editlights);
2649 Cvar_RegisterVariable(&r_editlights_cursordistance);
2650 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2651 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2652 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2653 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2654 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2655 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2656 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2657 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2658 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2659 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2660 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2661 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2662 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2663 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2664 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);