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_Color(0, 0, 0, 1);
741 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
742 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
743 r_shadowstage = SHADOWSTAGE_NONE;
745 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
746 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
747 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
750 void R_Shadow_LoadWorldLightsIfNeeded(void)
752 if (r_shadow_reloadlights && cl.worldmodel)
754 R_Shadow_ClearWorldLights();
755 r_shadow_reloadlights = false;
756 R_Shadow_LoadWorldLights();
757 if (r_shadow_worldlightchain == NULL)
759 R_Shadow_LoadLightsFile();
760 if (r_shadow_worldlightchain == NULL)
761 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
766 void R_Shadow_Stage_ShadowVolumes(void)
769 memset(&m, 0, sizeof(m));
771 GL_Color(1, 1, 1, 1);
772 GL_ColorMask(0, 0, 0, 0);
773 GL_BlendFunc(GL_ONE, GL_ZERO);
776 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
777 //if (r_shadow_polygonoffset.value != 0)
779 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
780 // qglEnable(GL_POLYGON_OFFSET_FILL);
783 // qglDisable(GL_POLYGON_OFFSET_FILL);
784 qglDepthFunc(GL_LESS);
785 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
786 qglEnable(GL_STENCIL_TEST);
787 qglStencilFunc(GL_ALWAYS, 128, ~0);
788 if (gl_ext_stenciltwoside.integer)
790 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
791 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
792 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
794 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
795 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
797 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
801 r_shadowstage = SHADOWSTAGE_STENCIL;
803 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
805 GL_Clear(GL_STENCIL_BUFFER_BIT);
807 // LordHavoc note: many shadow volumes reside entirely inside the world
808 // (that is to say they are entirely bounded by their lit surfaces),
809 // which can be optimized by handling things as an inverted light volume,
810 // with the shadow boundaries of the world being simulated by an altered
811 // (129) bias to stencil clearing on such lights
812 // FIXME: generate inverted light volumes for use as shadow volumes and
813 // optimize for them as noted above
816 void R_Shadow_Stage_LightWithoutShadows(void)
819 memset(&m, 0, sizeof(m));
821 GL_BlendFunc(GL_ONE, GL_ONE);
824 qglPolygonOffset(0, 0);
825 //qglDisable(GL_POLYGON_OFFSET_FILL);
826 GL_Color(1, 1, 1, 1);
827 GL_ColorMask(1, 1, 1, 1);
828 qglDepthFunc(GL_EQUAL);
829 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
830 qglDisable(GL_STENCIL_TEST);
831 if (gl_support_stenciltwoside)
832 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
834 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
835 qglStencilFunc(GL_EQUAL, 128, 0xFF);
836 r_shadowstage = SHADOWSTAGE_LIGHT;
840 void R_Shadow_Stage_LightWithShadows(void)
843 memset(&m, 0, sizeof(m));
845 GL_BlendFunc(GL_ONE, GL_ONE);
848 qglPolygonOffset(0, 0);
849 //qglDisable(GL_POLYGON_OFFSET_FILL);
850 GL_Color(1, 1, 1, 1);
851 GL_ColorMask(1, 1, 1, 1);
852 qglDepthFunc(GL_EQUAL);
853 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
854 qglEnable(GL_STENCIL_TEST);
855 if (gl_support_stenciltwoside)
856 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
858 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
859 // only draw light where this geometry was already rendered AND the
860 // stencil is 128 (values other than this mean shadow)
861 qglStencilFunc(GL_EQUAL, 128, 0xFF);
862 r_shadowstage = SHADOWSTAGE_LIGHT;
866 void R_Shadow_Stage_End(void)
869 memset(&m, 0, sizeof(m));
871 GL_BlendFunc(GL_ONE, GL_ZERO);
874 qglPolygonOffset(0, 0);
875 //qglDisable(GL_POLYGON_OFFSET_FILL);
876 GL_Color(1, 1, 1, 1);
877 GL_ColorMask(1, 1, 1, 1);
878 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
879 qglDepthFunc(GL_LEQUAL);
880 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
881 qglDisable(GL_STENCIL_TEST);
882 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
883 if (gl_support_stenciltwoside)
884 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
886 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
887 r_shadowstage = SHADOWSTAGE_NONE;
890 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
892 int i, ix1, iy1, ix2, iy2;
893 float x1, y1, x2, y2, x, y, f;
896 if (!r_shadow_scissor.integer)
898 // if view is inside the box, just say yes it's visible
899 // LordHavoc: for some odd reason scissor seems broken without stencil
900 // (?!? seems like a driver bug) so abort if gl_stencil is false
901 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
903 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
906 for (i = 0;i < 3;i++)
908 if (r_viewforward[i] >= 0)
919 f = DotProduct(r_viewforward, r_vieworigin) + 1;
920 if (DotProduct(r_viewforward, v2) <= f)
922 // entirely behind nearclip plane
925 if (DotProduct(r_viewforward, v) >= f)
927 // entirely infront of nearclip plane
928 x1 = y1 = x2 = y2 = 0;
929 for (i = 0;i < 8;i++)
931 v[0] = (i & 1) ? mins[0] : maxs[0];
932 v[1] = (i & 2) ? mins[1] : maxs[1];
933 v[2] = (i & 4) ? mins[2] : maxs[2];
935 GL_TransformToScreen(v, v2);
936 //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]);
955 // clipped by nearclip plane
956 // this is nasty and crude...
957 // create viewspace bbox
958 for (i = 0;i < 8;i++)
960 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
961 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
962 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
963 v2[0] = -DotProduct(v, r_viewleft);
964 v2[1] = DotProduct(v, r_viewup);
965 v2[2] = DotProduct(v, r_viewforward);
968 if (smins[0] > v2[0]) smins[0] = v2[0];
969 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
970 if (smins[1] > v2[1]) smins[1] = v2[1];
971 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
972 if (smins[2] > v2[2]) smins[2] = v2[2];
973 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
977 smins[0] = smaxs[0] = v2[0];
978 smins[1] = smaxs[1] = v2[1];
979 smins[2] = smaxs[2] = v2[2];
982 // now we have a bbox in viewspace
983 // clip it to the view plane
986 // return true if that culled the box
987 if (smins[2] >= smaxs[2])
989 // ok some of it is infront of the view, transform each corner back to
990 // worldspace and then to screenspace and make screen rect
991 // initialize these variables just to avoid compiler warnings
992 x1 = y1 = x2 = y2 = 0;
993 for (i = 0;i < 8;i++)
995 v2[0] = (i & 1) ? smins[0] : smaxs[0];
996 v2[1] = (i & 2) ? smins[1] : smaxs[1];
997 v2[2] = (i & 4) ? smins[2] : smaxs[2];
998 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
999 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1000 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1002 GL_TransformToScreen(v, v2);
1003 //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]);
1020 // this code doesn't handle boxes with any points behind view properly
1021 x1 = 1000;x2 = -1000;
1022 y1 = 1000;y2 = -1000;
1023 for (i = 0;i < 8;i++)
1025 v[0] = (i & 1) ? mins[0] : maxs[0];
1026 v[1] = (i & 2) ? mins[1] : maxs[1];
1027 v[2] = (i & 4) ? mins[2] : maxs[2];
1029 GL_TransformToScreen(v, v2);
1030 //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]);
1048 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1049 if (ix1 < r_view_x) ix1 = r_view_x;
1050 if (iy1 < r_view_y) iy1 = r_view_y;
1051 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1052 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1053 if (ix2 <= ix1 || iy2 <= iy1)
1055 // set up the scissor rectangle
1056 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1057 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1058 //qglEnable(GL_SCISSOR_TEST);
1063 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1065 float *color4f = varray_color4f;
1066 float dist, dot, intensity, v[3], n[3];
1067 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1069 Matrix4x4_Transform(m, vertex3f, v);
1070 if ((dist = DotProduct(v, v)) < 1)
1072 Matrix4x4_Transform3x3(m, normal3f, n);
1073 if ((dot = DotProduct(n, v)) > 0)
1076 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1077 VectorScale(lightcolor, intensity, color4f);
1082 VectorClear(color4f);
1088 VectorClear(color4f);
1094 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1096 float *color4f = varray_color4f;
1097 float dist, dot, intensity, v[3], n[3];
1098 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1100 Matrix4x4_Transform(m, vertex3f, v);
1101 if ((dist = fabs(v[2])) < 1)
1103 Matrix4x4_Transform3x3(m, normal3f, n);
1104 if ((dot = DotProduct(n, v)) > 0)
1106 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1107 VectorScale(lightcolor, intensity, color4f);
1112 VectorClear(color4f);
1118 VectorClear(color4f);
1124 // FIXME: this should be done in a vertex program when possible
1125 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1126 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1130 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1131 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1132 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1139 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1143 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1144 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1151 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)
1155 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1157 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1158 // the cubemap normalizes this for us
1159 out3f[0] = DotProduct(svector3f, lightdir);
1160 out3f[1] = DotProduct(tvector3f, lightdir);
1161 out3f[2] = DotProduct(normal3f, lightdir);
1165 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)
1168 float lightdir[3], eyedir[3], halfdir[3];
1169 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1171 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1172 VectorNormalizeFast(lightdir);
1173 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1174 VectorNormalizeFast(eyedir);
1175 VectorAdd(lightdir, eyedir, halfdir);
1176 // the cubemap normalizes this for us
1177 out3f[0] = DotProduct(svector3f, halfdir);
1178 out3f[1] = DotProduct(tvector3f, halfdir);
1179 out3f[2] = DotProduct(normal3f, halfdir);
1183 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)
1186 float color[3], color2[3];
1188 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1191 bumptexture = r_shadow_blankbumptexture;
1193 // colorscale accounts for how much we multiply the brightness during combine
1194 // mult is how many times the final pass of the lighting will be
1195 // performed to get more brightness than otherwise possible
1196 // limit mult to 64 for sanity sake
1197 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1199 // 3/2 3D combine path (Geforce3, Radeon 8500)
1200 memset(&m, 0, sizeof(m));
1201 m.pointer_vertex = vertex3f;
1202 m.tex[0] = R_GetTexture(bumptexture);
1203 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1204 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1205 m.texcombinergb[0] = GL_REPLACE;
1206 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1207 m.pointer_texcoord[0] = texcoord2f;
1208 m.pointer_texcoord[1] = varray_texcoord3f[1];
1209 m.pointer_texcoord[2] = varray_texcoord3f[2];
1211 GL_ColorMask(0,0,0,1);
1212 GL_BlendFunc(GL_ONE, GL_ZERO);
1213 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1214 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1215 R_Mesh_Draw(numverts, numtriangles, elements);
1217 c_rt_lighttris += numtriangles;
1219 memset(&m, 0, sizeof(m));
1220 m.pointer_vertex = vertex3f;
1221 m.tex[0] = R_GetTexture(basetexture);
1222 m.pointer_texcoord[0] = texcoord2f;
1225 m.texcubemap[1] = R_GetTexture(lightcubemap);
1226 m.pointer_texcoord[1] = varray_texcoord3f[1];
1227 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1230 GL_ColorMask(1,1,1,0);
1231 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1232 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1233 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1235 color[0] = bound(0, color2[0], 1);
1236 color[1] = bound(0, color2[1], 1);
1237 color[2] = bound(0, color2[2], 1);
1238 GL_Color(color[0], color[1], color[2], 1);
1239 R_Mesh_Draw(numverts, numtriangles, elements);
1241 c_rt_lighttris += numtriangles;
1244 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1246 // 1/2/2 3D combine path (original Radeon)
1247 memset(&m, 0, sizeof(m));
1248 m.pointer_vertex = vertex3f;
1249 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1250 m.pointer_texcoord[0] = varray_texcoord3f[0];
1252 GL_ColorMask(0,0,0,1);
1253 GL_BlendFunc(GL_ONE, GL_ZERO);
1254 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1255 R_Mesh_Draw(numverts, numtriangles, elements);
1257 c_rt_lighttris += numtriangles;
1259 memset(&m, 0, sizeof(m));
1260 m.pointer_vertex = vertex3f;
1261 m.tex[0] = R_GetTexture(bumptexture);
1262 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1263 m.texcombinergb[0] = GL_REPLACE;
1264 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1265 m.pointer_texcoord[0] = texcoord2f;
1266 m.pointer_texcoord[1] = varray_texcoord3f[1];
1268 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1269 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1270 R_Mesh_Draw(numverts, numtriangles, elements);
1272 c_rt_lighttris += numtriangles;
1274 memset(&m, 0, sizeof(m));
1275 m.pointer_vertex = vertex3f;
1276 m.tex[0] = R_GetTexture(basetexture);
1277 m.pointer_texcoord[0] = texcoord2f;
1280 m.texcubemap[1] = R_GetTexture(lightcubemap);
1281 m.pointer_texcoord[1] = varray_texcoord3f[1];
1282 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1285 GL_ColorMask(1,1,1,0);
1286 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1287 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1288 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1290 color[0] = bound(0, color2[0], 1);
1291 color[1] = bound(0, color2[1], 1);
1292 color[2] = bound(0, color2[2], 1);
1293 GL_Color(color[0], color[1], color[2], 1);
1294 R_Mesh_Draw(numverts, numtriangles, elements);
1296 c_rt_lighttris += numtriangles;
1299 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1301 // 2/2 3D combine path (original Radeon)
1302 memset(&m, 0, sizeof(m));
1303 m.pointer_vertex = vertex3f;
1304 m.tex[0] = R_GetTexture(bumptexture);
1305 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1306 m.texcombinergb[0] = GL_REPLACE;
1307 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1308 m.pointer_texcoord[0] = texcoord2f;
1309 m.pointer_texcoord[1] = varray_texcoord3f[1];
1311 GL_ColorMask(0,0,0,1);
1312 GL_BlendFunc(GL_ONE, GL_ZERO);
1313 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1314 R_Mesh_Draw(numverts, numtriangles, elements);
1316 c_rt_lighttris += numtriangles;
1318 memset(&m, 0, sizeof(m));
1319 m.pointer_vertex = vertex3f;
1320 m.tex[0] = R_GetTexture(basetexture);
1321 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1322 m.pointer_texcoord[0] = texcoord2f;
1323 m.pointer_texcoord[1] = varray_texcoord3f[1];
1325 GL_ColorMask(1,1,1,0);
1326 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1327 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1328 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1329 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1331 color[0] = bound(0, color2[0], 1);
1332 color[1] = bound(0, color2[1], 1);
1333 color[2] = bound(0, color2[2], 1);
1334 GL_Color(color[0], color[1], color[2], 1);
1335 R_Mesh_Draw(numverts, numtriangles, elements);
1337 c_rt_lighttris += numtriangles;
1340 else if (r_textureunits.integer >= 4)
1342 // 4/2 2D combine path (Geforce3, Radeon 8500)
1343 memset(&m, 0, sizeof(m));
1344 m.pointer_vertex = vertex3f;
1345 m.tex[0] = R_GetTexture(bumptexture);
1346 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1347 m.texcombinergb[0] = GL_REPLACE;
1348 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1349 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1350 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1351 m.pointer_texcoord[0] = texcoord2f;
1352 m.pointer_texcoord[1] = varray_texcoord3f[1];
1353 m.pointer_texcoord[2] = varray_texcoord2f[2];
1354 m.pointer_texcoord[3] = varray_texcoord2f[3];
1356 GL_ColorMask(0,0,0,1);
1357 GL_BlendFunc(GL_ONE, GL_ZERO);
1358 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1359 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1360 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1361 R_Mesh_Draw(numverts, numtriangles, elements);
1363 c_rt_lighttris += numtriangles;
1365 memset(&m, 0, sizeof(m));
1366 m.pointer_vertex = vertex3f;
1367 m.tex[0] = R_GetTexture(basetexture);
1368 m.pointer_texcoord[0] = texcoord2f;
1371 m.texcubemap[1] = R_GetTexture(lightcubemap);
1372 m.pointer_texcoord[1] = varray_texcoord3f[1];
1373 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1376 GL_ColorMask(1,1,1,0);
1377 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1378 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1379 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1381 color[0] = bound(0, color2[0], 1);
1382 color[1] = bound(0, color2[1], 1);
1383 color[2] = bound(0, color2[2], 1);
1384 GL_Color(color[0], color[1], color[2], 1);
1385 R_Mesh_Draw(numverts, numtriangles, elements);
1387 c_rt_lighttris += numtriangles;
1392 // 2/2/2 2D combine path (any dot3 card)
1393 memset(&m, 0, sizeof(m));
1394 m.pointer_vertex = vertex3f;
1395 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1396 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1397 m.pointer_texcoord[0] = varray_texcoord2f[0];
1398 m.pointer_texcoord[1] = varray_texcoord2f[1];
1400 GL_ColorMask(0,0,0,1);
1401 GL_BlendFunc(GL_ONE, GL_ZERO);
1402 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1403 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1404 R_Mesh_Draw(numverts, numtriangles, elements);
1406 c_rt_lighttris += numtriangles;
1408 memset(&m, 0, sizeof(m));
1409 m.pointer_vertex = vertex3f;
1410 m.tex[0] = R_GetTexture(bumptexture);
1411 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1412 m.texcombinergb[0] = GL_REPLACE;
1413 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1414 m.pointer_texcoord[0] = texcoord2f;
1415 m.pointer_texcoord[1] = varray_texcoord3f[1];
1417 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1418 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1419 R_Mesh_Draw(numverts, numtriangles, elements);
1421 c_rt_lighttris += numtriangles;
1423 memset(&m, 0, sizeof(m));
1424 m.pointer_vertex = vertex3f;
1425 m.tex[0] = R_GetTexture(basetexture);
1426 m.pointer_texcoord[0] = texcoord2f;
1429 m.texcubemap[1] = R_GetTexture(lightcubemap);
1430 m.pointer_texcoord[1] = varray_texcoord3f[1];
1431 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1434 GL_ColorMask(1,1,1,0);
1435 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1436 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1437 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1439 color[0] = bound(0, color2[0], 1);
1440 color[1] = bound(0, color2[1], 1);
1441 color[2] = bound(0, color2[2], 1);
1442 GL_Color(color[0], color[1], color[2], 1);
1443 R_Mesh_Draw(numverts, numtriangles, elements);
1445 c_rt_lighttris += numtriangles;
1451 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1452 GL_DepthMask(false);
1454 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1455 memset(&m, 0, sizeof(m));
1456 m.pointer_vertex = vertex3f;
1457 m.pointer_color = varray_color4f;
1458 m.tex[0] = R_GetTexture(basetexture);
1459 m.pointer_texcoord[0] = texcoord2f;
1460 if (r_textureunits.integer >= 2)
1463 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1464 m.pointer_texcoord[1] = varray_texcoord2f[1];
1465 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1468 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1470 color[0] = bound(0, color2[0], 1);
1471 color[1] = bound(0, color2[1], 1);
1472 color[2] = bound(0, color2[2], 1);
1473 if (r_textureunits.integer >= 2)
1474 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1476 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1477 R_Mesh_Draw(numverts, numtriangles, elements);
1479 c_rt_lighttris += numtriangles;
1484 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)
1487 float color[3], color2[3], colorscale;
1489 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1492 glosstexture = r_shadow_blankglosstexture;
1493 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1495 colorscale = r_shadow_glossintensity.value;
1497 bumptexture = r_shadow_blankbumptexture;
1498 if (glosstexture == r_shadow_blankglosstexture)
1499 colorscale *= r_shadow_gloss2intensity.value;
1501 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1503 // 2/0/0/1/2 3D combine blendsquare path
1504 memset(&m, 0, sizeof(m));
1505 m.pointer_vertex = vertex3f;
1506 m.tex[0] = R_GetTexture(bumptexture);
1507 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1508 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1509 m.pointer_texcoord[0] = texcoord2f;
1510 m.pointer_texcoord[1] = varray_texcoord3f[1];
1512 GL_ColorMask(0,0,0,1);
1513 // this squares the result
1514 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1515 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1516 R_Mesh_Draw(numverts, numtriangles, elements);
1518 c_rt_lighttris += numtriangles;
1520 memset(&m, 0, sizeof(m));
1521 m.pointer_vertex = vertex3f;
1523 // square alpha in framebuffer a few times to make it shiny
1524 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1525 // these comments are a test run through this math for intensity 0.5
1526 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1527 // 0.25 * 0.25 = 0.0625 (this is another pass)
1528 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1529 R_Mesh_Draw(numverts, numtriangles, elements);
1531 c_rt_lighttris += numtriangles;
1532 R_Mesh_Draw(numverts, numtriangles, elements);
1534 c_rt_lighttris += numtriangles;
1536 memset(&m, 0, sizeof(m));
1537 m.pointer_vertex = vertex3f;
1538 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1539 m.pointer_texcoord[0] = varray_texcoord3f[0];
1541 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1542 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1543 R_Mesh_Draw(numverts, numtriangles, elements);
1545 c_rt_lighttris += numtriangles;
1547 memset(&m, 0, sizeof(m));
1548 m.pointer_vertex = vertex3f;
1549 m.tex[0] = R_GetTexture(glosstexture);
1552 m.texcubemap[1] = R_GetTexture(lightcubemap);
1553 m.pointer_texcoord[1] = varray_texcoord3f[1];
1554 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1556 m.pointer_texcoord[0] = texcoord2f;
1558 GL_ColorMask(1,1,1,0);
1559 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1560 VectorScale(lightcolor, colorscale, color2);
1561 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1563 color[0] = bound(0, color2[0], 1);
1564 color[1] = bound(0, color2[1], 1);
1565 color[2] = bound(0, color2[2], 1);
1566 GL_Color(color[0], color[1], color[2], 1);
1567 R_Mesh_Draw(numverts, numtriangles, elements);
1569 c_rt_lighttris += numtriangles;
1572 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1574 // 2/0/0/2 3D combine blendsquare path
1575 memset(&m, 0, sizeof(m));
1576 m.pointer_vertex = vertex3f;
1577 m.tex[0] = R_GetTexture(bumptexture);
1578 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1579 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1580 m.pointer_texcoord[0] = texcoord2f;
1581 m.pointer_texcoord[1] = varray_texcoord3f[1];
1583 GL_ColorMask(0,0,0,1);
1584 // this squares the result
1585 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1586 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1587 R_Mesh_Draw(numverts, numtriangles, elements);
1589 c_rt_lighttris += numtriangles;
1591 memset(&m, 0, sizeof(m));
1592 m.pointer_vertex = vertex3f;
1594 // square alpha in framebuffer a few times to make it shiny
1595 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1596 // these comments are a test run through this math for intensity 0.5
1597 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1598 // 0.25 * 0.25 = 0.0625 (this is another pass)
1599 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1600 R_Mesh_Draw(numverts, numtriangles, elements);
1602 c_rt_lighttris += numtriangles;
1603 R_Mesh_Draw(numverts, numtriangles, elements);
1605 c_rt_lighttris += numtriangles;
1607 memset(&m, 0, sizeof(m));
1608 m.pointer_vertex = vertex3f;
1609 m.tex[0] = R_GetTexture(glosstexture);
1610 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1611 m.pointer_texcoord[0] = texcoord2f;
1612 m.pointer_texcoord[1] = varray_texcoord3f[1];
1614 GL_ColorMask(1,1,1,0);
1615 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1616 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1617 VectorScale(lightcolor, colorscale, color2);
1618 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1620 color[0] = bound(0, color2[0], 1);
1621 color[1] = bound(0, color2[1], 1);
1622 color[2] = bound(0, color2[2], 1);
1623 GL_Color(color[0], color[1], color[2], 1);
1624 R_Mesh_Draw(numverts, numtriangles, elements);
1626 c_rt_lighttris += numtriangles;
1629 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1631 // 2/0/0/2/2 2D combine blendsquare path
1632 memset(&m, 0, sizeof(m));
1633 m.pointer_vertex = vertex3f;
1634 m.tex[0] = R_GetTexture(bumptexture);
1635 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1636 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1637 m.pointer_texcoord[0] = texcoord2f;
1638 m.pointer_texcoord[1] = varray_texcoord3f[1];
1640 GL_ColorMask(0,0,0,1);
1641 // this squares the result
1642 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1643 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1644 R_Mesh_Draw(numverts, numtriangles, elements);
1646 c_rt_lighttris += numtriangles;
1648 memset(&m, 0, sizeof(m));
1649 m.pointer_vertex = vertex3f;
1651 // square alpha in framebuffer a few times to make it shiny
1652 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1653 // these comments are a test run through this math for intensity 0.5
1654 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1655 // 0.25 * 0.25 = 0.0625 (this is another pass)
1656 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1657 R_Mesh_Draw(numverts, numtriangles, elements);
1659 c_rt_lighttris += numtriangles;
1660 R_Mesh_Draw(numverts, numtriangles, elements);
1662 c_rt_lighttris += numtriangles;
1664 memset(&m, 0, sizeof(m));
1665 m.pointer_vertex = vertex3f;
1666 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1667 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1668 m.pointer_texcoord[0] = varray_texcoord2f[0];
1669 m.pointer_texcoord[1] = varray_texcoord2f[1];
1671 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1672 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1673 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1674 R_Mesh_Draw(numverts, numtriangles, elements);
1676 c_rt_lighttris += numtriangles;
1678 memset(&m, 0, sizeof(m));
1679 m.pointer_vertex = vertex3f;
1680 m.tex[0] = R_GetTexture(glosstexture);
1683 m.texcubemap[1] = R_GetTexture(lightcubemap);
1684 m.pointer_texcoord[1] = varray_texcoord3f[1];
1685 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1687 m.pointer_texcoord[0] = texcoord2f;
1689 GL_ColorMask(1,1,1,0);
1690 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1691 VectorScale(lightcolor, colorscale, color2);
1692 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1694 color[0] = bound(0, color2[0], 1);
1695 color[1] = bound(0, color2[1], 1);
1696 color[2] = bound(0, color2[2], 1);
1697 GL_Color(color[0], color[1], color[2], 1);
1698 R_Mesh_Draw(numverts, numtriangles, elements);
1700 c_rt_lighttris += numtriangles;
1706 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1710 R_RTLight_Uncompile(rtlight);
1711 memset(rtlight, 0, sizeof(*rtlight));
1713 VectorCopy(light->origin, rtlight->shadoworigin);
1714 VectorCopy(light->color, rtlight->color);
1715 rtlight->radius = light->radius;
1716 rtlight->cullradius = rtlight->radius;
1717 rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
1718 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
1719 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
1720 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
1721 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
1722 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
1723 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
1724 rtlight->cubemapname[0] = 0;
1725 if (light->cubemapname[0])
1726 strcpy(rtlight->cubemapname, light->cubemapname);
1727 else if (light->cubemapnum > 0)
1728 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1729 rtlight->shadow = light->shadow;
1730 rtlight->corona = light->corona;
1731 rtlight->style = light->style;
1732 rtlight->isstatic = isstatic;
1733 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1734 // ConcatScale won't work here because this needs to scale rotate and
1735 // translate, not just rotate
1736 scale = 1.0f / rtlight->radius;
1737 for (k = 0;k < 3;k++)
1738 for (j = 0;j < 4;j++)
1739 rtlight->matrix_worldtolight.m[k][j] *= scale;
1740 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1741 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1743 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1744 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1745 VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1746 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1749 // compiles rtlight geometry
1750 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1751 void R_RTLight_Compile(rtlight_t *rtlight)
1753 int i, j, k, l, maxverts = 256, tris;
1754 float *vertex3f = NULL, mins[3], maxs[3];
1755 shadowmesh_t *mesh, *castmesh = NULL;
1757 qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1758 qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1760 // compile the light
1761 rtlight->compiled = true;
1762 VectorCopy(rtlight->cullmins, mins);
1763 VectorCopy(rtlight->cullmaxs, maxs);
1764 if (rtlight->shadow)
1765 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1766 rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1769 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
1770 memset(lightpvs, 0, lightpvsbytes);
1771 if (cl.worldmodel->brushq3.num_leafs)
1776 // make a pvs that only includes things within the box
1777 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1778 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1779 SETPVSBIT(lightpvs, leaf->clusterindex);
1781 // make a cluster list for fast visibility checking during rendering
1782 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1783 if (CHECKPVSBIT(lightpvs, i))
1784 rtlight->static_numclusters++;
1785 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1786 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1787 if (CHECKPVSBIT(lightpvs, i))
1788 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1790 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1791 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1792 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1793 face->lighttemp_castshadow = false;
1794 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1796 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1798 for (k = 0;k < 3;k++)
1800 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1801 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1803 for (j = 0;j < leaf->numleaffaces;j++)
1805 face = leaf->firstleafface[j];
1806 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1807 face->lighttemp_castshadow = true;
1812 // add surfaces to shadow casting mesh and light mesh
1813 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1815 if (face->lighttemp_castshadow)
1817 face->lighttemp_castshadow = false;
1818 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1820 if (rtlight->shadow)
1821 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1822 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1823 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1824 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);
1829 else if (cl.worldmodel->brushq1.num_leafs)
1833 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1834 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1835 i = CL_PointQ1Contents(rtlight->shadoworigin);
1837 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1838 surf->lighttemp_castshadow = false;
1840 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1843 qbyte *bytesurfacepvs;
1845 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
1846 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1848 Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
1850 // make a pvs that only includes things within the box
1851 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1853 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1855 SETPVSBIT(lightpvs, leaf->clusterindex);
1856 for (k = 0;k < 3;k++)
1858 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1859 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1864 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1865 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1866 surf->lighttemp_castshadow = true;
1868 Mem_Free(byteleafpvs);
1869 Mem_Free(bytesurfacepvs);
1871 // make a cluster list for fast visibility checking during rendering
1872 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1873 if (CHECKPVSBIT(lightpvs, i))
1874 rtlight->static_numclusters++;
1875 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1876 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1877 if (CHECKPVSBIT(lightpvs, i))
1878 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1882 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1884 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1886 // make a pvs that only includes things within the box
1887 SETPVSBIT(lightpvs, leaf->clusterindex);
1888 for (k = 0;k < 3;k++)
1890 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1891 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1893 for (j = 0;j < leaf->nummarksurfaces;j++)
1895 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1896 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1897 surf->lighttemp_castshadow = true;
1902 // make a pvs that only includes things within the box
1903 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1904 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1905 SETPVSBIT(lightpvs, leaf->clusterindex);
1907 // make a cluster list for fast visibility checking during rendering
1908 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1909 if (CHECKPVSBIT(lightpvs, i))
1910 rtlight->static_numclusters++;
1911 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1912 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1913 if (CHECKPVSBIT(lightpvs, i))
1914 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1917 // add surfaces to shadow casting mesh and light mesh
1918 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1920 if (surf->lighttemp_castshadow)
1922 surf->lighttemp_castshadow = false;
1923 if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
1924 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);
1925 if (!(surf->flags & SURF_DRAWSKY))
1926 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);
1932 // limit box to light bounds (in case it grew larger)
1933 for (k = 0;k < 3;k++)
1935 if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
1936 if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
1938 rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1939 rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1941 // cast shadow volume from castmesh
1942 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1946 for (mesh = castmesh;mesh;mesh = mesh->next)
1948 R_Shadow_ResizeShadowElements(mesh->numtriangles);
1949 maxverts = max(maxverts, mesh->numverts * 2);
1954 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1955 // now that we have the buffers big enough, construct and add
1956 // the shadow volume mesh
1957 if (rtlight->shadow)
1958 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1959 for (mesh = castmesh;mesh;mesh = mesh->next)
1961 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1962 R_Shadow_PrepareShadowMark(mesh->numtriangles);
1963 for (i = 0;i < mesh->numtriangles;i++)
1966 v[0] = mesh->vertex3f + mesh->element3i[i*3+0] * 3;
1967 v[1] = mesh->vertex3f + mesh->element3i[i*3+1] * 3;
1968 v[2] = mesh->vertex3f + mesh->element3i[i*3+2] * 3;
1969 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])))
1970 shadowmarklist[numshadowmark++] = i;
1972 if (maxshadowelements < numshadowmark * 24)
1973 R_Shadow_ResizeShadowElements((numshadowmark + 256) * 24);
1974 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)))
1975 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
1980 // we're done with castmesh now
1981 Mod_ShadowMesh_Free(castmesh);
1984 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1985 rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1988 if (rtlight->static_meshchain_shadow)
1989 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1990 k += mesh->numtriangles;
1992 if (rtlight->static_meshchain_light)
1993 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
1994 l += mesh->numtriangles;
1995 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);
1998 void R_RTLight_Uncompile(rtlight_t *rtlight)
2000 if (rtlight->compiled)
2002 if (rtlight->static_meshchain_shadow)
2003 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2004 rtlight->static_meshchain_shadow = NULL;
2005 if (rtlight->static_meshchain_light)
2006 Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2007 rtlight->static_meshchain_light = NULL;
2008 if (rtlight->static_clusterindices)
2009 Mem_Free(rtlight->static_clusterindices);
2010 rtlight->static_clusterindices = NULL;
2011 rtlight->static_numclusters = 0;
2012 rtlight->compiled = false;
2016 int shadowframecount = 0;
2018 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
2021 if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
2023 vec3_t relativeshadoworigin;
2024 Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
2025 ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
2029 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2031 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2034 entity_render_t *ent;
2036 vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2037 rtexture_t *cubemaptexture;
2038 matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2040 if (d_lightstylevalue[rtlight->style] <= 0)
2042 if (rtlight->compiled)
2044 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2046 for (i = 0;i < rtlight->static_numclusters;i++)
2047 if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
2049 if (i == rtlight->static_numclusters)
2052 else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2054 if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2057 if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2058 R_RTLight_Compile(rtlight);
2060 f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2061 VectorScale(rtlight->color, f, lightcolor);
2063 if (rtlight->selected)
2065 f = 2 + sin(realtime * M_PI * 4.0);
2066 VectorScale(lightcolor, f, lightcolor);
2070 if (rtlight->cubemapname[0])
2071 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2073 cubemaptexture = NULL;
2075 shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2076 if (shadow && (gl_stencil || visiblevolumes))
2078 if (!visiblevolumes)
2079 R_Shadow_Stage_ShadowVolumes();
2080 ent = &cl_entities[0].render;
2081 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2083 R_Mesh_Matrix(&ent->matrix);
2084 if (r_shadow_showtris.integer)
2088 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2089 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2090 qglDisable(GL_DEPTH_TEST);
2091 qglDisable(GL_STENCIL_TEST);
2092 //qglDisable(GL_CULL_FACE);
2093 GL_ColorMask(1,1,1,1);
2094 GL_Color(0,0.1,0,1);
2095 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2096 memset(&m, 0, sizeof(m));
2097 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2099 m.pointer_vertex = mesh->vertex3f;
2101 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2103 //qglEnable(GL_CULL_FACE);
2105 qglEnable(GL_DEPTH_TEST);
2108 qglEnable(GL_STENCIL_TEST);
2109 GL_ColorMask(0,0,0,0);
2112 R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
2115 R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2116 if (r_drawentities.integer)
2117 for (i = 0;i < r_refdef.numentities;i++)
2118 if (r_refdef.entities[i]->flags & RENDER_SHADOW)
2119 R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2122 if (!visiblevolumes)
2124 if (shadow && gl_stencil)
2125 R_Shadow_Stage_LightWithShadows();
2127 R_Shadow_Stage_LightWithoutShadows();
2129 ent = &cl_entities[0].render;
2130 if (ent->model && ent->model->DrawLight)
2132 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2133 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2134 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2135 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2136 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2137 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2139 //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2141 R_Mesh_Matrix(&ent->matrix);
2142 if (r_shadow_showtris.integer)
2145 int depthenabled = qglIsEnabled(GL_DEPTH_TEST);
2146 int stencilenabled = qglIsEnabled(GL_STENCIL_TEST);
2147 qglDisable(GL_DEPTH_TEST);
2148 qglDisable(GL_STENCIL_TEST);
2149 //qglDisable(GL_CULL_FACE);
2150 memset(&m, 0, sizeof(m));
2151 GL_Color(0.2,0,0,1);
2152 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2153 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2155 m.pointer_vertex = mesh->vertex3f;
2157 R_Mesh_Draw_ShowTris(mesh->numverts, mesh->numtriangles, mesh->element3i);
2159 //qglEnable(GL_CULL_FACE);
2161 qglEnable(GL_DEPTH_TEST);
2163 qglEnable(GL_STENCIL_TEST);
2165 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2167 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);
2168 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);
2172 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2174 if (r_drawentities.integer)
2176 for (i = 0;i < r_refdef.numentities;i++)
2178 ent = r_refdef.entities[i];
2179 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
2180 && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2181 && (ent->flags & RENDER_LIGHT))
2183 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2184 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2185 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2186 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2187 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2188 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2195 void R_ShadowVolumeLighting(int visiblevolumes)
2203 memset(&m, 0, sizeof(m));
2206 GL_BlendFunc(GL_ONE, GL_ONE);
2207 GL_DepthMask(false);
2208 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2209 qglDisable(GL_CULL_FACE);
2210 GL_Color(0.0, 0.0125, 0.1, 1);
2213 R_Shadow_Stage_Begin();
2215 if (r_shadow_realtime_world.integer)
2217 R_Shadow_LoadWorldLightsIfNeeded();
2218 if (r_shadow_debuglight.integer >= 0)
2220 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2221 if (lnum == r_shadow_debuglight.integer)
2222 R_DrawRTLight(&light->rtlight, visiblevolumes);
2225 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2226 R_DrawRTLight(&light->rtlight, visiblevolumes);
2228 if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2229 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2230 R_DrawRTLight(&light->rtlight, visiblevolumes);
2234 qglEnable(GL_CULL_FACE);
2235 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2238 R_Shadow_Stage_End();
2241 cvar_t r_editlights = {0, "r_editlights", "0"};
2242 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2243 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2244 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2245 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2246 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2247 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2248 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2249 dlight_t *r_shadow_worldlightchain;
2250 dlight_t *r_shadow_selectedlight;
2251 vec3_t r_editlights_cursorlocation;
2253 typedef struct cubemapinfo_s
2256 rtexture_t *texture;
2260 #define MAX_CUBEMAPS 128
2261 static int numcubemaps;
2262 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2264 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2265 typedef struct suffixinfo_s
2268 int flipx, flipy, flipdiagonal;
2271 static suffixinfo_t suffix[3][6] =
2274 {"posx", false, false, false},
2275 {"negx", false, false, false},
2276 {"posy", false, false, false},
2277 {"negy", false, false, false},
2278 {"posz", false, false, false},
2279 {"negz", false, false, false}
2282 {"px", false, false, false},
2283 {"nx", false, false, false},
2284 {"py", false, false, false},
2285 {"ny", false, false, false},
2286 {"pz", false, false, false},
2287 {"nz", false, false, false}
2290 {"ft", true, false, true},
2291 {"bk", false, true, true},
2292 {"lf", true, true, false},
2293 {"rt", false, false, false},
2294 {"up", false, false, false},
2295 {"dn", false, false, false}
2299 static int componentorder[4] = {0, 1, 2, 3};
2301 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2303 int i, j, cubemapsize;
2304 qbyte *cubemappixels, *image_rgba;
2305 rtexture_t *cubemaptexture;
2307 // must start 0 so the first loadimagepixels has no requested width/height
2309 cubemappixels = NULL;
2310 cubemaptexture = NULL;
2311 for (j = 0;j < 3 && !cubemappixels;j++)
2313 for (i = 0;i < 6;i++)
2315 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2316 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2318 if (image_width == image_height)
2320 if (!cubemappixels && image_width >= 1)
2322 cubemapsize = image_width;
2323 // note this clears to black, so unavailable sizes are black
2324 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2327 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);
2330 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2331 Mem_Free(image_rgba);
2337 if (!r_shadow_filters_texturepool)
2338 r_shadow_filters_texturepool = R_AllocTexturePool();
2339 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2340 Mem_Free(cubemappixels);
2344 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2345 for (j = 0;j < 3;j++)
2346 for (i = 0;i < 6;i++)
2347 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2348 Con_Print(" and was unable to find any of them.\n");
2350 return cubemaptexture;
2353 rtexture_t *R_Shadow_Cubemap(const char *basename)
2356 for (i = 0;i < numcubemaps;i++)
2357 if (!strcasecmp(cubemaps[i].basename, basename))
2358 return cubemaps[i].texture;
2359 if (i >= MAX_CUBEMAPS)
2362 strcpy(cubemaps[i].basename, basename);
2363 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2364 return cubemaps[i].texture;
2367 void R_Shadow_FreeCubemaps(void)
2370 R_FreeTexturePool(&r_shadow_filters_texturepool);
2373 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)
2377 if (radius < 15 || DotProduct(color, color) < 0.03)
2379 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2383 light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2384 VectorCopy(origin, light->origin);
2385 VectorCopy(angles, light->angles);
2386 VectorCopy(color, light->color);
2387 light->radius = radius;
2388 light->style = style;
2389 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2391 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2394 light->shadow = shadowenable;
2395 light->corona = corona;
2396 if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2397 strcpy(light->cubemapname, cubemapname);
2398 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2399 light->next = r_shadow_worldlightchain;
2400 r_shadow_worldlightchain = light;
2402 R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2403 if (r_shadow_staticworldlights.integer)
2404 R_RTLight_Compile(&light->rtlight);
2407 void R_Shadow_FreeWorldLight(dlight_t *light)
2409 dlight_t **lightpointer;
2410 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2411 if (*lightpointer != light)
2412 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2413 *lightpointer = light->next;
2414 R_RTLight_Uncompile(&light->rtlight);
2418 void R_Shadow_ClearWorldLights(void)
2420 while (r_shadow_worldlightchain)
2421 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2422 r_shadow_selectedlight = NULL;
2423 R_Shadow_FreeCubemaps();
2426 void R_Shadow_SelectLight(dlight_t *light)
2428 if (r_shadow_selectedlight)
2429 r_shadow_selectedlight->selected = false;
2430 r_shadow_selectedlight = light;
2431 if (r_shadow_selectedlight)
2432 r_shadow_selectedlight->selected = true;
2435 rtexture_t *lighttextures[5];
2437 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2439 float scale = r_editlights_cursorgrid.value * 0.5f;
2440 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);
2443 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2446 const dlight_t *light;
2449 if (light->selected)
2450 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2453 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);
2456 void R_Shadow_DrawLightSprites(void)
2462 for (i = 0;i < 5;i++)
2464 lighttextures[i] = NULL;
2465 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2466 lighttextures[i] = pic->tex;
2469 for (light = r_shadow_worldlightchain;light;light = light->next)
2470 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2471 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2474 void R_Shadow_SelectLightInView(void)
2476 float bestrating, rating, temp[3];
2477 dlight_t *best, *light;
2480 for (light = r_shadow_worldlightchain;light;light = light->next)
2482 VectorSubtract(light->origin, r_vieworigin, temp);
2483 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2486 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2487 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2489 bestrating = rating;
2494 R_Shadow_SelectLight(best);
2497 void R_Shadow_LoadWorldLights(void)
2499 int n, a, style, shadow;
2500 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2501 float origin[3], radius, color[3], angles[3], corona;
2502 if (cl.worldmodel == NULL)
2504 Con_Print("No map loaded.\n");
2507 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2508 strlcat (name, ".rtlights", sizeof (name));
2509 lightsstring = FS_LoadFile(name, false);
2519 for (;COM_Parse(t, true) && strcmp(
2520 if (COM_Parse(t, true))
2522 if (com_token[0] == '!')
2525 origin[0] = atof(com_token+1);
2528 origin[0] = atof(com_token);
2533 while (*s && *s != '\n')
2539 // check for modifier flags
2545 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]);
2547 VectorClear(angles);
2550 if (a < 9 || !strcmp(cubemapname, "\"\""))
2555 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);
2558 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2559 radius *= r_editlights_rtlightssizescale.value;
2560 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2565 Con_Printf("invalid rtlights file \"%s\"\n", name);
2566 Mem_Free(lightsstring);
2570 void R_Shadow_SaveWorldLights(void)
2573 int bufchars, bufmaxchars;
2575 char name[MAX_QPATH];
2577 if (!r_shadow_worldlightchain)
2579 if (cl.worldmodel == NULL)
2581 Con_Print("No map loaded.\n");
2584 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2585 strlcat (name, ".rtlights", sizeof (name));
2586 bufchars = bufmaxchars = 0;
2588 for (light = r_shadow_worldlightchain;light;light = light->next)
2590 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]);
2591 if (bufchars + (int) strlen(line) > bufmaxchars)
2593 bufmaxchars = bufchars + strlen(line) + 2048;
2595 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2599 memcpy(buf, oldbuf, bufchars);
2605 memcpy(buf + bufchars, line, strlen(line));
2606 bufchars += strlen(line);
2610 FS_WriteFile(name, buf, bufchars);
2615 void R_Shadow_LoadLightsFile(void)
2618 char name[MAX_QPATH], *lightsstring, *s, *t;
2619 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2620 if (cl.worldmodel == NULL)
2622 Con_Print("No map loaded.\n");
2625 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2626 strlcat (name, ".lights", sizeof (name));
2627 lightsstring = FS_LoadFile(name, false);
2635 while (*s && *s != '\n')
2640 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);
2644 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);
2647 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2648 radius = bound(15, radius, 4096);
2649 VectorScale(color, (2.0f / (8388608.0f)), color);
2650 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2655 Con_Printf("invalid lights file \"%s\"\n", name);
2656 Mem_Free(lightsstring);
2660 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2662 int entnum, style, islight, skin, pflags, effects;
2663 char key[256], value[1024];
2664 float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2667 if (cl.worldmodel == NULL)
2669 Con_Print("No map loaded.\n");
2672 data = cl.worldmodel->brush.entities;
2675 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2678 origin[0] = origin[1] = origin[2] = 0;
2679 originhack[0] = originhack[1] = originhack[2] = 0;
2680 angles[0] = angles[1] = angles[2] = 0;
2681 color[0] = color[1] = color[2] = 1;
2682 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2692 if (!COM_ParseToken(&data, false))
2694 if (com_token[0] == '}')
2695 break; // end of entity
2696 if (com_token[0] == '_')
2697 strcpy(key, com_token + 1);
2699 strcpy(key, com_token);
2700 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2701 key[strlen(key)-1] = 0;
2702 if (!COM_ParseToken(&data, false))
2704 strcpy(value, com_token);
2706 // now that we have the key pair worked out...
2707 if (!strcmp("light", key))
2708 light = atof(value);
2709 else if (!strcmp("origin", key))
2710 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2711 else if (!strcmp("angle", key))
2712 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2713 else if (!strcmp("angles", key))
2714 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2715 else if (!strcmp("color", key))
2716 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2717 else if (!strcmp("wait", key))
2718 fadescale = atof(value);
2719 else if (!strcmp("classname", key))
2721 if (!strncmp(value, "light", 5))
2724 if (!strcmp(value, "light_fluoro"))
2729 overridecolor[0] = 1;
2730 overridecolor[1] = 1;
2731 overridecolor[2] = 1;
2733 if (!strcmp(value, "light_fluorospark"))
2738 overridecolor[0] = 1;
2739 overridecolor[1] = 1;
2740 overridecolor[2] = 1;
2742 if (!strcmp(value, "light_globe"))
2747 overridecolor[0] = 1;
2748 overridecolor[1] = 0.8;
2749 overridecolor[2] = 0.4;
2751 if (!strcmp(value, "light_flame_large_yellow"))
2756 overridecolor[0] = 1;
2757 overridecolor[1] = 0.5;
2758 overridecolor[2] = 0.1;
2760 if (!strcmp(value, "light_flame_small_yellow"))
2765 overridecolor[0] = 1;
2766 overridecolor[1] = 0.5;
2767 overridecolor[2] = 0.1;
2769 if (!strcmp(value, "light_torch_small_white"))
2774 overridecolor[0] = 1;
2775 overridecolor[1] = 0.5;
2776 overridecolor[2] = 0.1;
2778 if (!strcmp(value, "light_torch_small_walltorch"))
2783 overridecolor[0] = 1;
2784 overridecolor[1] = 0.5;
2785 overridecolor[2] = 0.1;
2789 else if (!strcmp("style", key))
2790 style = atoi(value);
2791 else if (cl.worldmodel->type == mod_brushq3)
2793 if (!strcmp("scale", key))
2794 lightscale = atof(value);
2795 if (!strcmp("fade", key))
2796 fadescale = atof(value);
2798 else if (!strcmp("skin", key))
2799 skin = (int)atof(value);
2800 else if (!strcmp("pflags", key))
2801 pflags = (int)atof(value);
2802 else if (!strcmp("effects", key))
2803 effects = (int)atof(value);
2805 if (light <= 0 && islight)
2807 if (lightscale <= 0)
2811 if (gamemode == GAME_TENEBRAE)
2813 if (effects & EF_NODRAW)
2815 pflags |= PFLAGS_FULLDYNAMIC;
2816 effects &= ~EF_NODRAW;
2819 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2820 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2821 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2822 VectorCopy(overridecolor, color);
2823 VectorScale(color, light, color);
2824 VectorAdd(origin, originhack, origin);
2825 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2826 R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2831 void R_Shadow_SetCursorLocationForView(void)
2833 vec_t dist, push, frac;
2834 vec3_t dest, endpos, normal;
2835 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2836 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2839 dist = frac * r_editlights_cursordistance.value;
2840 push = r_editlights_cursorpushback.value;
2844 VectorMA(endpos, push, r_viewforward, endpos);
2845 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2847 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2848 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2849 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2852 void R_Shadow_UpdateWorldLightSelection(void)
2854 if (r_editlights.integer)
2856 R_Shadow_SetCursorLocationForView();
2857 R_Shadow_SelectLightInView();
2858 R_Shadow_DrawLightSprites();
2861 R_Shadow_SelectLight(NULL);
2864 void R_Shadow_EditLights_Clear_f(void)
2866 R_Shadow_ClearWorldLights();
2869 void R_Shadow_EditLights_Reload_f(void)
2871 r_shadow_reloadlights = true;
2874 void R_Shadow_EditLights_Save_f(void)
2877 R_Shadow_SaveWorldLights();
2880 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2882 R_Shadow_ClearWorldLights();
2883 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2886 void R_Shadow_EditLights_ImportLightsFile_f(void)
2888 R_Shadow_ClearWorldLights();
2889 R_Shadow_LoadLightsFile();
2892 void R_Shadow_EditLights_Spawn_f(void)
2895 if (!r_editlights.integer)
2897 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2900 if (Cmd_Argc() != 1)
2902 Con_Print("r_editlights_spawn does not take parameters\n");
2905 color[0] = color[1] = color[2] = 1;
2906 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2909 void R_Shadow_EditLights_Edit_f(void)
2911 vec3_t origin, angles, color;
2912 vec_t radius, corona;
2914 char cubemapname[1024];
2915 if (!r_editlights.integer)
2917 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2920 if (!r_shadow_selectedlight)
2922 Con_Print("No selected light.\n");
2925 VectorCopy(r_shadow_selectedlight->origin, origin);
2926 VectorCopy(r_shadow_selectedlight->angles, angles);
2927 VectorCopy(r_shadow_selectedlight->color, color);
2928 radius = r_shadow_selectedlight->radius;
2929 style = r_shadow_selectedlight->style;
2930 if (r_shadow_selectedlight->cubemapname)
2931 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2934 shadows = r_shadow_selectedlight->shadow;
2935 corona = r_shadow_selectedlight->corona;
2936 if (!strcmp(Cmd_Argv(1), "origin"))
2938 if (Cmd_Argc() != 5)
2940 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2943 origin[0] = atof(Cmd_Argv(2));
2944 origin[1] = atof(Cmd_Argv(3));
2945 origin[2] = atof(Cmd_Argv(4));
2947 else if (!strcmp(Cmd_Argv(1), "originx"))
2949 if (Cmd_Argc() != 3)
2951 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2954 origin[0] = atof(Cmd_Argv(2));
2956 else if (!strcmp(Cmd_Argv(1), "originy"))
2958 if (Cmd_Argc() != 3)
2960 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2963 origin[1] = atof(Cmd_Argv(2));
2965 else if (!strcmp(Cmd_Argv(1), "originz"))
2967 if (Cmd_Argc() != 3)
2969 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2972 origin[2] = atof(Cmd_Argv(2));
2974 else if (!strcmp(Cmd_Argv(1), "move"))
2976 if (Cmd_Argc() != 5)
2978 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2981 origin[0] += atof(Cmd_Argv(2));
2982 origin[1] += atof(Cmd_Argv(3));
2983 origin[2] += atof(Cmd_Argv(4));
2985 else if (!strcmp(Cmd_Argv(1), "movex"))
2987 if (Cmd_Argc() != 3)
2989 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2992 origin[0] += atof(Cmd_Argv(2));
2994 else if (!strcmp(Cmd_Argv(1), "movey"))
2996 if (Cmd_Argc() != 3)
2998 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3001 origin[1] += atof(Cmd_Argv(2));
3003 else if (!strcmp(Cmd_Argv(1), "movez"))
3005 if (Cmd_Argc() != 3)
3007 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3010 origin[2] += atof(Cmd_Argv(2));
3012 else if (!strcmp(Cmd_Argv(1), "angles"))
3014 if (Cmd_Argc() != 5)
3016 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3019 angles[0] = atof(Cmd_Argv(2));
3020 angles[1] = atof(Cmd_Argv(3));
3021 angles[2] = atof(Cmd_Argv(4));
3023 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3025 if (Cmd_Argc() != 3)
3027 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3030 angles[0] = atof(Cmd_Argv(2));
3032 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3034 if (Cmd_Argc() != 3)
3036 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3039 angles[1] = atof(Cmd_Argv(2));
3041 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3043 if (Cmd_Argc() != 3)
3045 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3048 angles[2] = atof(Cmd_Argv(2));
3050 else if (!strcmp(Cmd_Argv(1), "color"))
3052 if (Cmd_Argc() != 5)
3054 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3057 color[0] = atof(Cmd_Argv(2));
3058 color[1] = atof(Cmd_Argv(3));
3059 color[2] = atof(Cmd_Argv(4));
3061 else if (!strcmp(Cmd_Argv(1), "radius"))
3063 if (Cmd_Argc() != 3)
3065 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3068 radius = atof(Cmd_Argv(2));
3070 else if (!strcmp(Cmd_Argv(1), "style"))
3072 if (Cmd_Argc() != 3)
3074 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3077 style = atoi(Cmd_Argv(2));
3079 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3083 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3086 if (Cmd_Argc() == 3)
3087 strcpy(cubemapname, Cmd_Argv(2));
3091 else if (!strcmp(Cmd_Argv(1), "shadows"))
3093 if (Cmd_Argc() != 3)
3095 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3098 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3100 else if (!strcmp(Cmd_Argv(1), "corona"))
3102 if (Cmd_Argc() != 3)
3104 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3107 corona = atof(Cmd_Argv(2));
3111 Con_Print("usage: r_editlights_edit [property] [value]\n");
3112 Con_Print("Selected light's properties:\n");
3113 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3114 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3115 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3116 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3117 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3118 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3119 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3120 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3123 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3124 r_shadow_selectedlight = NULL;
3125 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3128 extern int con_vislines;
3129 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3133 if (r_shadow_selectedlight == NULL)
3137 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3138 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;
3139 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;
3140 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;
3141 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3142 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3143 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3144 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;
3145 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3148 void R_Shadow_EditLights_ToggleShadow_f(void)
3150 if (!r_editlights.integer)
3152 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3155 if (!r_shadow_selectedlight)
3157 Con_Print("No selected light.\n");
3160 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);
3161 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3162 r_shadow_selectedlight = NULL;
3165 void R_Shadow_EditLights_ToggleCorona_f(void)
3167 if (!r_editlights.integer)
3169 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3172 if (!r_shadow_selectedlight)
3174 Con_Print("No selected light.\n");
3177 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);
3178 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3179 r_shadow_selectedlight = NULL;
3182 void R_Shadow_EditLights_Remove_f(void)
3184 if (!r_editlights.integer)
3186 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3189 if (!r_shadow_selectedlight)
3191 Con_Print("No selected light.\n");
3194 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3195 r_shadow_selectedlight = NULL;
3198 void R_Shadow_EditLights_Help_f(void)
3201 "Documentation on r_editlights system:\n"
3203 "r_editlights : enable/disable editing mode\n"
3204 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3205 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3206 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3207 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3208 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3209 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3210 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3212 "r_editlights_help : this help\n"
3213 "r_editlights_clear : remove all lights\n"
3214 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3215 "r_editlights_save : save to .rtlights file\n"
3216 "r_editlights_spawn : create a light with default settings\n"
3217 "r_editlights_edit command : edit selected light - more documentation below\n"
3218 "r_editlights_remove : remove selected light\n"
3219 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3220 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3221 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3223 "origin x y z : set light location\n"
3224 "originx x: set x component of light location\n"
3225 "originy y: set y component of light location\n"
3226 "originz z: set z component of light location\n"
3227 "move x y z : adjust light location\n"
3228 "movex x: adjust x component of light location\n"
3229 "movey y: adjust y component of light location\n"
3230 "movez z: adjust z component of light location\n"
3231 "angles x y z : set light angles\n"
3232 "anglesx x: set x component of light angles\n"
3233 "anglesy y: set y component of light angles\n"
3234 "anglesz z: set z component of light angles\n"
3235 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3236 "radius radius : set radius (size) of light\n"
3237 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3238 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3239 "shadows 1/0 : turn on/off shadows\n"
3240 "corona n : set corona intensity\n"
3241 "<nothing> : print light properties to console\n"
3245 void R_Shadow_EditLights_Init(void)
3247 Cvar_RegisterVariable(&r_editlights);
3248 Cvar_RegisterVariable(&r_editlights_cursordistance);
3249 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3250 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3251 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3252 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3253 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3254 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3255 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3256 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3257 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3258 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3259 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3260 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3261 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3262 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3263 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3264 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3265 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);