3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however. Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
120 extern void R_Shadow_EditLights_Init(void);
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_STENCILTWOSIDE 3
127 int r_shadowstage = SHADOWSTAGE_NONE;
128 int r_shadow_reloadlights = false;
130 mempool_t *r_shadow_mempool;
132 int maxshadowelements;
146 rtexturepool_t *r_shadow_texturepool;
147 rtexture_t *r_shadow_normalcubetexture;
148 rtexture_t *r_shadow_attenuation2dtexture;
149 rtexture_t *r_shadow_attenuation3dtexture;
150 rtexture_t *r_shadow_blankbumptexture;
151 rtexture_t *r_shadow_blankglosstexture;
152 rtexture_t *r_shadow_blankwhitetexture;
154 // used only for light filters (cubemaps)
155 rtexturepool_t *r_shadow_filters_texturepool;
157 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
158 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
159 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
160 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
161 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
162 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
163 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
164 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
165 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
166 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
167 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
168 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
169 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
170 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
171 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
172 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
173 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
174 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
175 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
176 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
177 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
178 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
179 cvar_t r_shadow_showtris = {0, "r_shadow_showtris", "0"};
180 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
181 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
182 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
184 int c_rt_lights, c_rt_clears, c_rt_scissored;
185 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
186 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
188 void R_Shadow_ClearWorldLights(void);
189 void R_Shadow_SaveWorldLights(void);
190 void R_Shadow_LoadWorldLights(void);
191 void R_Shadow_LoadLightsFile(void);
192 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
194 void r_shadow_start(void)
196 // allocate vertex processing arrays
197 r_shadow_mempool = Mem_AllocPool("R_Shadow");
198 maxshadowelements = 0;
199 shadowelements = NULL;
207 shadowmarklist = NULL;
209 r_shadow_normalcubetexture = NULL;
210 r_shadow_attenuation2dtexture = NULL;
211 r_shadow_attenuation3dtexture = NULL;
212 r_shadow_blankbumptexture = NULL;
213 r_shadow_blankglosstexture = NULL;
214 r_shadow_blankwhitetexture = NULL;
215 r_shadow_texturepool = NULL;
216 r_shadow_filters_texturepool = NULL;
217 R_Shadow_ClearWorldLights();
218 r_shadow_reloadlights = true;
221 void r_shadow_shutdown(void)
223 R_Shadow_ClearWorldLights();
224 r_shadow_reloadlights = true;
225 r_shadow_normalcubetexture = NULL;
226 r_shadow_attenuation2dtexture = NULL;
227 r_shadow_attenuation3dtexture = NULL;
228 r_shadow_blankbumptexture = NULL;
229 r_shadow_blankglosstexture = NULL;
230 r_shadow_blankwhitetexture = NULL;
231 R_FreeTexturePool(&r_shadow_texturepool);
232 R_FreeTexturePool(&r_shadow_filters_texturepool);
233 maxshadowelements = 0;
234 shadowelements = NULL;
242 shadowmarklist = NULL;
244 Mem_FreePool(&r_shadow_mempool);
247 void r_shadow_newmap(void)
249 R_Shadow_ClearWorldLights();
250 r_shadow_reloadlights = true;
253 void R_Shadow_Help_f(void)
256 "Documentation on r_shadow system:\n"
258 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
259 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
260 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
261 "r_shadow_realtime_world : use realtime world light rendering\n"
262 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
263 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
264 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
265 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
266 "r_shadow_glossintensity : brightness of textured gloss\n"
267 "r_shadow_gloss2intensity : brightness of forced gloss\n"
268 "r_shadow_debuglight : render only this light number (-1 = all)\n"
269 "r_shadow_scissor : use scissor optimization\n"
270 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
271 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
272 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
273 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
274 "r_shadow_portallight : use portal visibility for static light precomputation\n"
275 "r_shadow_projectdistance : shadow volume projection distance\n"
276 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
277 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
278 "r_shadow_worldshadows : enable world shadows\n"
279 "r_shadow_dlightshadows : enable dlight shadows\n"
281 "r_shadow_help : this help\n"
285 void R_Shadow_Init(void)
287 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
288 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
289 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
290 Cvar_RegisterVariable(&r_shadow_realtime_world);
291 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
292 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
293 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
294 Cvar_RegisterVariable(&r_shadow_gloss);
295 Cvar_RegisterVariable(&r_shadow_glossintensity);
296 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
297 Cvar_RegisterVariable(&r_shadow_debuglight);
298 Cvar_RegisterVariable(&r_shadow_scissor);
299 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
300 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
301 Cvar_RegisterVariable(&r_shadow_polygonfactor);
302 Cvar_RegisterVariable(&r_shadow_polygonoffset);
303 Cvar_RegisterVariable(&r_shadow_portallight);
304 Cvar_RegisterVariable(&r_shadow_projectdistance);
305 Cvar_RegisterVariable(&r_shadow_texture3d);
306 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
307 Cvar_RegisterVariable(&r_shadow_worldshadows);
308 Cvar_RegisterVariable(&r_shadow_dlightshadows);
309 Cvar_RegisterVariable(&r_shadow_showtris);
310 Cvar_RegisterVariable(&r_shadow_staticworldlights);
311 Cvar_RegisterVariable(&r_shadow_cull);
312 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
313 if (gamemode == GAME_TENEBRAE)
315 Cvar_SetValue("r_shadow_gloss", 2);
316 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
318 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
319 R_Shadow_EditLights_Init();
320 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
323 matrix4x4_t matrix_attenuationxyz =
326 {0.5, 0.0, 0.0, 0.5},
327 {0.0, 0.5, 0.0, 0.5},
328 {0.0, 0.0, 0.5, 0.5},
333 matrix4x4_t matrix_attenuationz =
336 {0.0, 0.0, 0.5, 0.5},
337 {0.0, 0.0, 0.0, 0.0},
338 {0.0, 0.0, 0.0, 0.0},
343 int *R_Shadow_ResizeShadowElements(int numtris)
345 // make sure shadowelements is big enough for this volume
346 if (maxshadowelements < numtris * 24)
348 maxshadowelements = numtris * 24;
350 Mem_Free(shadowelements);
351 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
353 return shadowelements;
356 void R_Shadow_PrepareShadowMark(int numtris)
358 // make sure shadowmark is big enough for this volume
359 if (maxshadowmark < numtris)
361 maxshadowmark = numtris;
363 Mem_Free(shadowmark);
365 Mem_Free(shadowmarklist);
366 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
367 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
371 // if shadowmarkcount wrapped we clear the array and adjust accordingly
372 if (shadowmarkcount == 0)
375 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
380 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
382 int i, j, tris = 0, vr[3], t, outvertices = 0;
386 if (maxvertexupdate < innumvertices)
388 maxvertexupdate = innumvertices;
390 Mem_Free(vertexupdate);
392 Mem_Free(vertexremap);
393 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
394 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
398 if (vertexupdatenum == 0)
401 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
402 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
405 for (i = 0;i < numshadowmarktris;i++)
407 t = shadowmarktris[i];
408 shadowmark[t] = shadowmarkcount;
409 e = inelement3i + t * 3;
410 // make sure the vertices are created
411 for (j = 0;j < 3;j++)
413 if (vertexupdate[e[j]] != vertexupdatenum)
415 vertexupdate[e[j]] = vertexupdatenum;
416 vertexremap[e[j]] = outvertices;
417 VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
418 f = projectdistance / VectorLength(temp);
419 VectorCopy(invertex3f + e[j] * 3, outvertex3f);
420 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
425 // output the front and back triangles
426 outelement3i[0] = vertexremap[e[0]];
427 outelement3i[1] = vertexremap[e[1]];
428 outelement3i[2] = vertexremap[e[2]];
429 outelement3i[3] = vertexremap[e[2]] + 1;
430 outelement3i[4] = vertexremap[e[1]] + 1;
431 outelement3i[5] = vertexremap[e[0]] + 1;
436 for (i = 0;i < numshadowmarktris;i++)
438 t = shadowmarktris[i];
439 e = inelement3i + t * 3;
440 n = inneighbor3i + t * 3;
441 // output the sides (facing outward from this triangle)
442 if (shadowmark[n[0]] != shadowmarkcount)
444 vr[0] = vertexremap[e[0]];
445 vr[1] = vertexremap[e[1]];
446 outelement3i[0] = vr[1];
447 outelement3i[1] = vr[0];
448 outelement3i[2] = vr[0] + 1;
449 outelement3i[3] = vr[1];
450 outelement3i[4] = vr[0] + 1;
451 outelement3i[5] = vr[1] + 1;
455 if (shadowmark[n[1]] != shadowmarkcount)
457 vr[1] = vertexremap[e[1]];
458 vr[2] = vertexremap[e[2]];
459 outelement3i[0] = vr[2];
460 outelement3i[1] = vr[1];
461 outelement3i[2] = vr[1] + 1;
462 outelement3i[3] = vr[2];
463 outelement3i[4] = vr[1] + 1;
464 outelement3i[5] = vr[2] + 1;
468 if (shadowmark[n[2]] != shadowmarkcount)
470 vr[0] = vertexremap[e[0]];
471 vr[2] = vertexremap[e[2]];
472 outelement3i[0] = vr[0];
473 outelement3i[1] = vr[2];
474 outelement3i[2] = vr[2] + 1;
475 outelement3i[3] = vr[0];
476 outelement3i[4] = vr[2] + 1;
477 outelement3i[5] = vr[0] + 1;
483 *outnumvertices = outvertices;
487 float varray_vertex3f2[65536*3];
489 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
492 if (projectdistance < 0.1)
494 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
497 if (!numverts || !nummarktris)
499 // make sure shadowelements is big enough for this volume
500 if (maxshadowelements < nummarktris * 24)
501 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
502 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
503 R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
506 void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs)
511 // check which triangles are facing the , and then output
512 // triangle elements and vertices... by clever use of elements we
513 // can construct the whole shadow from the unprojected vertices and
514 // the projected vertices
516 // identify lit faces within the bounding box
517 R_Shadow_PrepareShadowMark(numtris);
518 for (i = 0;i < numtris;i++)
520 v[0] = invertex3f + elements[i*3+0] * 3;
521 v[1] = invertex3f + elements[i*3+1] * 3;
522 v[2] = invertex3f + elements[i*3+2] * 3;
523 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2])))
524 shadowmarklist[numshadowmark++] = i;
526 R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
529 void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius)
532 mins[0] = projectorigin[0] - radius;
533 mins[1] = projectorigin[1] - radius;
534 mins[2] = projectorigin[2] - radius;
535 maxs[0] = projectorigin[0] + radius;
536 maxs[1] = projectorigin[1] + radius;
537 maxs[2] = projectorigin[2] + radius;
538 R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
541 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
544 memset(&m, 0, sizeof(m));
545 m.pointer_vertex = vertex3f;
547 if (r_shadowstage == SHADOWSTAGE_STENCIL)
549 // decrement stencil if frontface is behind depthbuffer
550 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
551 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
552 R_Mesh_Draw(numvertices, numtriangles, element3i);
554 c_rt_shadowtris += numtriangles;
555 // increment stencil if backface is behind depthbuffer
556 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
557 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
559 R_Mesh_Draw(numvertices, numtriangles, element3i);
561 c_rt_shadowtris += numtriangles;
564 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
568 memset(&m, 0, sizeof(m));
569 for (mesh = firstmesh;mesh;mesh = mesh->next)
571 m.pointer_vertex = mesh->vertex3f;
573 if (r_shadowstage == SHADOWSTAGE_STENCIL)
575 // decrement stencil if frontface is behind depthbuffer
576 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
577 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
578 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
579 c_rtcached_shadowmeshes++;
580 c_rtcached_shadowtris += mesh->numtriangles;
581 // increment stencil if backface is behind depthbuffer
582 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
583 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
585 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
586 c_rtcached_shadowmeshes++;
587 c_rtcached_shadowtris += mesh->numtriangles;
591 float r_shadow_attenpower, r_shadow_attenscale;
592 static void R_Shadow_MakeTextures(void)
594 int x, y, z, d, side;
595 float v[3], s, t, intensity;
597 R_FreeTexturePool(&r_shadow_texturepool);
598 r_shadow_texturepool = R_AllocTexturePool();
599 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
600 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
602 #define ATTEN2DSIZE 64
603 #define ATTEN3DSIZE 32
604 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
609 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
614 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
619 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
620 if (gl_texturecubemap)
622 for (side = 0;side < 6;side++)
624 for (y = 0;y < NORMSIZE;y++)
626 for (x = 0;x < NORMSIZE;x++)
628 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
629 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
663 intensity = 127.0f / sqrt(DotProduct(v, v));
664 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
665 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
666 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
667 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
671 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
674 r_shadow_normalcubetexture = NULL;
675 for (y = 0;y < ATTEN2DSIZE;y++)
677 for (x = 0;x < ATTEN2DSIZE;x++)
679 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
680 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
682 intensity = 1.0f - sqrt(DotProduct(v, v));
684 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
685 d = bound(0, intensity, 255);
686 data[(y*ATTEN2DSIZE+x)*4+0] = d;
687 data[(y*ATTEN2DSIZE+x)*4+1] = d;
688 data[(y*ATTEN2DSIZE+x)*4+2] = d;
689 data[(y*ATTEN2DSIZE+x)*4+3] = d;
692 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
693 if (r_shadow_texture3d.integer)
695 for (z = 0;z < ATTEN3DSIZE;z++)
697 for (y = 0;y < ATTEN3DSIZE;y++)
699 for (x = 0;x < ATTEN3DSIZE;x++)
701 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
702 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
703 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
704 intensity = 1.0f - sqrt(DotProduct(v, v));
706 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
707 d = bound(0, intensity, 255);
708 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
709 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
710 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
711 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
715 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
720 void R_Shadow_Stage_Begin(void)
724 if (r_shadow_texture3d.integer && !gl_texture3d)
725 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
726 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
727 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
729 if (!r_shadow_attenuation2dtexture
730 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
731 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
732 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
733 R_Shadow_MakeTextures();
735 memset(&m, 0, sizeof(m));
736 GL_BlendFunc(GL_ONE, GL_ZERO);
740 GL_ColorPointer(NULL);
741 GL_Color(0, 0, 0, 1);
742 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
743 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
744 r_shadowstage = SHADOWSTAGE_NONE;
746 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
747 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
748 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
751 void R_Shadow_LoadWorldLightsIfNeeded(void)
753 if (r_shadow_reloadlights && cl.worldmodel)
755 R_Shadow_ClearWorldLights();
756 r_shadow_reloadlights = false;
757 R_Shadow_LoadWorldLights();
758 if (r_shadow_worldlightchain == NULL)
760 R_Shadow_LoadLightsFile();
761 if (r_shadow_worldlightchain == NULL)
762 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
767 void R_Shadow_Stage_ShadowVolumes(void)
770 memset(&m, 0, sizeof(m));
772 GL_ColorPointer(NULL);
773 GL_Color(1, 1, 1, 1);
774 GL_ColorMask(0, 0, 0, 0);
775 GL_BlendFunc(GL_ONE, GL_ZERO);
778 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
779 //if (r_shadow_polygonoffset.value != 0)
781 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
782 // qglEnable(GL_POLYGON_OFFSET_FILL);
785 // qglDisable(GL_POLYGON_OFFSET_FILL);
786 qglDepthFunc(GL_LESS);
787 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
788 qglEnable(GL_STENCIL_TEST);
789 qglStencilFunc(GL_ALWAYS, 128, ~0);
790 if (gl_ext_stenciltwoside.integer)
792 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
793 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
794 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
796 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
797 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
799 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
803 r_shadowstage = SHADOWSTAGE_STENCIL;
805 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
807 GL_Clear(GL_STENCIL_BUFFER_BIT);
809 // LordHavoc note: many shadow volumes reside entirely inside the world
810 // (that is to say they are entirely bounded by their lit surfaces),
811 // which can be optimized by handling things as an inverted light volume,
812 // with the shadow boundaries of the world being simulated by an altered
813 // (129) bias to stencil clearing on such lights
814 // FIXME: generate inverted light volumes for use as shadow volumes and
815 // optimize for them as noted above
818 void R_Shadow_Stage_LightWithoutShadows(void)
821 memset(&m, 0, sizeof(m));
823 GL_BlendFunc(GL_ONE, GL_ONE);
826 qglPolygonOffset(0, 0);
827 //qglDisable(GL_POLYGON_OFFSET_FILL);
828 GL_ColorPointer(NULL);
829 GL_Color(1, 1, 1, 1);
830 GL_ColorMask(1, 1, 1, 1);
831 qglDepthFunc(GL_EQUAL);
832 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
833 qglDisable(GL_STENCIL_TEST);
834 if (gl_support_stenciltwoside)
835 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
837 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
838 qglStencilFunc(GL_EQUAL, 128, 0xFF);
839 r_shadowstage = SHADOWSTAGE_LIGHT;
843 void R_Shadow_Stage_LightWithShadows(void)
846 memset(&m, 0, sizeof(m));
848 GL_BlendFunc(GL_ONE, GL_ONE);
851 qglPolygonOffset(0, 0);
852 //qglDisable(GL_POLYGON_OFFSET_FILL);
853 GL_ColorPointer(NULL);
854 GL_Color(1, 1, 1, 1);
855 GL_ColorMask(1, 1, 1, 1);
856 qglDepthFunc(GL_EQUAL);
857 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
858 qglEnable(GL_STENCIL_TEST);
859 if (gl_support_stenciltwoside)
860 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
862 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
863 // only draw light where this geometry was already rendered AND the
864 // stencil is 128 (values other than this mean shadow)
865 qglStencilFunc(GL_EQUAL, 128, 0xFF);
866 r_shadowstage = SHADOWSTAGE_LIGHT;
870 void R_Shadow_Stage_End(void)
873 memset(&m, 0, sizeof(m));
875 GL_BlendFunc(GL_ONE, GL_ZERO);
878 qglPolygonOffset(0, 0);
879 //qglDisable(GL_POLYGON_OFFSET_FILL);
880 GL_ColorPointer(NULL);
881 GL_Color(1, 1, 1, 1);
882 GL_ColorMask(1, 1, 1, 1);
883 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
884 qglDepthFunc(GL_LEQUAL);
885 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
886 qglDisable(GL_STENCIL_TEST);
887 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
888 if (gl_support_stenciltwoside)
889 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
891 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
892 r_shadowstage = SHADOWSTAGE_NONE;
895 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
897 int i, ix1, iy1, ix2, iy2;
898 float x1, y1, x2, y2, x, y, f;
901 if (!r_shadow_scissor.integer)
903 // if view is inside the box, just say yes it's visible
904 // LordHavoc: for some odd reason scissor seems broken without stencil
905 // (?!? seems like a driver bug) so abort if gl_stencil is false
906 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
908 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
911 for (i = 0;i < 3;i++)
913 if (r_viewforward[i] >= 0)
924 f = DotProduct(r_viewforward, r_vieworigin) + 1;
925 if (DotProduct(r_viewforward, v2) <= f)
927 // entirely behind nearclip plane
930 if (DotProduct(r_viewforward, v) >= f)
932 // entirely infront of nearclip plane
933 x1 = y1 = x2 = y2 = 0;
934 for (i = 0;i < 8;i++)
936 v[0] = (i & 1) ? mins[0] : maxs[0];
937 v[1] = (i & 2) ? mins[1] : maxs[1];
938 v[2] = (i & 4) ? mins[2] : maxs[2];
940 GL_TransformToScreen(v, v2);
941 //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]);
960 // clipped by nearclip plane
961 // this is nasty and crude...
962 // create viewspace bbox
963 for (i = 0;i < 8;i++)
965 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
966 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
967 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
968 v2[0] = -DotProduct(v, r_viewleft);
969 v2[1] = DotProduct(v, r_viewup);
970 v2[2] = DotProduct(v, r_viewforward);
973 if (smins[0] > v2[0]) smins[0] = v2[0];
974 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
975 if (smins[1] > v2[1]) smins[1] = v2[1];
976 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
977 if (smins[2] > v2[2]) smins[2] = v2[2];
978 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
982 smins[0] = smaxs[0] = v2[0];
983 smins[1] = smaxs[1] = v2[1];
984 smins[2] = smaxs[2] = v2[2];
987 // now we have a bbox in viewspace
988 // clip it to the view plane
991 // return true if that culled the box
992 if (smins[2] >= smaxs[2])
994 // ok some of it is infront of the view, transform each corner back to
995 // worldspace and then to screenspace and make screen rect
996 // initialize these variables just to avoid compiler warnings
997 x1 = y1 = x2 = y2 = 0;
998 for (i = 0;i < 8;i++)
1000 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1001 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1002 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1003 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1004 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1005 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1007 GL_TransformToScreen(v, v2);
1008 //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]);
1025 // this code doesn't handle boxes with any points behind view properly
1026 x1 = 1000;x2 = -1000;
1027 y1 = 1000;y2 = -1000;
1028 for (i = 0;i < 8;i++)
1030 v[0] = (i & 1) ? mins[0] : maxs[0];
1031 v[1] = (i & 2) ? mins[1] : maxs[1];
1032 v[2] = (i & 4) ? mins[2] : maxs[2];
1034 GL_TransformToScreen(v, v2);
1035 //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]);
1053 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1054 if (ix1 < r_view_x) ix1 = r_view_x;
1055 if (iy1 < r_view_y) iy1 = r_view_y;
1056 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1057 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1058 if (ix2 <= ix1 || iy2 <= iy1)
1060 // set up the scissor rectangle
1061 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1062 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1063 //qglEnable(GL_SCISSOR_TEST);
1068 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1070 float *color4f = varray_color4f;
1071 float dist, dot, intensity, v[3], n[3];
1072 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1074 Matrix4x4_Transform(m, vertex3f, v);
1075 if ((dist = DotProduct(v, v)) < 1)
1077 Matrix4x4_Transform3x3(m, normal3f, n);
1078 if ((dot = DotProduct(n, v)) > 0)
1081 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1082 VectorScale(lightcolor, intensity, color4f);
1087 VectorClear(color4f);
1093 VectorClear(color4f);
1099 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1101 float *color4f = varray_color4f;
1102 float dist, dot, intensity, v[3], n[3];
1103 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1105 Matrix4x4_Transform(m, vertex3f, v);
1106 if ((dist = fabs(v[2])) < 1)
1108 Matrix4x4_Transform3x3(m, normal3f, n);
1109 if ((dot = DotProduct(n, v)) > 0)
1111 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1112 VectorScale(lightcolor, intensity, color4f);
1117 VectorClear(color4f);
1123 VectorClear(color4f);
1129 // FIXME: this should be done in a vertex program when possible
1130 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1131 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1135 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1136 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1137 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1144 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1148 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1149 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1156 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)
1160 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1162 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1163 // the cubemap normalizes this for us
1164 out3f[0] = DotProduct(svector3f, lightdir);
1165 out3f[1] = DotProduct(tvector3f, lightdir);
1166 out3f[2] = DotProduct(normal3f, lightdir);
1170 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)
1173 float lightdir[3], eyedir[3], halfdir[3];
1174 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1176 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1177 VectorNormalizeFast(lightdir);
1178 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1179 VectorNormalizeFast(eyedir);
1180 VectorAdd(lightdir, eyedir, halfdir);
1181 // the cubemap normalizes this for us
1182 out3f[0] = DotProduct(svector3f, halfdir);
1183 out3f[1] = DotProduct(tvector3f, halfdir);
1184 out3f[2] = DotProduct(normal3f, halfdir);
1188 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, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1191 float color[3], color2[3];
1193 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1196 bumptexture = r_shadow_blankbumptexture;
1197 GL_ColorPointer(NULL);
1199 // colorscale accounts for how much we multiply the brightness during combine
1200 // mult is how many times the final pass of the lighting will be
1201 // performed to get more brightness than otherwise possible
1202 // limit mult to 64 for sanity sake
1203 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1205 // 3/2 3D combine path (Geforce3, Radeon 8500)
1206 memset(&m, 0, sizeof(m));
1207 m.pointer_vertex = vertex3f;
1208 m.tex[0] = R_GetTexture(bumptexture);
1209 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1210 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1211 m.texcombinergb[0] = GL_REPLACE;
1212 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1213 m.pointer_texcoord[0] = texcoord2f;
1214 m.pointer_texcoord[1] = varray_texcoord3f[1];
1215 m.pointer_texcoord[2] = varray_texcoord3f[2];
1217 GL_ColorMask(0,0,0,1);
1218 GL_BlendFunc(GL_ONE, GL_ZERO);
1219 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1220 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1221 R_Mesh_Draw(numverts, numtriangles, elements);
1223 c_rt_lighttris += numtriangles;
1225 memset(&m, 0, sizeof(m));
1226 m.pointer_vertex = vertex3f;
1227 m.tex[0] = R_GetTexture(basetexture);
1228 m.pointer_texcoord[0] = texcoord2f;
1231 m.texcubemap[1] = R_GetTexture(lightcubemap);
1232 m.pointer_texcoord[1] = varray_texcoord3f[1];
1233 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1236 GL_ColorMask(1,1,1,0);
1237 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1238 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1239 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1241 color[0] = bound(0, color2[0], 1);
1242 color[1] = bound(0, color2[1], 1);
1243 color[2] = bound(0, color2[2], 1);
1244 GL_Color(color[0], color[1], color[2], 1);
1245 R_Mesh_Draw(numverts, numtriangles, elements);
1247 c_rt_lighttris += numtriangles;
1250 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1252 // 1/2/2 3D combine path (original Radeon)
1253 memset(&m, 0, sizeof(m));
1254 m.pointer_vertex = vertex3f;
1255 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1256 m.pointer_texcoord[0] = varray_texcoord3f[0];
1258 GL_ColorMask(0,0,0,1);
1259 GL_BlendFunc(GL_ONE, GL_ZERO);
1260 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1261 R_Mesh_Draw(numverts, numtriangles, elements);
1263 c_rt_lighttris += numtriangles;
1265 memset(&m, 0, sizeof(m));
1266 m.pointer_vertex = vertex3f;
1267 m.tex[0] = R_GetTexture(bumptexture);
1268 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1269 m.texcombinergb[0] = GL_REPLACE;
1270 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1271 m.pointer_texcoord[0] = texcoord2f;
1272 m.pointer_texcoord[1] = varray_texcoord3f[1];
1274 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1275 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1276 R_Mesh_Draw(numverts, numtriangles, elements);
1278 c_rt_lighttris += numtriangles;
1280 memset(&m, 0, sizeof(m));
1281 m.pointer_vertex = vertex3f;
1282 m.tex[0] = R_GetTexture(basetexture);
1283 m.pointer_texcoord[0] = texcoord2f;
1286 m.texcubemap[1] = R_GetTexture(lightcubemap);
1287 m.pointer_texcoord[1] = varray_texcoord3f[1];
1288 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1291 GL_ColorMask(1,1,1,0);
1292 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1293 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1294 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1296 color[0] = bound(0, color2[0], 1);
1297 color[1] = bound(0, color2[1], 1);
1298 color[2] = bound(0, color2[2], 1);
1299 GL_Color(color[0], color[1], color[2], 1);
1300 R_Mesh_Draw(numverts, numtriangles, elements);
1302 c_rt_lighttris += numtriangles;
1305 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1307 // 2/2 3D combine path (original Radeon)
1308 memset(&m, 0, sizeof(m));
1309 m.pointer_vertex = vertex3f;
1310 m.tex[0] = R_GetTexture(bumptexture);
1311 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1312 m.texcombinergb[0] = GL_REPLACE;
1313 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1314 m.pointer_texcoord[0] = texcoord2f;
1315 m.pointer_texcoord[1] = varray_texcoord3f[1];
1317 GL_ColorMask(0,0,0,1);
1318 GL_BlendFunc(GL_ONE, GL_ZERO);
1319 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1320 R_Mesh_Draw(numverts, numtriangles, elements);
1322 c_rt_lighttris += numtriangles;
1324 memset(&m, 0, sizeof(m));
1325 m.pointer_vertex = vertex3f;
1326 m.tex[0] = R_GetTexture(basetexture);
1327 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1328 m.pointer_texcoord[0] = texcoord2f;
1329 m.pointer_texcoord[1] = varray_texcoord3f[1];
1331 GL_ColorMask(1,1,1,0);
1332 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1333 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1334 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1335 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1337 color[0] = bound(0, color2[0], 1);
1338 color[1] = bound(0, color2[1], 1);
1339 color[2] = bound(0, color2[2], 1);
1340 GL_Color(color[0], color[1], color[2], 1);
1341 R_Mesh_Draw(numverts, numtriangles, elements);
1343 c_rt_lighttris += numtriangles;
1346 else if (r_textureunits.integer >= 4)
1348 // 4/2 2D combine path (Geforce3, Radeon 8500)
1349 memset(&m, 0, sizeof(m));
1350 m.pointer_vertex = vertex3f;
1351 m.tex[0] = R_GetTexture(bumptexture);
1352 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1353 m.texcombinergb[0] = GL_REPLACE;
1354 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1355 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1356 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1357 m.pointer_texcoord[0] = texcoord2f;
1358 m.pointer_texcoord[1] = varray_texcoord3f[1];
1359 m.pointer_texcoord[2] = varray_texcoord2f[2];
1360 m.pointer_texcoord[3] = varray_texcoord2f[3];
1362 GL_ColorMask(0,0,0,1);
1363 GL_BlendFunc(GL_ONE, GL_ZERO);
1364 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1365 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1366 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1367 R_Mesh_Draw(numverts, numtriangles, elements);
1369 c_rt_lighttris += numtriangles;
1371 memset(&m, 0, sizeof(m));
1372 m.pointer_vertex = vertex3f;
1373 m.tex[0] = R_GetTexture(basetexture);
1374 m.pointer_texcoord[0] = texcoord2f;
1377 m.texcubemap[1] = R_GetTexture(lightcubemap);
1378 m.pointer_texcoord[1] = varray_texcoord3f[1];
1379 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1382 GL_ColorMask(1,1,1,0);
1383 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1384 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1385 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1387 color[0] = bound(0, color2[0], 1);
1388 color[1] = bound(0, color2[1], 1);
1389 color[2] = bound(0, color2[2], 1);
1390 GL_Color(color[0], color[1], color[2], 1);
1391 R_Mesh_Draw(numverts, numtriangles, elements);
1393 c_rt_lighttris += numtriangles;
1398 // 2/2/2 2D combine path (any dot3 card)
1399 memset(&m, 0, sizeof(m));
1400 m.pointer_vertex = vertex3f;
1401 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1402 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1403 m.pointer_texcoord[0] = varray_texcoord2f[0];
1404 m.pointer_texcoord[1] = varray_texcoord2f[1];
1406 GL_ColorMask(0,0,0,1);
1407 GL_BlendFunc(GL_ONE, GL_ZERO);
1408 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1409 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1410 R_Mesh_Draw(numverts, numtriangles, elements);
1412 c_rt_lighttris += numtriangles;
1414 memset(&m, 0, sizeof(m));
1415 m.pointer_vertex = vertex3f;
1416 m.tex[0] = R_GetTexture(bumptexture);
1417 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1418 m.texcombinergb[0] = GL_REPLACE;
1419 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1420 m.pointer_texcoord[0] = texcoord2f;
1421 m.pointer_texcoord[1] = varray_texcoord3f[1];
1423 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1424 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1425 R_Mesh_Draw(numverts, numtriangles, elements);
1427 c_rt_lighttris += numtriangles;
1429 memset(&m, 0, sizeof(m));
1430 m.pointer_vertex = vertex3f;
1431 m.tex[0] = R_GetTexture(basetexture);
1432 m.pointer_texcoord[0] = texcoord2f;
1435 m.texcubemap[1] = R_GetTexture(lightcubemap);
1436 m.pointer_texcoord[1] = varray_texcoord3f[1];
1437 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1440 GL_ColorMask(1,1,1,0);
1441 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1442 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1443 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1445 color[0] = bound(0, color2[0], 1);
1446 color[1] = bound(0, color2[1], 1);
1447 color[2] = bound(0, color2[2], 1);
1448 GL_Color(color[0], color[1], color[2], 1);
1449 R_Mesh_Draw(numverts, numtriangles, elements);
1451 c_rt_lighttris += numtriangles;
1457 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1458 GL_DepthMask(false);
1460 GL_ColorPointer(varray_color4f);
1461 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1462 memset(&m, 0, sizeof(m));
1463 m.pointer_vertex = vertex3f;
1464 m.tex[0] = R_GetTexture(basetexture);
1465 m.pointer_texcoord[0] = texcoord2f;
1466 if (r_textureunits.integer >= 2)
1469 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1470 m.pointer_texcoord[1] = varray_texcoord2f[1];
1471 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1474 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1476 color[0] = bound(0, color2[0], 1);
1477 color[1] = bound(0, color2[1], 1);
1478 color[2] = bound(0, color2[2], 1);
1479 if (r_textureunits.integer >= 2)
1480 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1482 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1483 R_Mesh_Draw(numverts, numtriangles, elements);
1485 c_rt_lighttris += numtriangles;
1490 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, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1493 float color[3], color2[3], colorscale;
1495 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1498 glosstexture = r_shadow_blankglosstexture;
1499 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1501 colorscale = r_shadow_glossintensity.value;
1503 bumptexture = r_shadow_blankbumptexture;
1504 if (glosstexture == r_shadow_blankglosstexture)
1505 colorscale *= r_shadow_gloss2intensity.value;
1506 GL_ColorPointer(NULL);
1508 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1510 // 2/0/0/1/2 3D combine blendsquare path
1511 memset(&m, 0, sizeof(m));
1512 m.pointer_vertex = vertex3f;
1513 m.tex[0] = R_GetTexture(bumptexture);
1514 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1515 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1516 m.pointer_texcoord[0] = texcoord2f;
1517 m.pointer_texcoord[1] = varray_texcoord3f[1];
1519 GL_ColorMask(0,0,0,1);
1520 // this squares the result
1521 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1522 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1523 R_Mesh_Draw(numverts, numtriangles, elements);
1525 c_rt_lighttris += numtriangles;
1527 memset(&m, 0, sizeof(m));
1528 m.pointer_vertex = vertex3f;
1530 // square alpha in framebuffer a few times to make it shiny
1531 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1532 // these comments are a test run through this math for intensity 0.5
1533 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1534 // 0.25 * 0.25 = 0.0625 (this is another pass)
1535 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1536 R_Mesh_Draw(numverts, numtriangles, elements);
1538 c_rt_lighttris += numtriangles;
1539 R_Mesh_Draw(numverts, numtriangles, elements);
1541 c_rt_lighttris += numtriangles;
1543 memset(&m, 0, sizeof(m));
1544 m.pointer_vertex = vertex3f;
1545 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1546 m.pointer_texcoord[0] = varray_texcoord3f[0];
1548 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1549 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1550 R_Mesh_Draw(numverts, numtriangles, elements);
1552 c_rt_lighttris += numtriangles;
1554 memset(&m, 0, sizeof(m));
1555 m.pointer_vertex = vertex3f;
1556 m.tex[0] = R_GetTexture(glosstexture);
1559 m.texcubemap[1] = R_GetTexture(lightcubemap);
1560 m.pointer_texcoord[1] = varray_texcoord3f[1];
1561 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1563 m.pointer_texcoord[0] = texcoord2f;
1565 GL_ColorMask(1,1,1,0);
1566 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1567 VectorScale(lightcolor, colorscale, color2);
1568 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1570 color[0] = bound(0, color2[0], 1);
1571 color[1] = bound(0, color2[1], 1);
1572 color[2] = bound(0, color2[2], 1);
1573 GL_Color(color[0], color[1], color[2], 1);
1574 R_Mesh_Draw(numverts, numtriangles, elements);
1576 c_rt_lighttris += numtriangles;
1579 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1581 // 2/0/0/2 3D combine blendsquare path
1582 memset(&m, 0, sizeof(m));
1583 m.pointer_vertex = vertex3f;
1584 m.tex[0] = R_GetTexture(bumptexture);
1585 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1586 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1587 m.pointer_texcoord[0] = texcoord2f;
1588 m.pointer_texcoord[1] = varray_texcoord3f[1];
1590 GL_ColorMask(0,0,0,1);
1591 // this squares the result
1592 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1593 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1594 R_Mesh_Draw(numverts, numtriangles, elements);
1596 c_rt_lighttris += numtriangles;
1598 memset(&m, 0, sizeof(m));
1599 m.pointer_vertex = vertex3f;
1601 // square alpha in framebuffer a few times to make it shiny
1602 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1603 // these comments are a test run through this math for intensity 0.5
1604 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1605 // 0.25 * 0.25 = 0.0625 (this is another pass)
1606 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1607 R_Mesh_Draw(numverts, numtriangles, elements);
1609 c_rt_lighttris += numtriangles;
1610 R_Mesh_Draw(numverts, numtriangles, elements);
1612 c_rt_lighttris += numtriangles;
1614 memset(&m, 0, sizeof(m));
1615 m.pointer_vertex = vertex3f;
1616 m.tex[0] = R_GetTexture(glosstexture);
1617 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1618 m.pointer_texcoord[0] = texcoord2f;
1619 m.pointer_texcoord[1] = varray_texcoord3f[1];
1621 GL_ColorMask(1,1,1,0);
1622 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1623 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1624 VectorScale(lightcolor, colorscale, color2);
1625 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1627 color[0] = bound(0, color2[0], 1);
1628 color[1] = bound(0, color2[1], 1);
1629 color[2] = bound(0, color2[2], 1);
1630 GL_Color(color[0], color[1], color[2], 1);
1631 R_Mesh_Draw(numverts, numtriangles, elements);
1633 c_rt_lighttris += numtriangles;
1636 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1638 // 2/0/0/2/2 2D combine blendsquare path
1639 memset(&m, 0, sizeof(m));
1640 m.pointer_vertex = vertex3f;
1641 m.tex[0] = R_GetTexture(bumptexture);
1642 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1643 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1644 m.pointer_texcoord[0] = texcoord2f;
1645 m.pointer_texcoord[1] = varray_texcoord3f[1];
1647 GL_ColorMask(0,0,0,1);
1648 // this squares the result
1649 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1650 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1651 R_Mesh_Draw(numverts, numtriangles, elements);
1653 c_rt_lighttris += numtriangles;
1655 memset(&m, 0, sizeof(m));
1656 m.pointer_vertex = vertex3f;
1658 // square alpha in framebuffer a few times to make it shiny
1659 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1660 // these comments are a test run through this math for intensity 0.5
1661 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1662 // 0.25 * 0.25 = 0.0625 (this is another pass)
1663 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1664 R_Mesh_Draw(numverts, numtriangles, elements);
1666 c_rt_lighttris += numtriangles;
1667 R_Mesh_Draw(numverts, numtriangles, elements);
1669 c_rt_lighttris += numtriangles;
1671 memset(&m, 0, sizeof(m));
1672 m.pointer_vertex = vertex3f;
1673 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1674 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1675 m.pointer_texcoord[0] = varray_texcoord2f[0];
1676 m.pointer_texcoord[1] = varray_texcoord2f[1];
1678 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1679 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1680 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1681 R_Mesh_Draw(numverts, numtriangles, elements);
1683 c_rt_lighttris += numtriangles;
1685 memset(&m, 0, sizeof(m));
1686 m.pointer_vertex = vertex3f;
1687 m.tex[0] = R_GetTexture(glosstexture);
1690 m.texcubemap[1] = R_GetTexture(lightcubemap);
1691 m.pointer_texcoord[1] = varray_texcoord3f[1];
1692 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1694 m.pointer_texcoord[0] = texcoord2f;
1696 GL_ColorMask(1,1,1,0);
1697 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1698 VectorScale(lightcolor, colorscale, color2);
1699 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1701 color[0] = bound(0, color2[0], 1);
1702 color[1] = bound(0, color2[1], 1);
1703 color[2] = bound(0, color2[2], 1);
1704 GL_Color(color[0], color[1], color[2], 1);
1705 R_Mesh_Draw(numverts, numtriangles, elements);
1707 c_rt_lighttris += numtriangles;
1713 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1717 R_RTLight_Uncompile(rtlight);
1718 memset(rtlight, 0, sizeof(*rtlight));
1720 VectorCopy(light->origin, rtlight->shadoworigin);
1721 VectorCopy(light->color, rtlight->color);
1722 rtlight->radius = light->radius;
1723 rtlight->cullradius = rtlight->radius;
1724 rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
1725 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
1726 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
1727 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
1728 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
1729 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
1730 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
1731 rtlight->cubemapname[0] = 0;
1732 if (light->cubemapname[0])
1733 strcpy(rtlight->cubemapname, light->cubemapname);
1734 else if (light->cubemapnum > 0)
1735 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1736 rtlight->shadow = light->shadow;
1737 rtlight->corona = light->corona;
1738 rtlight->style = light->style;
1739 rtlight->isstatic = isstatic;
1740 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1741 // ConcatScale won't work here because this needs to scale rotate and
1742 // translate, not just rotate
1743 scale = 1.0f / rtlight->radius;
1744 for (k = 0;k < 3;k++)
1745 for (j = 0;j < 4;j++)
1746 rtlight->matrix_worldtolight.m[k][j] *= scale;
1747 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1748 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1750 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1751 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1752 VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1753 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1756 // compiles rtlight geometry
1757 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1758 void R_RTLight_Compile(rtlight_t *rtlight)
1760 int i, j, k, l, maxverts = 256, tris;
1761 float *vertex3f = NULL, mins[3], maxs[3];
1762 shadowmesh_t *mesh, *castmesh = NULL;
1764 qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1765 qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1767 // compile the light
1768 rtlight->compiled = true;
1769 VectorCopy(rtlight->cullmins, mins);
1770 VectorCopy(rtlight->cullmaxs, maxs);
1771 if (rtlight->shadow)
1772 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1773 rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1776 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
1777 memset(lightpvs, 0, lightpvsbytes);
1778 if (cl.worldmodel->brushq3.num_leafs)
1783 // make a pvs that only includes things within the box
1784 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1785 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1786 SETPVSBIT(lightpvs, leaf->clusterindex);
1788 // make a cluster list for fast visibility checking during rendering
1789 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1790 if (CHECKPVSBIT(lightpvs, i))
1791 rtlight->static_numclusters++;
1792 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1793 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1794 if (CHECKPVSBIT(lightpvs, i))
1795 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1797 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1798 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1799 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1800 face->lighttemp_castshadow = false;
1801 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1803 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1805 for (k = 0;k < 3;k++)
1807 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1808 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1810 for (j = 0;j < leaf->numleaffaces;j++)
1812 face = leaf->firstleafface[j];
1813 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1814 face->lighttemp_castshadow = true;
1819 // add surfaces to shadow casting mesh and light mesh
1820 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1822 if (face->lighttemp_castshadow)
1824 face->lighttemp_castshadow = false;
1825 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1827 if (rtlight->shadow)
1828 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1829 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1830 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1831 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, face->texture->skin.base, face->texture->skin.gloss, face->texture->skin.nmap, face->data_vertex3f, face->data_svector3f, face->data_tvector3f, face->data_normal3f, face->data_texcoordtexture2f, face->num_triangles, face->data_element3i);
1836 else if (cl.worldmodel->brushq1.num_leafs)
1840 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1841 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1842 i = CL_PointQ1Contents(rtlight->shadoworigin);
1844 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1845 surf->lighttemp_castshadow = false;
1847 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1850 qbyte *bytesurfacepvs;
1852 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
1853 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1855 Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
1857 // make a pvs that only includes things within the box
1858 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1860 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1862 SETPVSBIT(lightpvs, leaf->clusterindex);
1863 for (k = 0;k < 3;k++)
1865 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1866 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1871 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1872 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1873 surf->lighttemp_castshadow = true;
1875 Mem_Free(byteleafpvs);
1876 Mem_Free(bytesurfacepvs);
1878 // make a cluster list for fast visibility checking during rendering
1879 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1880 if (CHECKPVSBIT(lightpvs, i))
1881 rtlight->static_numclusters++;
1882 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1883 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1884 if (CHECKPVSBIT(lightpvs, i))
1885 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1889 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1891 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1893 // make a pvs that only includes things within the box
1894 SETPVSBIT(lightpvs, leaf->clusterindex);
1895 for (k = 0;k < 3;k++)
1897 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1898 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1900 for (j = 0;j < leaf->nummarksurfaces;j++)
1902 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1903 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1904 surf->lighttemp_castshadow = true;
1909 // make a pvs that only includes things within the box
1910 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1911 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1912 SETPVSBIT(lightpvs, leaf->clusterindex);
1914 // make a cluster list for fast visibility checking during rendering
1915 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1916 if (CHECKPVSBIT(lightpvs, i))
1917 rtlight->static_numclusters++;
1918 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1919 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1920 if (CHECKPVSBIT(lightpvs, i))
1921 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1924 // add surfaces to shadow casting mesh and light mesh
1925 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1927 if (surf->lighttemp_castshadow)
1929 surf->lighttemp_castshadow = false;
1930 if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
1931 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, surf->mesh.data_vertex3f, NULL, NULL, NULL, NULL, surf->mesh.num_triangles, surf->mesh.data_element3i);
1932 if (!(surf->flags & SURF_DRAWSKY))
1933 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_light, surf->texinfo->texture->skin.base, surf->texinfo->texture->skin.gloss, surf->texinfo->texture->skin.nmap, surf->mesh.data_vertex3f, surf->mesh.data_svector3f, surf->mesh.data_tvector3f, surf->mesh.data_normal3f, surf->mesh.data_texcoordtexture2f, surf->mesh.num_triangles, surf->mesh.data_element3i);
1939 // limit box to light bounds (in case it grew larger)
1940 for (k = 0;k < 3;k++)
1942 if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
1943 if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
1945 rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1946 rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1948 // cast shadow volume from castmesh
1949 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1953 for (mesh = castmesh;mesh;mesh = mesh->next)
1955 R_Shadow_ResizeShadowElements(mesh->numtriangles);
1956 maxverts = max(maxverts, mesh->numverts * 2);
1961 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1962 // now that we have the buffers big enough, construct and add
1963 // the shadow volume mesh
1964 if (rtlight->shadow)
1965 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1966 for (mesh = castmesh;mesh;mesh = mesh->next)
1968 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1969 R_Shadow_PrepareShadowMark(mesh->numtriangles);
1970 for (i = 0;i < mesh->numtriangles;i++)
1973 v[0] = mesh->vertex3f + mesh->element3i[i*3+0] * 3;
1974 v[1] = mesh->vertex3f + mesh->element3i[i*3+1] * 3;
1975 v[2] = mesh->vertex3f + mesh->element3i[i*3+2] * 3;
1976 if (PointInfrontOfTriangle(rtlight->shadoworigin, v[0], v[1], v[2]) && rtlight->cullmaxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && rtlight->cullmins[0] < max(v[0][0], max(v[1][0], v[2][0])) && rtlight->cullmaxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && rtlight->cullmins[1] < max(v[0][1], max(v[1][1], v[2][1])) && rtlight->cullmaxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && rtlight->cullmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
1977 shadowmarklist[numshadowmark++] = i;
1979 if (maxshadowelements < numshadowmark * 24)
1980 R_Shadow_ResizeShadowElements((numshadowmark + 256) * 24);
1981 if ((tris = R_Shadow_ConstructShadowVolume(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->neighbor3i, mesh->vertex3f, NULL, shadowelements, vertex3f, rtlight->shadoworigin, r_shadow_projectdistance.value, numshadowmark, shadowmarklist)))
1982 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
1987 // we're done with castmesh now
1988 Mod_ShadowMesh_Free(castmesh);
1991 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1992 rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1995 if (rtlight->static_meshchain_shadow)
1996 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1997 k += mesh->numtriangles;
1999 if (rtlight->static_meshchain_light)
2000 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2001 l += mesh->numtriangles;
2002 Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles, %i light triangles\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], k, l);
2005 void R_RTLight_Uncompile(rtlight_t *rtlight)
2007 if (rtlight->compiled)
2009 if (rtlight->static_meshchain_shadow)
2010 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2011 rtlight->static_meshchain_shadow = NULL;
2012 if (rtlight->static_meshchain_light)
2013 Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2014 rtlight->static_meshchain_light = NULL;
2015 if (rtlight->static_clusterindices)
2016 Mem_Free(rtlight->static_clusterindices);
2017 rtlight->static_clusterindices = NULL;
2018 rtlight->static_numclusters = 0;
2019 rtlight->compiled = false;
2023 int shadowframecount = 0;
2025 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
2028 if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
2030 vec3_t relativeshadoworigin;
2031 Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
2032 ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
2036 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2038 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2041 entity_render_t *ent;
2043 vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2044 rtexture_t *cubemaptexture;
2045 matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2047 if (d_lightstylevalue[rtlight->style] <= 0)
2049 if (rtlight->compiled)
2051 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2053 for (i = 0;i < rtlight->static_numclusters;i++)
2054 if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
2056 if (i == rtlight->static_numclusters)
2059 else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2061 if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2064 if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2065 R_RTLight_Compile(rtlight);
2067 f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2068 VectorScale(rtlight->color, f, lightcolor);
2070 if (rtlight->selected)
2072 f = 2 + sin(realtime * M_PI * 4.0);
2073 VectorScale(lightcolor, f, lightcolor);
2077 if (rtlight->cubemapname[0])
2078 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2080 cubemaptexture = NULL;
2082 shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2083 if (shadow && (gl_stencil || visiblevolumes))
2085 if (!visiblevolumes)
2086 R_Shadow_Stage_ShadowVolumes();
2087 ent = &cl_entities[0].render;
2088 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2090 R_Mesh_Matrix(&ent->matrix);
2091 if (r_shadow_showtris.integer)
2095 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2096 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2097 qglDisable(GL_DEPTH_TEST);
2098 qglDisable(GL_STENCIL_TEST);
2099 //qglDisable(GL_CULL_FACE);
2100 GL_ColorMask(1,1,1,1);
2101 GL_ColorPointer(NULL);
2102 GL_Color(0,0.1,0,1);
2103 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2104 memset(&m, 0, sizeof(m));
2105 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2107 m.pointer_vertex = mesh->vertex3f;
2109 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2111 //qglEnable(GL_CULL_FACE);
2113 qglEnable(GL_DEPTH_TEST);
2116 qglEnable(GL_STENCIL_TEST);
2117 GL_ColorMask(0,0,0,0);
2120 R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
2123 R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2124 if (r_drawentities.integer)
2125 for (i = 0;i < r_refdef.numentities;i++)
2126 if (r_refdef.entities[i]->flags & RENDER_SHADOW)
2127 R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2130 if (!visiblevolumes)
2132 if (shadow && gl_stencil)
2133 R_Shadow_Stage_LightWithShadows();
2135 R_Shadow_Stage_LightWithoutShadows();
2137 ent = &cl_entities[0].render;
2138 if (ent->model && ent->model->DrawLight)
2140 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2141 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2142 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2143 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2144 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2145 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2147 //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2149 R_Mesh_Matrix(&ent->matrix);
2150 if (r_shadow_showtris.integer)
2153 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2154 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2155 qglDisable(GL_DEPTH_TEST);
2156 qglDisable(GL_STENCIL_TEST);
2157 //qglDisable(GL_CULL_FACE);
2158 memset(&m, 0, sizeof(m));
2159 GL_ColorPointer(NULL);
2160 GL_Color(0.2,0,0,1);
2161 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2162 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2164 m.pointer_vertex = mesh->vertex3f;
2166 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2168 //qglEnable(GL_CULL_FACE);
2170 qglEnable(GL_DEPTH_TEST);
2172 qglEnable(GL_STENCIL_TEST);
2174 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2176 R_Shadow_DiffuseLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, cubemaptexture);
2177 R_Shadow_SpecularLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_specular, mesh->map_normal, cubemaptexture);
2181 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2183 if (r_drawentities.integer)
2185 for (i = 0;i < r_refdef.numentities;i++)
2187 ent = r_refdef.entities[i];
2188 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
2189 && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2190 && (ent->flags & RENDER_LIGHT))
2192 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2193 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2194 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2195 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2196 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2197 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2204 void R_ShadowVolumeLighting(int visiblevolumes)
2212 memset(&m, 0, sizeof(m));
2215 GL_BlendFunc(GL_ONE, GL_ONE);
2216 GL_DepthMask(false);
2217 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2218 qglDisable(GL_CULL_FACE);
2219 GL_ColorPointer(NULL);
2220 GL_Color(0.0, 0.0125, 0.1, 1);
2223 R_Shadow_Stage_Begin();
2225 if (r_shadow_realtime_world.integer)
2227 R_Shadow_LoadWorldLightsIfNeeded();
2228 if (r_shadow_debuglight.integer >= 0)
2230 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2231 if (lnum == r_shadow_debuglight.integer)
2232 R_DrawRTLight(&light->rtlight, visiblevolumes);
2235 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2236 R_DrawRTLight(&light->rtlight, visiblevolumes);
2238 if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2239 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2240 R_DrawRTLight(&light->rtlight, visiblevolumes);
2244 qglEnable(GL_CULL_FACE);
2245 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2248 R_Shadow_Stage_End();
2251 cvar_t r_editlights = {0, "r_editlights", "0"};
2252 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2253 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2254 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2255 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2256 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2257 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2258 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2259 dlight_t *r_shadow_worldlightchain;
2260 dlight_t *r_shadow_selectedlight;
2261 vec3_t r_editlights_cursorlocation;
2263 typedef struct cubemapinfo_s
2266 rtexture_t *texture;
2270 #define MAX_CUBEMAPS 128
2271 static int numcubemaps;
2272 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2274 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2275 typedef struct suffixinfo_s
2278 int flipx, flipy, flipdiagonal;
2281 static suffixinfo_t suffix[3][6] =
2284 {"posx", false, false, false},
2285 {"negx", false, false, false},
2286 {"posy", false, false, false},
2287 {"negy", false, false, false},
2288 {"posz", false, false, false},
2289 {"negz", false, false, false}
2292 {"px", false, false, false},
2293 {"nx", false, false, false},
2294 {"py", false, false, false},
2295 {"ny", false, false, false},
2296 {"pz", false, false, false},
2297 {"nz", false, false, false}
2300 {"ft", true, false, true},
2301 {"bk", false, true, true},
2302 {"lf", true, true, false},
2303 {"rt", false, false, false},
2304 {"up", false, false, false},
2305 {"dn", false, false, false}
2309 static int componentorder[4] = {0, 1, 2, 3};
2311 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2313 int i, j, cubemapsize;
2314 qbyte *cubemappixels, *image_rgba;
2315 rtexture_t *cubemaptexture;
2317 // must start 0 so the first loadimagepixels has no requested width/height
2319 cubemappixels = NULL;
2320 cubemaptexture = NULL;
2321 for (j = 0;j < 3 && !cubemappixels;j++)
2323 for (i = 0;i < 6;i++)
2325 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2326 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2328 if (image_width == image_height)
2330 if (!cubemappixels && image_width >= 1)
2332 cubemapsize = image_width;
2333 // note this clears to black, so unavailable sizes are black
2334 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2337 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
2340 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2341 Mem_Free(image_rgba);
2347 if (!r_shadow_filters_texturepool)
2348 r_shadow_filters_texturepool = R_AllocTexturePool();
2349 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2350 Mem_Free(cubemappixels);
2354 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2355 for (j = 0;j < 3;j++)
2356 for (i = 0;i < 6;i++)
2357 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2358 Con_Print(" and was unable to find any of them.\n");
2360 return cubemaptexture;
2363 rtexture_t *R_Shadow_Cubemap(const char *basename)
2366 for (i = 0;i < numcubemaps;i++)
2367 if (!strcasecmp(cubemaps[i].basename, basename))
2368 return cubemaps[i].texture;
2369 if (i >= MAX_CUBEMAPS)
2372 strcpy(cubemaps[i].basename, basename);
2373 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2374 return cubemaps[i].texture;
2377 void R_Shadow_FreeCubemaps(void)
2380 R_FreeTexturePool(&r_shadow_filters_texturepool);
2383 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
2387 if (radius < 15 || DotProduct(color, color) < 0.03)
2389 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2393 light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2394 VectorCopy(origin, light->origin);
2395 VectorCopy(angles, light->angles);
2396 VectorCopy(color, light->color);
2397 light->radius = radius;
2398 light->style = style;
2399 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2401 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2404 light->shadow = shadowenable;
2405 light->corona = corona;
2406 if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2407 strcpy(light->cubemapname, cubemapname);
2408 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2409 light->next = r_shadow_worldlightchain;
2410 r_shadow_worldlightchain = light;
2412 R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2413 if (r_shadow_staticworldlights.integer)
2414 R_RTLight_Compile(&light->rtlight);
2417 void R_Shadow_FreeWorldLight(dlight_t *light)
2419 dlight_t **lightpointer;
2420 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2421 if (*lightpointer != light)
2422 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2423 *lightpointer = light->next;
2424 R_RTLight_Uncompile(&light->rtlight);
2428 void R_Shadow_ClearWorldLights(void)
2430 while (r_shadow_worldlightchain)
2431 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2432 r_shadow_selectedlight = NULL;
2433 R_Shadow_FreeCubemaps();
2436 void R_Shadow_SelectLight(dlight_t *light)
2438 if (r_shadow_selectedlight)
2439 r_shadow_selectedlight->selected = false;
2440 r_shadow_selectedlight = light;
2441 if (r_shadow_selectedlight)
2442 r_shadow_selectedlight->selected = true;
2445 rtexture_t *lighttextures[5];
2447 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2449 float scale = r_editlights_cursorgrid.value * 0.5f;
2450 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2453 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2456 const dlight_t *light;
2459 if (light->selected)
2460 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2463 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2466 void R_Shadow_DrawLightSprites(void)
2472 for (i = 0;i < 5;i++)
2474 lighttextures[i] = NULL;
2475 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2476 lighttextures[i] = pic->tex;
2479 for (light = r_shadow_worldlightchain;light;light = light->next)
2480 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2481 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2484 void R_Shadow_SelectLightInView(void)
2486 float bestrating, rating, temp[3];
2487 dlight_t *best, *light;
2490 for (light = r_shadow_worldlightchain;light;light = light->next)
2492 VectorSubtract(light->origin, r_vieworigin, temp);
2493 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2496 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2497 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2499 bestrating = rating;
2504 R_Shadow_SelectLight(best);
2507 void R_Shadow_LoadWorldLights(void)
2509 int n, a, style, shadow;
2510 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2511 float origin[3], radius, color[3], angles[3], corona;
2512 if (cl.worldmodel == NULL)
2514 Con_Print("No map loaded.\n");
2517 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2518 strlcat (name, ".rtlights", sizeof (name));
2519 lightsstring = FS_LoadFile(name, false);
2529 for (;COM_Parse(t, true) && strcmp(
2530 if (COM_Parse(t, true))
2532 if (com_token[0] == '!')
2535 origin[0] = atof(com_token+1);
2538 origin[0] = atof(com_token);
2543 while (*s && *s != '\n')
2549 // check for modifier flags
2555 a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2557 VectorClear(angles);
2560 if (a < 9 || !strcmp(cubemapname, "\"\""))
2565 Con_Printf("found %d parameters on line %i, should be 8 or more parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style \"cubemapname\" corona angles[0] angles[1] angles[2])\n", a, n + 1);
2568 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2569 radius *= r_editlights_rtlightssizescale.value;
2570 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2575 Con_Printf("invalid rtlights file \"%s\"\n", name);
2576 Mem_Free(lightsstring);
2580 void R_Shadow_SaveWorldLights(void)
2583 int bufchars, bufmaxchars;
2585 char name[MAX_QPATH];
2587 if (!r_shadow_worldlightchain)
2589 if (cl.worldmodel == NULL)
2591 Con_Print("No map loaded.\n");
2594 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2595 strlcat (name, ".rtlights", sizeof (name));
2596 bufchars = bufmaxchars = 0;
2598 for (light = r_shadow_worldlightchain;light;light = light->next)
2600 sprintf(line, "%s%f %f %f %f %f %f %f %d %s %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]);
2601 if (bufchars + (int) strlen(line) > bufmaxchars)
2603 bufmaxchars = bufchars + strlen(line) + 2048;
2605 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2609 memcpy(buf, oldbuf, bufchars);
2615 memcpy(buf + bufchars, line, strlen(line));
2616 bufchars += strlen(line);
2620 FS_WriteFile(name, buf, bufchars);
2625 void R_Shadow_LoadLightsFile(void)
2628 char name[MAX_QPATH], *lightsstring, *s, *t;
2629 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2630 if (cl.worldmodel == NULL)
2632 Con_Print("No map loaded.\n");
2635 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2636 strlcat (name, ".lights", sizeof (name));
2637 lightsstring = FS_LoadFile(name, false);
2645 while (*s && *s != '\n')
2650 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);
2654 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);
2657 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2658 radius = bound(15, radius, 4096);
2659 VectorScale(color, (2.0f / (8388608.0f)), color);
2660 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2665 Con_Printf("invalid lights file \"%s\"\n", name);
2666 Mem_Free(lightsstring);
2670 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2672 int entnum, style, islight, skin, pflags, effects;
2673 char key[256], value[1024];
2674 float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2677 if (cl.worldmodel == NULL)
2679 Con_Print("No map loaded.\n");
2682 data = cl.worldmodel->brush.entities;
2685 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2688 origin[0] = origin[1] = origin[2] = 0;
2689 originhack[0] = originhack[1] = originhack[2] = 0;
2690 angles[0] = angles[1] = angles[2] = 0;
2691 color[0] = color[1] = color[2] = 1;
2692 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2702 if (!COM_ParseToken(&data, false))
2704 if (com_token[0] == '}')
2705 break; // end of entity
2706 if (com_token[0] == '_')
2707 strcpy(key, com_token + 1);
2709 strcpy(key, com_token);
2710 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2711 key[strlen(key)-1] = 0;
2712 if (!COM_ParseToken(&data, false))
2714 strcpy(value, com_token);
2716 // now that we have the key pair worked out...
2717 if (!strcmp("light", key))
2718 light = atof(value);
2719 else if (!strcmp("origin", key))
2720 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2721 else if (!strcmp("angle", key))
2722 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2723 else if (!strcmp("angles", key))
2724 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2725 else if (!strcmp("color", key))
2726 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2727 else if (!strcmp("wait", key))
2728 fadescale = atof(value);
2729 else if (!strcmp("classname", key))
2731 if (!strncmp(value, "light", 5))
2734 if (!strcmp(value, "light_fluoro"))
2739 overridecolor[0] = 1;
2740 overridecolor[1] = 1;
2741 overridecolor[2] = 1;
2743 if (!strcmp(value, "light_fluorospark"))
2748 overridecolor[0] = 1;
2749 overridecolor[1] = 1;
2750 overridecolor[2] = 1;
2752 if (!strcmp(value, "light_globe"))
2757 overridecolor[0] = 1;
2758 overridecolor[1] = 0.8;
2759 overridecolor[2] = 0.4;
2761 if (!strcmp(value, "light_flame_large_yellow"))
2766 overridecolor[0] = 1;
2767 overridecolor[1] = 0.5;
2768 overridecolor[2] = 0.1;
2770 if (!strcmp(value, "light_flame_small_yellow"))
2775 overridecolor[0] = 1;
2776 overridecolor[1] = 0.5;
2777 overridecolor[2] = 0.1;
2779 if (!strcmp(value, "light_torch_small_white"))
2784 overridecolor[0] = 1;
2785 overridecolor[1] = 0.5;
2786 overridecolor[2] = 0.1;
2788 if (!strcmp(value, "light_torch_small_walltorch"))
2793 overridecolor[0] = 1;
2794 overridecolor[1] = 0.5;
2795 overridecolor[2] = 0.1;
2799 else if (!strcmp("style", key))
2800 style = atoi(value);
2801 else if (cl.worldmodel->type == mod_brushq3)
2803 if (!strcmp("scale", key))
2804 lightscale = atof(value);
2805 if (!strcmp("fade", key))
2806 fadescale = atof(value);
2808 else if (!strcmp("skin", key))
2809 skin = (int)atof(value);
2810 else if (!strcmp("pflags", key))
2811 pflags = (int)atof(value);
2812 else if (!strcmp("effects", key))
2813 effects = (int)atof(value);
2815 if (light <= 0 && islight)
2817 if (lightscale <= 0)
2821 if (gamemode == GAME_TENEBRAE)
2823 if (effects & EF_NODRAW)
2825 pflags |= PFLAGS_FULLDYNAMIC;
2826 effects &= ~EF_NODRAW;
2829 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2830 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2831 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2832 VectorCopy(overridecolor, color);
2833 VectorScale(color, light, color);
2834 VectorAdd(origin, originhack, origin);
2835 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2836 R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2841 void R_Shadow_SetCursorLocationForView(void)
2843 vec_t dist, push, frac;
2844 vec3_t dest, endpos, normal;
2845 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2846 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2849 dist = frac * r_editlights_cursordistance.value;
2850 push = r_editlights_cursorpushback.value;
2854 VectorMA(endpos, push, r_viewforward, endpos);
2855 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2857 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2858 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2859 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2862 void R_Shadow_UpdateWorldLightSelection(void)
2864 if (r_editlights.integer)
2866 R_Shadow_SetCursorLocationForView();
2867 R_Shadow_SelectLightInView();
2868 R_Shadow_DrawLightSprites();
2871 R_Shadow_SelectLight(NULL);
2874 void R_Shadow_EditLights_Clear_f(void)
2876 R_Shadow_ClearWorldLights();
2879 void R_Shadow_EditLights_Reload_f(void)
2881 r_shadow_reloadlights = true;
2884 void R_Shadow_EditLights_Save_f(void)
2887 R_Shadow_SaveWorldLights();
2890 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2892 R_Shadow_ClearWorldLights();
2893 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2896 void R_Shadow_EditLights_ImportLightsFile_f(void)
2898 R_Shadow_ClearWorldLights();
2899 R_Shadow_LoadLightsFile();
2902 void R_Shadow_EditLights_Spawn_f(void)
2905 if (!r_editlights.integer)
2907 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2910 if (Cmd_Argc() != 1)
2912 Con_Print("r_editlights_spawn does not take parameters\n");
2915 color[0] = color[1] = color[2] = 1;
2916 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2919 void R_Shadow_EditLights_Edit_f(void)
2921 vec3_t origin, angles, color;
2922 vec_t radius, corona;
2924 char cubemapname[1024];
2925 if (!r_editlights.integer)
2927 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2930 if (!r_shadow_selectedlight)
2932 Con_Print("No selected light.\n");
2935 VectorCopy(r_shadow_selectedlight->origin, origin);
2936 VectorCopy(r_shadow_selectedlight->angles, angles);
2937 VectorCopy(r_shadow_selectedlight->color, color);
2938 radius = r_shadow_selectedlight->radius;
2939 style = r_shadow_selectedlight->style;
2940 if (r_shadow_selectedlight->cubemapname)
2941 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2944 shadows = r_shadow_selectedlight->shadow;
2945 corona = r_shadow_selectedlight->corona;
2946 if (!strcmp(Cmd_Argv(1), "origin"))
2948 if (Cmd_Argc() != 5)
2950 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2953 origin[0] = atof(Cmd_Argv(2));
2954 origin[1] = atof(Cmd_Argv(3));
2955 origin[2] = atof(Cmd_Argv(4));
2957 else if (!strcmp(Cmd_Argv(1), "originx"))
2959 if (Cmd_Argc() != 3)
2961 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2964 origin[0] = atof(Cmd_Argv(2));
2966 else if (!strcmp(Cmd_Argv(1), "originy"))
2968 if (Cmd_Argc() != 3)
2970 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2973 origin[1] = atof(Cmd_Argv(2));
2975 else if (!strcmp(Cmd_Argv(1), "originz"))
2977 if (Cmd_Argc() != 3)
2979 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2982 origin[2] = atof(Cmd_Argv(2));
2984 else if (!strcmp(Cmd_Argv(1), "move"))
2986 if (Cmd_Argc() != 5)
2988 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2991 origin[0] += atof(Cmd_Argv(2));
2992 origin[1] += atof(Cmd_Argv(3));
2993 origin[2] += atof(Cmd_Argv(4));
2995 else if (!strcmp(Cmd_Argv(1), "movex"))
2997 if (Cmd_Argc() != 3)
2999 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3002 origin[0] += atof(Cmd_Argv(2));
3004 else if (!strcmp(Cmd_Argv(1), "movey"))
3006 if (Cmd_Argc() != 3)
3008 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3011 origin[1] += atof(Cmd_Argv(2));
3013 else if (!strcmp(Cmd_Argv(1), "movez"))
3015 if (Cmd_Argc() != 3)
3017 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3020 origin[2] += atof(Cmd_Argv(2));
3022 else if (!strcmp(Cmd_Argv(1), "angles"))
3024 if (Cmd_Argc() != 5)
3026 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3029 angles[0] = atof(Cmd_Argv(2));
3030 angles[1] = atof(Cmd_Argv(3));
3031 angles[2] = atof(Cmd_Argv(4));
3033 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3035 if (Cmd_Argc() != 3)
3037 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3040 angles[0] = atof(Cmd_Argv(2));
3042 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3044 if (Cmd_Argc() != 3)
3046 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3049 angles[1] = atof(Cmd_Argv(2));
3051 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3053 if (Cmd_Argc() != 3)
3055 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3058 angles[2] = atof(Cmd_Argv(2));
3060 else if (!strcmp(Cmd_Argv(1), "color"))
3062 if (Cmd_Argc() != 5)
3064 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3067 color[0] = atof(Cmd_Argv(2));
3068 color[1] = atof(Cmd_Argv(3));
3069 color[2] = atof(Cmd_Argv(4));
3071 else if (!strcmp(Cmd_Argv(1), "radius"))
3073 if (Cmd_Argc() != 3)
3075 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3078 radius = atof(Cmd_Argv(2));
3080 else if (!strcmp(Cmd_Argv(1), "style"))
3082 if (Cmd_Argc() != 3)
3084 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3087 style = atoi(Cmd_Argv(2));
3089 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3093 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3096 if (Cmd_Argc() == 3)
3097 strcpy(cubemapname, Cmd_Argv(2));
3101 else if (!strcmp(Cmd_Argv(1), "shadows"))
3103 if (Cmd_Argc() != 3)
3105 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3108 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3110 else if (!strcmp(Cmd_Argv(1), "corona"))
3112 if (Cmd_Argc() != 3)
3114 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3117 corona = atof(Cmd_Argv(2));
3121 Con_Print("usage: r_editlights_edit [property] [value]\n");
3122 Con_Print("Selected light's properties:\n");
3123 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3124 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3125 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3126 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3127 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3128 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3129 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3130 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3133 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3134 r_shadow_selectedlight = NULL;
3135 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3138 extern int con_vislines;
3139 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3143 if (r_shadow_selectedlight == NULL)
3147 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3148 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;
3149 sprintf(temp, "Angles %f %f %f", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3150 sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3151 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3152 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3153 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3154 sprintf(temp, "Shadows %s", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3155 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3158 void R_Shadow_EditLights_ToggleShadow_f(void)
3160 if (!r_editlights.integer)
3162 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3165 if (!r_shadow_selectedlight)
3167 Con_Print("No selected light.\n");
3170 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, r_shadow_selectedlight->corona, r_shadow_selectedlight->style, !r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3171 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3172 r_shadow_selectedlight = NULL;
3175 void R_Shadow_EditLights_ToggleCorona_f(void)
3177 if (!r_editlights.integer)
3179 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3182 if (!r_shadow_selectedlight)
3184 Con_Print("No selected light.\n");
3187 R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->angles, r_shadow_selectedlight->color, r_shadow_selectedlight->radius, !r_shadow_selectedlight->corona, r_shadow_selectedlight->style, r_shadow_selectedlight->shadow, r_shadow_selectedlight->cubemapname);
3188 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3189 r_shadow_selectedlight = NULL;
3192 void R_Shadow_EditLights_Remove_f(void)
3194 if (!r_editlights.integer)
3196 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3199 if (!r_shadow_selectedlight)
3201 Con_Print("No selected light.\n");
3204 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3205 r_shadow_selectedlight = NULL;
3208 void R_Shadow_EditLights_Help_f(void)
3211 "Documentation on r_editlights system:\n"
3213 "r_editlights : enable/disable editing mode\n"
3214 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3215 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3216 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3217 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3218 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3219 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3220 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3222 "r_editlights_help : this help\n"
3223 "r_editlights_clear : remove all lights\n"
3224 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3225 "r_editlights_save : save to .rtlights file\n"
3226 "r_editlights_spawn : create a light with default settings\n"
3227 "r_editlights_edit command : edit selected light - more documentation below\n"
3228 "r_editlights_remove : remove selected light\n"
3229 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3230 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3231 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3233 "origin x y z : set light location\n"
3234 "originx x: set x component of light location\n"
3235 "originy y: set y component of light location\n"
3236 "originz z: set z component of light location\n"
3237 "move x y z : adjust light location\n"
3238 "movex x: adjust x component of light location\n"
3239 "movey y: adjust y component of light location\n"
3240 "movez z: adjust z component of light location\n"
3241 "angles x y z : set light angles\n"
3242 "anglesx x: set x component of light angles\n"
3243 "anglesy y: set y component of light angles\n"
3244 "anglesz z: set z component of light angles\n"
3245 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3246 "radius radius : set radius (size) of light\n"
3247 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3248 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3249 "shadows 1/0 : turn on/off shadows\n"
3250 "corona n : set corona intensity\n"
3251 "<nothing> : print light properties to console\n"
3255 void R_Shadow_EditLights_Init(void)
3257 Cvar_RegisterVariable(&r_editlights);
3258 Cvar_RegisterVariable(&r_editlights_cursordistance);
3259 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3260 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3261 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3262 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3263 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3264 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3265 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3266 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3267 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3268 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3269 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3270 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3271 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3272 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3273 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3274 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3275 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);