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[3] = vertexremap[e[0]];
450 outelement3i[4] = vertexremap[e[1]];
451 outelement3i[5] = vertexremap[e[2]];
452 outelement3i[0] = vertexremap[e[2]] + 1;
453 outelement3i[1] = vertexremap[e[1]] + 1;
454 outelement3i[2] = 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, 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)
560 memset(&m, 0, sizeof(m));
561 if (r_shadowstage == SHADOWSTAGE_STENCIL)
563 // increment stencil if backface is behind depthbuffer
564 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
565 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
566 for (mesh = firstmesh;mesh;mesh = mesh->next)
568 GL_VertexPointer(mesh->vertex3f);
569 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
570 c_rtcached_shadowmeshes++;
571 c_rtcached_shadowtris += mesh->numtriangles;
573 // decrement stencil if frontface is behind depthbuffer
574 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
575 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
577 for (mesh = firstmesh;mesh;mesh = mesh->next)
579 GL_VertexPointer(mesh->vertex3f);
580 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
581 c_rtcached_shadowmeshes++;
582 c_rtcached_shadowtris += mesh->numtriangles;
586 float r_shadow_attenpower, r_shadow_attenscale;
587 static void R_Shadow_MakeTextures(void)
589 int x, y, z, d, side;
590 float v[3], s, t, intensity;
592 R_FreeTexturePool(&r_shadow_texturepool);
593 r_shadow_texturepool = R_AllocTexturePool();
594 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
595 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
597 #define ATTEN2DSIZE 64
598 #define ATTEN3DSIZE 32
599 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
604 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
609 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
614 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
615 if (gl_texturecubemap)
617 for (side = 0;side < 6;side++)
619 for (y = 0;y < NORMSIZE;y++)
621 for (x = 0;x < NORMSIZE;x++)
623 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
624 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
658 intensity = 127.0f / sqrt(DotProduct(v, v));
659 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
660 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
661 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
662 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
666 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
669 r_shadow_normalcubetexture = NULL;
670 for (y = 0;y < ATTEN2DSIZE;y++)
672 for (x = 0;x < ATTEN2DSIZE;x++)
674 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
675 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
677 intensity = 1.0f - sqrt(DotProduct(v, v));
679 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
680 d = bound(0, intensity, 255);
681 data[(y*ATTEN2DSIZE+x)*4+0] = d;
682 data[(y*ATTEN2DSIZE+x)*4+1] = d;
683 data[(y*ATTEN2DSIZE+x)*4+2] = d;
684 data[(y*ATTEN2DSIZE+x)*4+3] = d;
687 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
688 if (r_shadow_texture3d.integer)
690 for (z = 0;z < ATTEN3DSIZE;z++)
692 for (y = 0;y < ATTEN3DSIZE;y++)
694 for (x = 0;x < ATTEN3DSIZE;x++)
696 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
697 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
698 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
699 intensity = 1.0f - sqrt(DotProduct(v, v));
701 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
702 d = bound(0, intensity, 255);
703 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
704 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
705 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
706 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
710 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
715 void R_Shadow_Stage_Begin(void)
719 if (r_shadow_texture3d.integer && !gl_texture3d)
720 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
722 //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
723 if (!r_shadow_attenuation2dtexture
724 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
725 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
726 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
727 R_Shadow_MakeTextures();
729 GL_BlendFunc(GL_ONE, GL_ZERO);
732 GL_Color(0, 0, 0, 1);
734 memset(&m, 0, sizeof(m));
735 R_Mesh_State_Texture(&m);
737 qglDisable(GL_SCISSOR_TEST);
738 r_shadowstage = SHADOWSTAGE_NONE;
740 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
741 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
742 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
745 void R_Shadow_LoadWorldLightsIfNeeded(void)
747 if (r_shadow_reloadlights && cl.worldmodel)
749 R_Shadow_ClearWorldLights();
750 r_shadow_reloadlights = false;
751 R_Shadow_LoadWorldLights();
752 if (r_shadow_worldlightchain == NULL)
754 R_Shadow_LoadLightsFile();
755 if (r_shadow_worldlightchain == NULL)
756 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
761 void R_Shadow_Stage_ShadowVolumes(void)
765 memset(&m, 0, sizeof(m));
766 R_Mesh_State_Texture(&m);
768 GL_Color(1, 1, 1, 1);
769 GL_BlendFunc(GL_ONE, GL_ZERO);
773 if (r_shadow_polygonoffset.value != 0)
775 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
776 qglEnable(GL_POLYGON_OFFSET_FILL);
779 qglDisable(GL_POLYGON_OFFSET_FILL);
780 qglColorMask(0, 0, 0, 0);
781 qglDepthFunc(GL_LESS);
782 qglEnable(GL_STENCIL_TEST);
783 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
784 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
785 r_shadowstage = SHADOWSTAGE_STENCIL;
787 qglClear(GL_STENCIL_BUFFER_BIT);
789 // LordHavoc note: many shadow volumes reside entirely inside the world
790 // (that is to say they are entirely bounded by their lit surfaces),
791 // which can be optimized by handling things as an inverted light volume,
792 // with the shadow boundaries of the world being simulated by an altered
793 // (129) bias to stencil clearing on such lights
794 // FIXME: generate inverted light volumes for use as shadow volumes and
795 // optimize for them as noted above
798 void R_Shadow_Stage_LightWithoutShadows(void)
802 memset(&m, 0, sizeof(m));
803 R_Mesh_State_Texture(&m);
805 GL_Color(1, 1, 1, 1);
806 GL_BlendFunc(GL_ONE, GL_ONE);
809 qglDisable(GL_POLYGON_OFFSET_FILL);
811 //GL_DepthTest(false);
813 qglColorMask(1, 1, 1, 1);
814 qglDepthFunc(GL_LEQUAL);
815 qglDisable(GL_STENCIL_TEST);
816 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
817 qglStencilFunc(GL_EQUAL, 128, 0xFF);
819 r_shadowstage = SHADOWSTAGE_LIGHT;
823 void R_Shadow_Stage_LightWithShadows(void)
827 memset(&m, 0, sizeof(m));
828 R_Mesh_State_Texture(&m);
830 GL_Color(1, 1, 1, 1);
831 GL_BlendFunc(GL_ONE, GL_ONE);
834 qglDisable(GL_POLYGON_OFFSET_FILL);
836 //GL_DepthTest(false);
838 qglColorMask(1, 1, 1, 1);
839 qglDepthFunc(GL_LEQUAL);
840 qglEnable(GL_STENCIL_TEST);
841 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
842 // only draw light where this geometry was already rendered AND the
843 // stencil is 128 (values other than this mean shadow)
844 qglStencilFunc(GL_EQUAL, 128, 0xFF);
846 r_shadowstage = SHADOWSTAGE_LIGHT;
850 void R_Shadow_Stage_End(void)
854 memset(&m, 0, sizeof(m));
855 R_Mesh_State_Texture(&m);
857 GL_Color(1, 1, 1, 1);
858 GL_BlendFunc(GL_ONE, GL_ZERO);
861 qglDisable(GL_POLYGON_OFFSET_FILL);
863 qglColorMask(1, 1, 1, 1);
864 qglDisable(GL_SCISSOR_TEST);
865 qglDepthFunc(GL_LEQUAL);
866 qglDisable(GL_STENCIL_TEST);
867 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
868 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
870 r_shadowstage = SHADOWSTAGE_NONE;
874 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
876 int i, ix1, iy1, ix2, iy2;
877 float x1, y1, x2, y2, x, y;
880 if (!r_shadow_scissor.integer)
882 // if view is inside the box, just say yes it's visible
883 if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
884 && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
885 && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
887 qglDisable(GL_SCISSOR_TEST);
890 VectorSubtract(r_origin, origin, v);
891 if (DotProduct(v, v) < radius * radius)
893 qglDisable(GL_SCISSOR_TEST);
896 // create viewspace bbox
897 for (i = 0;i < 8;i++)
899 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
900 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
901 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
902 v2[0] = DotProduct(v, vright);
903 v2[1] = DotProduct(v, vup);
904 v2[2] = DotProduct(v, vpn);
907 if (smins[0] > v2[0]) smins[0] = v2[0];
908 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
909 if (smins[1] > v2[1]) smins[1] = v2[1];
910 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
911 if (smins[2] > v2[2]) smins[2] = v2[2];
912 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
916 smins[0] = smaxs[0] = v2[0];
917 smins[1] = smaxs[1] = v2[1];
918 smins[2] = smaxs[2] = v2[2];
921 // now we have a bbox in viewspace
922 // clip it to the viewspace version of the sphere
923 v[0] = origin[0] - r_origin[0];
924 v[1] = origin[1] - r_origin[1];
925 v[2] = origin[2] - r_origin[2];
926 v2[0] = DotProduct(v, vright);
927 v2[1] = DotProduct(v, vup);
928 v2[2] = DotProduct(v, vpn);
929 if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
930 if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
931 if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
932 if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
933 if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
934 if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
935 // clip it to the view plane
938 // return true if that culled the box
939 if (smins[2] >= smaxs[2])
941 // ok some of it is infront of the view, transform each corner back to
942 // worldspace and then to screenspace and make screen rect
943 // initialize these variables just to avoid compiler warnings
944 x1 = y1 = x2 = y2 = 0;
945 for (i = 0;i < 8;i++)
947 v2[0] = (i & 1) ? smins[0] : smaxs[0];
948 v2[1] = (i & 2) ? smins[1] : smaxs[1];
949 v2[2] = (i & 4) ? smins[2] : smaxs[2];
950 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
951 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
952 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
954 GL_TransformToScreen(v, v2);
955 //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]);
972 // this code doesn't handle boxes with any points behind view properly
973 x1 = 1000;x2 = -1000;
974 y1 = 1000;y2 = -1000;
975 for (i = 0;i < 8;i++)
977 v[0] = (i & 1) ? mins[0] : maxs[0];
978 v[1] = (i & 2) ? mins[1] : maxs[1];
979 v[2] = (i & 4) ? mins[2] : maxs[2];
981 GL_TransformToScreen(v, v2);
982 //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]);
999 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1000 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1001 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1002 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1003 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1004 if (ix2 <= ix1 || iy2 <= iy1)
1006 // set up the scissor rectangle
1007 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1008 qglEnable(GL_SCISSOR_TEST);
1014 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1016 int i, ix1, iy1, ix2, iy2;
1017 float x1, y1, x2, y2, x, y, f;
1018 vec3_t smins, smaxs;
1020 if (!r_shadow_scissor.integer)
1022 // if view is inside the box, just say yes it's visible
1023 // LordHavoc: for some odd reason scissor seems broken without stencil
1024 // (?!? seems like a driver bug) so abort if gl_stencil is false
1025 if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1027 qglDisable(GL_SCISSOR_TEST);
1030 for (i = 0;i < 3;i++)
1043 f = DotProduct(vpn, r_origin) + 1;
1044 if (DotProduct(vpn, v2) <= f)
1046 // entirely behind nearclip plane
1049 if (DotProduct(vpn, v) >= f)
1051 // entirely infront of nearclip plane
1052 x1 = y1 = x2 = y2 = 0;
1053 for (i = 0;i < 8;i++)
1055 v[0] = (i & 1) ? mins[0] : maxs[0];
1056 v[1] = (i & 2) ? mins[1] : maxs[1];
1057 v[2] = (i & 4) ? mins[2] : maxs[2];
1059 GL_TransformToScreen(v, v2);
1060 //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]);
1079 // clipped by nearclip plane
1080 // this is nasty and crude...
1081 // create viewspace bbox
1082 for (i = 0;i < 8;i++)
1084 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1085 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1086 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1087 v2[0] = DotProduct(v, vright);
1088 v2[1] = DotProduct(v, vup);
1089 v2[2] = DotProduct(v, vpn);
1092 if (smins[0] > v2[0]) smins[0] = v2[0];
1093 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1094 if (smins[1] > v2[1]) smins[1] = v2[1];
1095 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1096 if (smins[2] > v2[2]) smins[2] = v2[2];
1097 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1101 smins[0] = smaxs[0] = v2[0];
1102 smins[1] = smaxs[1] = v2[1];
1103 smins[2] = smaxs[2] = v2[2];
1106 // now we have a bbox in viewspace
1107 // clip it to the view plane
1110 // return true if that culled the box
1111 if (smins[2] >= smaxs[2])
1113 // ok some of it is infront of the view, transform each corner back to
1114 // worldspace and then to screenspace and make screen rect
1115 // initialize these variables just to avoid compiler warnings
1116 x1 = y1 = x2 = y2 = 0;
1117 for (i = 0;i < 8;i++)
1119 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1120 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1121 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1122 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1123 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1124 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1126 GL_TransformToScreen(v, v2);
1127 //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]);
1144 // this code doesn't handle boxes with any points behind view properly
1145 x1 = 1000;x2 = -1000;
1146 y1 = 1000;y2 = -1000;
1147 for (i = 0;i < 8;i++)
1149 v[0] = (i & 1) ? mins[0] : maxs[0];
1150 v[1] = (i & 2) ? mins[1] : maxs[1];
1151 v[2] = (i & 4) ? mins[2] : maxs[2];
1153 GL_TransformToScreen(v, v2);
1154 //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]);
1172 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1173 if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1174 if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1175 if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1176 if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1177 if (ix2 <= ix1 || iy2 <= iy1)
1179 // set up the scissor rectangle
1180 qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1181 qglEnable(GL_SCISSOR_TEST);
1186 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1188 float *color4f = varray_color4f;
1189 float dist, dot, intensity, v[3], n[3];
1190 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1192 Matrix4x4_Transform(m, vertex3f, v);
1193 if ((dist = DotProduct(v, v)) < 1)
1195 Matrix4x4_Transform3x3(m, normal3f, n);
1196 if ((dot = DotProduct(n, v)) > 0)
1199 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1200 VectorScale(lightcolor, intensity, color4f);
1205 VectorClear(color4f);
1211 VectorClear(color4f);
1217 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1219 float *color4f = varray_color4f;
1220 float dist, dot, intensity, v[3], n[3];
1221 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1223 Matrix4x4_Transform(m, vertex3f, v);
1224 if ((dist = fabs(v[2])) < 1)
1226 Matrix4x4_Transform3x3(m, normal3f, n);
1227 if ((dot = DotProduct(n, v)) > 0)
1229 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1230 VectorScale(lightcolor, intensity, color4f);
1235 VectorClear(color4f);
1241 VectorClear(color4f);
1247 // FIXME: this should be done in a vertex program when possible
1248 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1249 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1253 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1254 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1255 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1262 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1266 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1267 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1274 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)
1278 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1280 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1281 // the cubemap normalizes this for us
1282 out3f[0] = DotProduct(svector3f, lightdir);
1283 out3f[1] = DotProduct(tvector3f, lightdir);
1284 out3f[2] = DotProduct(normal3f, lightdir);
1288 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)
1291 float lightdir[3], eyedir[3], halfdir[3];
1292 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1294 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1295 VectorNormalizeFast(lightdir);
1296 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1297 VectorNormalizeFast(eyedir);
1298 VectorAdd(lightdir, eyedir, halfdir);
1299 // the cubemap normalizes this for us
1300 out3f[0] = DotProduct(svector3f, halfdir);
1301 out3f[1] = DotProduct(tvector3f, halfdir);
1302 out3f[2] = DotProduct(normal3f, halfdir);
1306 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)
1309 float color[3], color2[3];
1311 GL_VertexPointer(vertex3f);
1312 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1315 bumptexture = r_shadow_blankbumptexture;
1317 // colorscale accounts for how much we multiply the brightness during combine
1318 // mult is how many times the final pass of the lighting will be
1319 // performed to get more brightness than otherwise possible
1320 // limit mult to 64 for sanity sake
1321 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1323 // 3/2 3D combine path (Geforce3, Radeon 8500)
1324 memset(&m, 0, sizeof(m));
1325 m.tex[0] = R_GetTexture(bumptexture);
1326 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1327 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1328 m.texcombinergb[0] = GL_REPLACE;
1329 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1330 m.pointer_texcoord[0] = texcoord2f;
1331 m.pointer_texcoord[1] = varray_texcoord3f[1];
1332 m.pointer_texcoord[2] = varray_texcoord3f[2];
1333 R_Mesh_State_Texture(&m);
1334 qglColorMask(0,0,0,1);
1335 GL_BlendFunc(GL_ONE, GL_ZERO);
1336 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1337 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1338 R_Mesh_Draw(numverts, numtriangles, elements);
1340 c_rt_lighttris += numtriangles;
1342 memset(&m, 0, sizeof(m));
1343 m.tex[0] = R_GetTexture(basetexture);
1344 m.texcubemap[1] = R_GetTexture(lightcubemap);
1345 m.pointer_texcoord[0] = texcoord2f;
1346 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1347 R_Mesh_State_Texture(&m);
1348 qglColorMask(1,1,1,0);
1349 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1351 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1352 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1353 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1355 color[0] = bound(0, color2[0], 1);
1356 color[1] = bound(0, color2[1], 1);
1357 color[2] = bound(0, color2[2], 1);
1358 GL_Color(color[0], color[1], color[2], 1);
1359 R_Mesh_Draw(numverts, numtriangles, elements);
1361 c_rt_lighttris += numtriangles;
1364 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1366 // 1/2/2 3D combine path (original Radeon)
1367 memset(&m, 0, sizeof(m));
1368 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1369 m.pointer_texcoord[0] = varray_texcoord3f[0];
1370 R_Mesh_State_Texture(&m);
1371 qglColorMask(0,0,0,1);
1372 GL_BlendFunc(GL_ONE, GL_ZERO);
1373 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1374 R_Mesh_Draw(numverts, numtriangles, elements);
1376 c_rt_lighttris += numtriangles;
1378 memset(&m, 0, sizeof(m));
1379 m.tex[0] = R_GetTexture(bumptexture);
1380 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1381 m.texcombinergb[0] = GL_REPLACE;
1382 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1383 m.pointer_texcoord[0] = texcoord2f;
1384 m.pointer_texcoord[1] = varray_texcoord3f[1];
1385 R_Mesh_State_Texture(&m);
1386 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1387 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1388 R_Mesh_Draw(numverts, numtriangles, elements);
1390 c_rt_lighttris += numtriangles;
1392 memset(&m, 0, sizeof(m));
1393 m.tex[0] = R_GetTexture(basetexture);
1394 m.texcubemap[1] = R_GetTexture(lightcubemap);
1395 m.pointer_texcoord[0] = texcoord2f;
1396 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1397 R_Mesh_State_Texture(&m);
1398 qglColorMask(1,1,1,0);
1399 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1401 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1402 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1403 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1405 color[0] = bound(0, color2[0], 1);
1406 color[1] = bound(0, color2[1], 1);
1407 color[2] = bound(0, color2[2], 1);
1408 GL_Color(color[0], color[1], color[2], 1);
1409 R_Mesh_Draw(numverts, numtriangles, elements);
1411 c_rt_lighttris += numtriangles;
1414 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1416 // 2/2 3D combine path (original Radeon)
1417 memset(&m, 0, sizeof(m));
1418 m.tex[0] = R_GetTexture(bumptexture);
1419 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1420 m.texcombinergb[0] = GL_REPLACE;
1421 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1422 m.pointer_texcoord[0] = texcoord2f;
1423 m.pointer_texcoord[1] = varray_texcoord3f[1];
1424 R_Mesh_State_Texture(&m);
1425 qglColorMask(0,0,0,1);
1426 GL_BlendFunc(GL_ONE, GL_ZERO);
1427 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1428 R_Mesh_Draw(numverts, numtriangles, elements);
1430 c_rt_lighttris += numtriangles;
1432 memset(&m, 0, sizeof(m));
1433 m.tex[0] = R_GetTexture(basetexture);
1434 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1435 m.pointer_texcoord[0] = texcoord2f;
1436 m.pointer_texcoord[1] = varray_texcoord3f[1];
1437 R_Mesh_State_Texture(&m);
1438 qglColorMask(1,1,1,0);
1439 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1440 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1441 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1442 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1444 color[0] = bound(0, color2[0], 1);
1445 color[1] = bound(0, color2[1], 1);
1446 color[2] = bound(0, color2[2], 1);
1447 GL_Color(color[0], color[1], color[2], 1);
1448 R_Mesh_Draw(numverts, numtriangles, elements);
1450 c_rt_lighttris += numtriangles;
1453 else if (r_textureunits.integer >= 4)
1455 // 4/2 2D combine path (Geforce3, Radeon 8500)
1456 memset(&m, 0, sizeof(m));
1457 m.tex[0] = R_GetTexture(bumptexture);
1458 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1459 m.texcombinergb[0] = GL_REPLACE;
1460 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1461 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1462 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1463 m.pointer_texcoord[0] = texcoord2f;
1464 m.pointer_texcoord[1] = varray_texcoord3f[1];
1465 m.pointer_texcoord[2] = varray_texcoord2f[2];
1466 m.pointer_texcoord[3] = varray_texcoord2f[3];
1467 R_Mesh_State_Texture(&m);
1468 qglColorMask(0,0,0,1);
1469 GL_BlendFunc(GL_ONE, GL_ZERO);
1470 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1471 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1472 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1473 R_Mesh_Draw(numverts, numtriangles, elements);
1475 c_rt_lighttris += numtriangles;
1477 memset(&m, 0, sizeof(m));
1478 m.tex[0] = R_GetTexture(basetexture);
1479 m.texcubemap[1] = R_GetTexture(lightcubemap);
1480 m.pointer_texcoord[0] = texcoord2f;
1481 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1482 R_Mesh_State_Texture(&m);
1483 qglColorMask(1,1,1,0);
1484 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1486 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1487 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1488 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1490 color[0] = bound(0, color2[0], 1);
1491 color[1] = bound(0, color2[1], 1);
1492 color[2] = bound(0, color2[2], 1);
1493 GL_Color(color[0], color[1], color[2], 1);
1494 R_Mesh_Draw(numverts, numtriangles, elements);
1496 c_rt_lighttris += numtriangles;
1501 // 2/2/2 2D combine path (any dot3 card)
1502 memset(&m, 0, sizeof(m));
1503 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1504 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1505 m.pointer_texcoord[0] = varray_texcoord2f[0];
1506 m.pointer_texcoord[1] = varray_texcoord2f[1];
1507 R_Mesh_State_Texture(&m);
1508 qglColorMask(0,0,0,1);
1509 GL_BlendFunc(GL_ONE, GL_ZERO);
1510 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1511 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1512 R_Mesh_Draw(numverts, numtriangles, elements);
1514 c_rt_lighttris += numtriangles;
1516 memset(&m, 0, sizeof(m));
1517 m.tex[0] = R_GetTexture(bumptexture);
1518 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1519 m.texcombinergb[0] = GL_REPLACE;
1520 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1521 m.pointer_texcoord[0] = texcoord2f;
1522 m.pointer_texcoord[1] = varray_texcoord3f[1];
1523 R_Mesh_State_Texture(&m);
1524 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1525 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1526 R_Mesh_Draw(numverts, numtriangles, elements);
1528 c_rt_lighttris += numtriangles;
1530 memset(&m, 0, sizeof(m));
1531 m.tex[0] = R_GetTexture(basetexture);
1532 m.texcubemap[1] = R_GetTexture(lightcubemap);
1533 m.pointer_texcoord[0] = texcoord2f;
1534 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1535 R_Mesh_State_Texture(&m);
1536 qglColorMask(1,1,1,0);
1537 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1539 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1540 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1541 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1543 color[0] = bound(0, color2[0], 1);
1544 color[1] = bound(0, color2[1], 1);
1545 color[2] = bound(0, color2[2], 1);
1546 GL_Color(color[0], color[1], color[2], 1);
1547 R_Mesh_Draw(numverts, numtriangles, elements);
1549 c_rt_lighttris += numtriangles;
1555 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1556 GL_DepthMask(false);
1558 GL_ColorPointer(varray_color4f);
1559 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1560 memset(&m, 0, sizeof(m));
1561 m.tex[0] = R_GetTexture(basetexture);
1562 m.pointer_texcoord[0] = texcoord2f;
1563 if (r_textureunits.integer >= 2)
1566 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1567 m.pointer_texcoord[1] = varray_texcoord2f[1];
1568 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1570 R_Mesh_State_Texture(&m);
1571 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1573 color[0] = bound(0, color2[0], 1);
1574 color[1] = bound(0, color2[1], 1);
1575 color[2] = bound(0, color2[2], 1);
1576 if (r_textureunits.integer >= 2)
1577 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1579 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1580 R_Mesh_Draw(numverts, numtriangles, elements);
1582 c_rt_lighttris += numtriangles;
1587 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)
1590 float color[3], color2[3];
1592 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1595 bumptexture = r_shadow_blankbumptexture;
1597 glosstexture = r_shadow_blankglosstexture;
1598 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1600 GL_VertexPointer(vertex3f);
1602 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1604 // 2/0/0/1/2 3D combine blendsquare path
1605 memset(&m, 0, sizeof(m));
1606 m.tex[0] = R_GetTexture(bumptexture);
1607 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1608 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1609 m.pointer_texcoord[0] = texcoord2f;
1610 m.pointer_texcoord[1] = varray_texcoord3f[1];
1611 R_Mesh_State_Texture(&m);
1612 qglColorMask(0,0,0,1);
1613 // this squares the result
1614 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1615 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1616 R_Mesh_Draw(numverts, numtriangles, elements);
1618 c_rt_lighttris += numtriangles;
1620 memset(&m, 0, sizeof(m));
1621 R_Mesh_State_Texture(&m);
1622 // square alpha in framebuffer a few times to make it shiny
1623 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1624 // these comments are a test run through this math for intensity 0.5
1625 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1626 // 0.25 * 0.25 = 0.0625 (this is another pass)
1627 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1628 R_Mesh_Draw(numverts, numtriangles, elements);
1630 c_rt_lighttris += numtriangles;
1631 R_Mesh_Draw(numverts, numtriangles, elements);
1633 c_rt_lighttris += numtriangles;
1635 memset(&m, 0, sizeof(m));
1636 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1637 m.pointer_texcoord[0] = varray_texcoord3f[0];
1638 R_Mesh_State_Texture(&m);
1639 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1640 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1641 R_Mesh_Draw(numverts, numtriangles, elements);
1643 c_rt_lighttris += numtriangles;
1645 memset(&m, 0, sizeof(m));
1646 m.tex[0] = R_GetTexture(glosstexture);
1647 m.texcubemap[1] = R_GetTexture(lightcubemap);
1648 m.pointer_texcoord[0] = texcoord2f;
1649 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1650 R_Mesh_State_Texture(&m);
1651 qglColorMask(1,1,1,0);
1652 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1654 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1655 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1656 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1658 color[0] = bound(0, color2[0], 1);
1659 color[1] = bound(0, color2[1], 1);
1660 color[2] = bound(0, color2[2], 1);
1661 GL_Color(color[0], color[1], color[2], 1);
1662 R_Mesh_Draw(numverts, numtriangles, elements);
1664 c_rt_lighttris += numtriangles;
1667 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1669 // 2/0/0/2 3D combine blendsquare path
1670 memset(&m, 0, sizeof(m));
1671 m.tex[0] = R_GetTexture(bumptexture);
1672 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1673 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1674 m.pointer_texcoord[0] = texcoord2f;
1675 m.pointer_texcoord[1] = varray_texcoord3f[1];
1676 R_Mesh_State_Texture(&m);
1677 qglColorMask(0,0,0,1);
1678 // this squares the result
1679 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1680 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1681 R_Mesh_Draw(numverts, numtriangles, elements);
1683 c_rt_lighttris += numtriangles;
1685 memset(&m, 0, sizeof(m));
1686 R_Mesh_State_Texture(&m);
1687 // square alpha in framebuffer a few times to make it shiny
1688 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1689 // these comments are a test run through this math for intensity 0.5
1690 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1691 // 0.25 * 0.25 = 0.0625 (this is another pass)
1692 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1693 R_Mesh_Draw(numverts, numtriangles, elements);
1695 c_rt_lighttris += numtriangles;
1696 R_Mesh_Draw(numverts, numtriangles, elements);
1698 c_rt_lighttris += numtriangles;
1700 memset(&m, 0, sizeof(m));
1701 m.tex[0] = R_GetTexture(glosstexture);
1702 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1703 m.pointer_texcoord[0] = texcoord2f;
1704 m.pointer_texcoord[1] = varray_texcoord3f[1];
1705 R_Mesh_State_Texture(&m);
1706 qglColorMask(1,1,1,0);
1707 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1708 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1709 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1710 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1712 color[0] = bound(0, color2[0], 1);
1713 color[1] = bound(0, color2[1], 1);
1714 color[2] = bound(0, color2[2], 1);
1715 GL_Color(color[0], color[1], color[2], 1);
1716 R_Mesh_Draw(numverts, numtriangles, elements);
1718 c_rt_lighttris += numtriangles;
1721 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1723 // 2/0/0/2/2 2D combine blendsquare path
1724 memset(&m, 0, sizeof(m));
1725 m.tex[0] = R_GetTexture(bumptexture);
1726 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1727 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1728 m.pointer_texcoord[0] = texcoord2f;
1729 m.pointer_texcoord[1] = varray_texcoord3f[1];
1730 R_Mesh_State_Texture(&m);
1731 qglColorMask(0,0,0,1);
1732 // this squares the result
1733 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1734 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1735 R_Mesh_Draw(numverts, numtriangles, elements);
1737 c_rt_lighttris += numtriangles;
1739 memset(&m, 0, sizeof(m));
1740 R_Mesh_State_Texture(&m);
1741 // square alpha in framebuffer a few times to make it shiny
1742 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1743 // these comments are a test run through this math for intensity 0.5
1744 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1745 // 0.25 * 0.25 = 0.0625 (this is another pass)
1746 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1747 R_Mesh_Draw(numverts, numtriangles, elements);
1749 c_rt_lighttris += numtriangles;
1750 R_Mesh_Draw(numverts, numtriangles, elements);
1752 c_rt_lighttris += numtriangles;
1754 memset(&m, 0, sizeof(m));
1755 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1756 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1757 m.pointer_texcoord[0] = varray_texcoord2f[0];
1758 m.pointer_texcoord[1] = varray_texcoord2f[1];
1759 R_Mesh_State_Texture(&m);
1760 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1761 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1762 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1763 R_Mesh_Draw(numverts, numtriangles, elements);
1765 c_rt_lighttris += numtriangles;
1767 memset(&m, 0, sizeof(m));
1768 m.tex[0] = R_GetTexture(glosstexture);
1769 m.texcubemap[1] = R_GetTexture(lightcubemap);
1770 m.pointer_texcoord[0] = texcoord2f;
1771 m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1772 R_Mesh_State_Texture(&m);
1773 qglColorMask(1,1,1,0);
1774 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1776 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1777 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1778 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1780 color[0] = bound(0, color2[0], 1);
1781 color[1] = bound(0, color2[1], 1);
1782 color[2] = bound(0, color2[2], 1);
1783 GL_Color(color[0], color[1], color[2], 1);
1784 R_Mesh_Draw(numverts, numtriangles, elements);
1786 c_rt_lighttris += numtriangles;
1792 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1794 R_Mesh_Matrix(matrix);
1795 R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1798 cvar_t r_editlights = {0, "r_editlights", "0"};
1799 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1800 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1801 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1802 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1803 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1804 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1805 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1806 worldlight_t *r_shadow_worldlightchain;
1807 worldlight_t *r_shadow_selectedlight;
1808 vec3_t r_editlights_cursorlocation;
1810 static int castshadowcount = 1;
1811 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1813 int i, j, k, l, maxverts = 256, *mark, tris;
1814 float *vertex3f = NULL;
1816 shadowmesh_t *mesh, *castmesh;
1820 surfmesh_t *surfmesh;
1822 if (radius < 15 || DotProduct(color, color) < 0.03)
1824 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1828 e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1829 VectorCopy(origin, e->origin);
1830 VectorCopy(color, e->light);
1831 e->lightradius = radius;
1833 if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1835 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1838 e->castshadows = castshadow;
1840 e->cullradius = e->lightradius;
1841 for (k = 0;k < 3;k++)
1843 e->mins[k] = e->origin[k] - e->lightradius;
1844 e->maxs[k] = e->origin[k] + e->lightradius;
1847 e->next = r_shadow_worldlightchain;
1848 r_shadow_worldlightchain = e;
1849 if (cubemapname && cubemapname[0])
1851 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1852 strcpy(e->cubemapname, cubemapname);
1853 // FIXME: add cubemap loading (and don't load a cubemap twice)
1858 i = cl.worldmodel->PointContents(cl.worldmodel, e->origin);
1859 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1862 qbyte *bytesurfacepvs;
1864 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1865 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1867 Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1869 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1870 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1871 leaf->worldnodeframe = castshadowcount;
1873 for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1874 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1875 surf->castshadow = castshadowcount;
1877 Mem_Free(byteleafpvs);
1878 Mem_Free(bytesurfacepvs);
1882 leaf = cl.worldmodel->PointInLeaf(cl.worldmodel, origin);
1883 pvs = cl.worldmodel->LeafPVS(cl.worldmodel, leaf);
1884 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1886 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1888 leaf->worldnodeframe = castshadowcount;
1889 for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1891 surf = cl.worldmodel->surfaces + *mark;
1892 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1893 surf->castshadow = castshadowcount;
1900 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1901 if (leaf->worldnodeframe == castshadowcount)
1904 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1905 if (surf->castshadow == castshadowcount)
1909 e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1911 e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1913 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1914 if (leaf->worldnodeframe == castshadowcount)
1915 e->leafs[e->numleafs++] = leaf;
1917 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1918 if (surf->castshadow == castshadowcount)
1919 e->surfaces[e->numsurfaces++] = surf;
1921 // find bounding box of lit leafs
1922 VectorCopy(e->origin, e->mins);
1923 VectorCopy(e->origin, e->maxs);
1924 for (j = 0;j < e->numleafs;j++)
1927 for (k = 0;k < 3;k++)
1929 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1930 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1934 for (k = 0;k < 3;k++)
1936 if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1937 if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1939 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1944 for (j = 0;j < e->numsurfaces;j++)
1946 surf = e->surfaces[j];
1947 if (surf->flags & SURF_SHADOWCAST)
1949 surf->castshadow = castshadowcount;
1950 if (maxverts < surf->poly_numverts)
1951 maxverts = surf->poly_numverts;
1954 e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1955 // make a mesh to cast a shadow volume from
1956 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1957 for (j = 0;j < e->numsurfaces;j++)
1958 if (e->surfaces[j]->castshadow == castshadowcount)
1959 for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1960 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1961 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1963 // cast shadow volume from castmesh
1964 for (mesh = castmesh;mesh;mesh = mesh->next)
1966 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1968 if (maxverts < castmesh->numverts * 2)
1970 maxverts = castmesh->numverts * 2;
1975 if (vertex3f == NULL && maxverts > 0)
1976 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1978 // now that we have the buffers big enough, construct and add
1979 // the shadow volume mesh
1980 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)))
1981 Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1986 // we're done with castmesh now
1987 Mod_ShadowMesh_Free(castmesh);
1988 e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1989 for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1990 l += mesh->numtriangles;
1991 Con_Printf("static shadow volume built containing %i triangles\n", l);
1994 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);
1997 void R_Shadow_FreeWorldLight(worldlight_t *light)
1999 worldlight_t **lightpointer;
2000 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2001 if (*lightpointer != light)
2002 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2003 *lightpointer = light->next;
2004 if (light->cubemapname)
2005 Mem_Free(light->cubemapname);
2006 if (light->shadowvolume)
2007 Mod_ShadowMesh_Free(light->shadowvolume);
2008 if (light->surfaces)
2009 Mem_Free(light->surfaces);
2011 Mem_Free(light->leafs);
2015 void R_Shadow_ClearWorldLights(void)
2017 while (r_shadow_worldlightchain)
2018 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2019 r_shadow_selectedlight = NULL;
2022 void R_Shadow_SelectLight(worldlight_t *light)
2024 if (r_shadow_selectedlight)
2025 r_shadow_selectedlight->selected = false;
2026 r_shadow_selectedlight = light;
2027 if (r_shadow_selectedlight)
2028 r_shadow_selectedlight->selected = true;
2031 rtexture_t *lighttextures[5];
2033 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2035 float scale = r_editlights_cursorgrid.value * 0.5f;
2036 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2039 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2042 const worldlight_t *light;
2045 if (light->selected)
2046 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2047 if (!light->shadowvolume)
2049 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2052 void R_Shadow_DrawLightSprites(void)
2056 worldlight_t *light;
2058 for (i = 0;i < 5;i++)
2060 lighttextures[i] = NULL;
2061 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2062 lighttextures[i] = pic->tex;
2065 for (light = r_shadow_worldlightchain;light;light = light->next)
2066 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2067 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2070 void R_Shadow_SelectLightInView(void)
2072 float bestrating, rating, temp[3];
2073 worldlight_t *best, *light;
2076 for (light = r_shadow_worldlightchain;light;light = light->next)
2078 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2079 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2082 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2083 if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2085 bestrating = rating;
2090 R_Shadow_SelectLight(best);
2093 void R_Shadow_LoadWorldLights(void)
2095 int n, a, style, shadow;
2096 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2097 float origin[3], radius, color[3];
2098 if (cl.worldmodel == NULL)
2100 Con_Printf("No map loaded.\n");
2103 FS_StripExtension(cl.worldmodel->name, name);
2104 strcat(name, ".rtlights");
2105 lightsstring = FS_LoadFile(name, false);
2113 while (*s && *s != '\n')
2119 // check for modifier flags
2125 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);
2131 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);
2134 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2135 radius *= r_editlights_rtlightssizescale.value;
2136 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2141 Con_Printf("invalid rtlights file \"%s\"\n", name);
2142 Mem_Free(lightsstring);
2146 void R_Shadow_SaveWorldLights(void)
2148 worldlight_t *light;
2149 int bufchars, bufmaxchars;
2151 char name[MAX_QPATH];
2153 if (!r_shadow_worldlightchain)
2155 if (cl.worldmodel == NULL)
2157 Con_Printf("No map loaded.\n");
2160 FS_StripExtension(cl.worldmodel->name, name);
2161 strcat(name, ".rtlights");
2162 bufchars = bufmaxchars = 0;
2164 for (light = r_shadow_worldlightchain;light;light = light->next)
2166 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 : "");
2167 if (bufchars + (int) strlen(line) > bufmaxchars)
2169 bufmaxchars = bufchars + strlen(line) + 2048;
2171 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2175 memcpy(buf, oldbuf, bufchars);
2181 memcpy(buf + bufchars, line, strlen(line));
2182 bufchars += strlen(line);
2186 FS_WriteFile(name, buf, bufchars);
2191 void R_Shadow_LoadLightsFile(void)
2194 char name[MAX_QPATH], *lightsstring, *s, *t;
2195 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2196 if (cl.worldmodel == NULL)
2198 Con_Printf("No map loaded.\n");
2201 FS_StripExtension(cl.worldmodel->name, name);
2202 strcat(name, ".lights");
2203 lightsstring = FS_LoadFile(name, false);
2211 while (*s && *s != '\n')
2216 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);
2220 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);
2223 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2224 radius = bound(15, radius, 4096);
2225 VectorScale(color, (2.0f / (8388608.0f)), color);
2226 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2231 Con_Printf("invalid lights file \"%s\"\n", name);
2232 Mem_Free(lightsstring);
2236 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2238 int entnum, style, islight;
2239 char key[256], value[1024];
2240 float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2243 if (cl.worldmodel == NULL)
2245 Con_Printf("No map loaded.\n");
2248 data = cl.worldmodel->entities;
2251 for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2254 origin[0] = origin[1] = origin[2] = 0;
2255 originhack[0] = originhack[1] = originhack[2] = 0;
2256 color[0] = color[1] = color[2] = 1;
2257 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2263 if (!COM_ParseToken(&data))
2265 if (com_token[0] == '}')
2266 break; // end of entity
2267 if (com_token[0] == '_')
2268 strcpy(key, com_token + 1);
2270 strcpy(key, com_token);
2271 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2272 key[strlen(key)-1] = 0;
2273 if (!COM_ParseToken(&data))
2275 strcpy(value, com_token);
2277 // now that we have the key pair worked out...
2278 if (!strcmp("light", key))
2279 light = atof(value);
2280 else if (!strcmp("origin", key))
2281 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2282 else if (!strcmp("color", key))
2283 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2284 else if (!strcmp("wait", key))
2285 scale = atof(value);
2286 else if (!strcmp("classname", key))
2288 if (!strncmp(value, "light", 5))
2291 if (!strcmp(value, "light_fluoro"))
2296 overridecolor[0] = 1;
2297 overridecolor[1] = 1;
2298 overridecolor[2] = 1;
2300 if (!strcmp(value, "light_fluorospark"))
2305 overridecolor[0] = 1;
2306 overridecolor[1] = 1;
2307 overridecolor[2] = 1;
2309 if (!strcmp(value, "light_globe"))
2314 overridecolor[0] = 1;
2315 overridecolor[1] = 0.8;
2316 overridecolor[2] = 0.4;
2318 if (!strcmp(value, "light_flame_large_yellow"))
2323 overridecolor[0] = 1;
2324 overridecolor[1] = 0.5;
2325 overridecolor[2] = 0.1;
2327 if (!strcmp(value, "light_flame_small_yellow"))
2332 overridecolor[0] = 1;
2333 overridecolor[1] = 0.5;
2334 overridecolor[2] = 0.1;
2336 if (!strcmp(value, "light_torch_small_white"))
2341 overridecolor[0] = 1;
2342 overridecolor[1] = 0.5;
2343 overridecolor[2] = 0.1;
2345 if (!strcmp(value, "light_torch_small_walltorch"))
2350 overridecolor[0] = 1;
2351 overridecolor[1] = 0.5;
2352 overridecolor[2] = 0.1;
2356 else if (!strcmp("style", key))
2357 style = atoi(value);
2359 if (light <= 0 && islight)
2361 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2362 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2363 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2364 VectorCopy(overridecolor, color);
2365 VectorScale(color, light, color);
2366 VectorAdd(origin, originhack, origin);
2368 R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2373 void R_Shadow_SetCursorLocationForView(void)
2375 vec_t dist, push, frac;
2376 vec3_t dest, endpos, normal;
2377 VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2378 frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2381 dist = frac * r_editlights_cursordistance.value;
2382 push = r_editlights_cursorpushback.value;
2386 VectorMA(endpos, push, vpn, endpos);
2387 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2389 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2390 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2391 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2394 void R_Shadow_UpdateWorldLightSelection(void)
2396 R_Shadow_SetCursorLocationForView();
2397 if (r_editlights.integer)
2399 R_Shadow_SelectLightInView();
2400 R_Shadow_DrawLightSprites();
2403 R_Shadow_SelectLight(NULL);
2406 void R_Shadow_EditLights_Clear_f(void)
2408 R_Shadow_ClearWorldLights();
2411 void R_Shadow_EditLights_Reload_f(void)
2413 r_shadow_reloadlights = true;
2416 void R_Shadow_EditLights_Save_f(void)
2419 R_Shadow_SaveWorldLights();
2422 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2424 R_Shadow_ClearWorldLights();
2425 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2428 void R_Shadow_EditLights_ImportLightsFile_f(void)
2430 R_Shadow_ClearWorldLights();
2431 R_Shadow_LoadLightsFile();
2434 void R_Shadow_EditLights_Spawn_f(void)
2437 if (!r_editlights.integer)
2439 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2442 if (Cmd_Argc() != 1)
2444 Con_Printf("r_editlights_spawn does not take parameters\n");
2447 color[0] = color[1] = color[2] = 1;
2448 R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2451 void R_Shadow_EditLights_Edit_f(void)
2453 vec3_t origin, color;
2456 char cubemapname[1024];
2457 if (!r_editlights.integer)
2459 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2462 if (!r_shadow_selectedlight)
2464 Con_Printf("No selected light.\n");
2467 VectorCopy(r_shadow_selectedlight->origin, origin);
2468 radius = r_shadow_selectedlight->lightradius;
2469 VectorCopy(r_shadow_selectedlight->light, color);
2470 style = r_shadow_selectedlight->style;
2471 if (r_shadow_selectedlight->cubemapname)
2472 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2475 shadows = r_shadow_selectedlight->castshadows;
2476 if (!strcmp(Cmd_Argv(1), "origin"))
2478 if (Cmd_Argc() != 5)
2480 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2483 origin[0] = atof(Cmd_Argv(2));
2484 origin[1] = atof(Cmd_Argv(3));
2485 origin[2] = atof(Cmd_Argv(4));
2487 else if (!strcmp(Cmd_Argv(1), "originx"))
2489 if (Cmd_Argc() != 3)
2491 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2494 origin[0] = atof(Cmd_Argv(2));
2496 else if (!strcmp(Cmd_Argv(1), "originy"))
2498 if (Cmd_Argc() != 3)
2500 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2503 origin[1] = atof(Cmd_Argv(2));
2505 else if (!strcmp(Cmd_Argv(1), "originz"))
2507 if (Cmd_Argc() != 3)
2509 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2512 origin[2] = atof(Cmd_Argv(2));
2514 else if (!strcmp(Cmd_Argv(1), "move"))
2516 if (Cmd_Argc() != 5)
2518 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2521 origin[0] += atof(Cmd_Argv(2));
2522 origin[1] += atof(Cmd_Argv(3));
2523 origin[2] += atof(Cmd_Argv(4));
2525 else if (!strcmp(Cmd_Argv(1), "movex"))
2527 if (Cmd_Argc() != 3)
2529 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2532 origin[0] += atof(Cmd_Argv(2));
2534 else if (!strcmp(Cmd_Argv(1), "movey"))
2536 if (Cmd_Argc() != 3)
2538 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2541 origin[1] += atof(Cmd_Argv(2));
2543 else if (!strcmp(Cmd_Argv(1), "movez"))
2545 if (Cmd_Argc() != 3)
2547 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2550 origin[2] += atof(Cmd_Argv(2));
2552 else if (!strcmp(Cmd_Argv(1), "color"))
2554 if (Cmd_Argc() != 5)
2556 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2559 color[0] = atof(Cmd_Argv(2));
2560 color[1] = atof(Cmd_Argv(3));
2561 color[2] = atof(Cmd_Argv(4));
2563 else if (!strcmp(Cmd_Argv(1), "radius"))
2565 if (Cmd_Argc() != 3)
2567 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2570 radius = atof(Cmd_Argv(2));
2572 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2574 if (Cmd_Argc() != 3)
2576 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2579 style = atoi(Cmd_Argv(2));
2581 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2585 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2588 if (Cmd_Argc() == 3)
2589 strcpy(cubemapname, Cmd_Argv(2));
2593 else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2595 if (Cmd_Argc() != 3)
2597 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2600 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2604 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2605 Con_Printf("Selected light's properties:\n");
2606 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2607 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2608 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2609 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2610 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2611 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2614 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2615 r_shadow_selectedlight = NULL;
2616 R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2619 extern int con_vislines;
2620 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2624 if (r_shadow_selectedlight == NULL)
2628 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2629 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;
2630 sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2631 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;
2632 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2633 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2634 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;
2637 void R_Shadow_EditLights_ToggleShadow_f(void)
2639 if (!r_editlights.integer)
2641 Con_Printf("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2644 if (!r_shadow_selectedlight)
2646 Con_Printf("No selected light.\n");
2649 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);
2650 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2651 r_shadow_selectedlight = NULL;
2654 void R_Shadow_EditLights_Remove_f(void)
2656 if (!r_editlights.integer)
2658 Con_Printf("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
2661 if (!r_shadow_selectedlight)
2663 Con_Printf("No selected light.\n");
2666 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2667 r_shadow_selectedlight = NULL;
2670 void R_Shadow_EditLights_Init(void)
2672 Cvar_RegisterVariable(&r_editlights);
2673 Cvar_RegisterVariable(&r_editlights_cursordistance);
2674 Cvar_RegisterVariable(&r_editlights_cursorpushback);
2675 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2676 Cvar_RegisterVariable(&r_editlights_cursorgrid);
2677 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2678 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2679 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2680 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2681 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2682 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2683 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2684 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2685 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2686 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2687 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2688 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);