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_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
180 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
181 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
183 int c_rt_lights, c_rt_clears, c_rt_scissored;
184 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
185 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
187 void R_Shadow_ClearWorldLights(void);
188 void R_Shadow_SaveWorldLights(void);
189 void R_Shadow_LoadWorldLights(void);
190 void R_Shadow_LoadLightsFile(void);
191 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
193 void r_shadow_start(void)
195 // allocate vertex processing arrays
196 r_shadow_mempool = Mem_AllocPool("R_Shadow");
197 maxshadowelements = 0;
198 shadowelements = NULL;
206 shadowmarklist = NULL;
208 r_shadow_normalcubetexture = NULL;
209 r_shadow_attenuation2dtexture = NULL;
210 r_shadow_attenuation3dtexture = NULL;
211 r_shadow_blankbumptexture = NULL;
212 r_shadow_blankglosstexture = NULL;
213 r_shadow_blankwhitetexture = NULL;
214 r_shadow_texturepool = NULL;
215 r_shadow_filters_texturepool = NULL;
216 R_Shadow_ClearWorldLights();
217 r_shadow_reloadlights = true;
220 void r_shadow_shutdown(void)
222 R_Shadow_ClearWorldLights();
223 r_shadow_reloadlights = true;
224 r_shadow_normalcubetexture = NULL;
225 r_shadow_attenuation2dtexture = NULL;
226 r_shadow_attenuation3dtexture = NULL;
227 r_shadow_blankbumptexture = NULL;
228 r_shadow_blankglosstexture = NULL;
229 r_shadow_blankwhitetexture = NULL;
230 R_FreeTexturePool(&r_shadow_texturepool);
231 R_FreeTexturePool(&r_shadow_filters_texturepool);
232 maxshadowelements = 0;
233 shadowelements = NULL;
241 shadowmarklist = NULL;
243 Mem_FreePool(&r_shadow_mempool);
246 void r_shadow_newmap(void)
248 R_Shadow_ClearWorldLights();
249 r_shadow_reloadlights = true;
252 void R_Shadow_Help_f(void)
255 "Documentation on r_shadow system:\n"
257 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
258 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
259 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
260 "r_shadow_realtime_world : use realtime world light rendering\n"
261 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
262 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
263 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
264 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
265 "r_shadow_glossintensity : brightness of textured gloss\n"
266 "r_shadow_gloss2intensity : brightness of forced gloss\n"
267 "r_shadow_debuglight : render only this light number (-1 = all)\n"
268 "r_shadow_scissor : use scissor optimization\n"
269 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
270 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
271 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
272 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
273 "r_shadow_portallight : use portal visibility for static light precomputation\n"
274 "r_shadow_projectdistance : shadow volume projection distance\n"
275 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
276 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
277 "r_shadow_worldshadows : enable world shadows\n"
278 "r_shadow_dlightshadows : enable dlight shadows\n"
280 "r_shadow_help : this help\n"
284 void R_Shadow_Init(void)
286 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
287 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
288 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
289 Cvar_RegisterVariable(&r_shadow_realtime_world);
290 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
291 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
292 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
293 Cvar_RegisterVariable(&r_shadow_gloss);
294 Cvar_RegisterVariable(&r_shadow_glossintensity);
295 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
296 Cvar_RegisterVariable(&r_shadow_debuglight);
297 Cvar_RegisterVariable(&r_shadow_scissor);
298 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
299 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
300 Cvar_RegisterVariable(&r_shadow_polygonfactor);
301 Cvar_RegisterVariable(&r_shadow_polygonoffset);
302 Cvar_RegisterVariable(&r_shadow_portallight);
303 Cvar_RegisterVariable(&r_shadow_projectdistance);
304 Cvar_RegisterVariable(&r_shadow_texture3d);
305 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
306 Cvar_RegisterVariable(&r_shadow_worldshadows);
307 Cvar_RegisterVariable(&r_shadow_dlightshadows);
308 Cvar_RegisterVariable(&r_shadow_staticworldlights);
309 Cvar_RegisterVariable(&r_shadow_cull);
310 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
311 if (gamemode == GAME_TENEBRAE)
313 Cvar_SetValue("r_shadow_gloss", 2);
314 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
316 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
317 R_Shadow_EditLights_Init();
318 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
321 matrix4x4_t matrix_attenuationxyz =
324 {0.5, 0.0, 0.0, 0.5},
325 {0.0, 0.5, 0.0, 0.5},
326 {0.0, 0.0, 0.5, 0.5},
331 matrix4x4_t matrix_attenuationz =
334 {0.0, 0.0, 0.5, 0.5},
335 {0.0, 0.0, 0.0, 0.0},
336 {0.0, 0.0, 0.0, 0.0},
341 int *R_Shadow_ResizeShadowElements(int numtris)
343 // make sure shadowelements is big enough for this volume
344 if (maxshadowelements < numtris * 24)
346 maxshadowelements = numtris * 24;
348 Mem_Free(shadowelements);
349 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
351 return shadowelements;
354 void R_Shadow_PrepareShadowMark(int numtris)
356 // make sure shadowmark is big enough for this volume
357 if (maxshadowmark < numtris)
359 maxshadowmark = numtris;
361 Mem_Free(shadowmark);
363 Mem_Free(shadowmarklist);
364 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
365 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
369 // if shadowmarkcount wrapped we clear the array and adjust accordingly
370 if (shadowmarkcount == 0)
373 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
378 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)
380 int i, j, tris = 0, vr[3], t, outvertices = 0;
384 if (maxvertexupdate < innumvertices)
386 maxvertexupdate = innumvertices;
388 Mem_Free(vertexupdate);
390 Mem_Free(vertexremap);
391 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
392 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
396 if (vertexupdatenum == 0)
399 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
400 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
403 for (i = 0;i < numshadowmarktris;i++)
405 t = shadowmarktris[i];
406 shadowmark[t] = shadowmarkcount;
407 e = inelement3i + t * 3;
408 // make sure the vertices are created
409 for (j = 0;j < 3;j++)
411 if (vertexupdate[e[j]] != vertexupdatenum)
413 vertexupdate[e[j]] = vertexupdatenum;
414 vertexremap[e[j]] = outvertices;
415 VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
416 f = projectdistance / VectorLength(temp);
417 VectorCopy(invertex3f + e[j] * 3, outvertex3f);
418 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
423 // output the front and back triangles
424 outelement3i[0] = vertexremap[e[0]];
425 outelement3i[1] = vertexremap[e[1]];
426 outelement3i[2] = vertexremap[e[2]];
427 outelement3i[3] = vertexremap[e[2]] + 1;
428 outelement3i[4] = vertexremap[e[1]] + 1;
429 outelement3i[5] = vertexremap[e[0]] + 1;
434 for (i = 0;i < numshadowmarktris;i++)
436 t = shadowmarktris[i];
437 e = inelement3i + t * 3;
438 n = inneighbor3i + t * 3;
439 // output the sides (facing outward from this triangle)
440 if (shadowmark[n[0]] != shadowmarkcount)
442 vr[0] = vertexremap[e[0]];
443 vr[1] = vertexremap[e[1]];
444 outelement3i[0] = vr[1];
445 outelement3i[1] = vr[0];
446 outelement3i[2] = vr[0] + 1;
447 outelement3i[3] = vr[1];
448 outelement3i[4] = vr[0] + 1;
449 outelement3i[5] = vr[1] + 1;
453 if (shadowmark[n[1]] != shadowmarkcount)
455 vr[1] = vertexremap[e[1]];
456 vr[2] = vertexremap[e[2]];
457 outelement3i[0] = vr[2];
458 outelement3i[1] = vr[1];
459 outelement3i[2] = vr[1] + 1;
460 outelement3i[3] = vr[2];
461 outelement3i[4] = vr[1] + 1;
462 outelement3i[5] = vr[2] + 1;
466 if (shadowmark[n[2]] != shadowmarkcount)
468 vr[0] = vertexremap[e[0]];
469 vr[2] = vertexremap[e[2]];
470 outelement3i[0] = vr[0];
471 outelement3i[1] = vr[2];
472 outelement3i[2] = vr[2] + 1;
473 outelement3i[3] = vr[0];
474 outelement3i[4] = vr[2] + 1;
475 outelement3i[5] = vr[0] + 1;
481 *outnumvertices = outvertices;
485 float varray_vertex3f2[65536*3];
487 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)
490 if (projectdistance < 0.1)
492 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
495 if (!numverts || !nummarktris)
497 // make sure shadowelements is big enough for this volume
498 if (maxshadowelements < nummarktris * 24)
499 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
500 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
501 R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
504 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)
509 // check which triangles are facing the , and then output
510 // triangle elements and vertices... by clever use of elements we
511 // can construct the whole shadow from the unprojected vertices and
512 // the projected vertices
514 // identify lit faces within the bounding box
515 R_Shadow_PrepareShadowMark(numtris);
516 for (i = 0;i < numtris;i++)
518 v[0] = invertex3f + elements[i*3+0] * 3;
519 v[1] = invertex3f + elements[i*3+1] * 3;
520 v[2] = invertex3f + elements[i*3+2] * 3;
521 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])))
522 shadowmarklist[numshadowmark++] = i;
524 R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
527 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)
530 mins[0] = projectorigin[0] - radius;
531 mins[1] = projectorigin[1] - radius;
532 mins[2] = projectorigin[2] - radius;
533 maxs[0] = projectorigin[0] + radius;
534 maxs[1] = projectorigin[1] + radius;
535 maxs[2] = projectorigin[2] + radius;
536 R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
539 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
542 memset(&m, 0, sizeof(m));
543 m.pointer_vertex = vertex3f;
545 GL_LockArrays(0, numvertices);
546 if (r_shadowstage == SHADOWSTAGE_STENCIL)
548 // decrement stencil if frontface is behind depthbuffer
549 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
550 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
551 R_Mesh_Draw(numvertices, numtriangles, element3i);
553 c_rt_shadowtris += numtriangles;
554 // increment stencil if backface is behind depthbuffer
555 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
556 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
558 R_Mesh_Draw(numvertices, numtriangles, element3i);
560 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 GL_LockArrays(0, mesh->numverts);
574 if (r_shadowstage == SHADOWSTAGE_STENCIL)
576 // decrement stencil if frontface is behind depthbuffer
577 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
578 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
579 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
580 c_rtcached_shadowmeshes++;
581 c_rtcached_shadowtris += mesh->numtriangles;
582 // increment stencil if backface is behind depthbuffer
583 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
584 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
586 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
587 c_rtcached_shadowmeshes++;
588 c_rtcached_shadowtris += mesh->numtriangles;
593 float r_shadow_attenpower, r_shadow_attenscale;
594 static void R_Shadow_MakeTextures(void)
596 int x, y, z, d, side;
597 float v[3], s, t, intensity;
599 R_FreeTexturePool(&r_shadow_texturepool);
600 r_shadow_texturepool = R_AllocTexturePool();
601 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
602 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
604 #define ATTEN2DSIZE 64
605 #define ATTEN3DSIZE 32
606 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
611 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
616 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
621 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
622 if (gl_texturecubemap)
624 for (side = 0;side < 6;side++)
626 for (y = 0;y < NORMSIZE;y++)
628 for (x = 0;x < NORMSIZE;x++)
630 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
631 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
665 intensity = 127.0f / sqrt(DotProduct(v, v));
666 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
667 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
668 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
669 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
673 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
676 r_shadow_normalcubetexture = NULL;
677 for (y = 0;y < ATTEN2DSIZE;y++)
679 for (x = 0;x < ATTEN2DSIZE;x++)
681 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
682 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
684 intensity = 1.0f - sqrt(DotProduct(v, v));
686 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
687 d = bound(0, intensity, 255);
688 data[(y*ATTEN2DSIZE+x)*4+0] = d;
689 data[(y*ATTEN2DSIZE+x)*4+1] = d;
690 data[(y*ATTEN2DSIZE+x)*4+2] = d;
691 data[(y*ATTEN2DSIZE+x)*4+3] = d;
694 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
695 if (r_shadow_texture3d.integer)
697 for (z = 0;z < ATTEN3DSIZE;z++)
699 for (y = 0;y < ATTEN3DSIZE;y++)
701 for (x = 0;x < ATTEN3DSIZE;x++)
703 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
704 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
705 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
706 intensity = 1.0f - sqrt(DotProduct(v, v));
708 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
709 d = bound(0, intensity, 255);
710 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
711 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
712 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
713 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
717 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
722 void R_Shadow_Stage_Begin(void)
726 if (r_shadow_texture3d.integer && !gl_texture3d)
727 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
728 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
729 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
731 if (!r_shadow_attenuation2dtexture
732 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
733 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
734 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
735 R_Shadow_MakeTextures();
737 memset(&m, 0, sizeof(m));
738 GL_BlendFunc(GL_ONE, GL_ZERO);
742 GL_Color(0, 0, 0, 1);
743 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
744 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
745 r_shadowstage = SHADOWSTAGE_NONE;
747 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
748 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
749 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
752 void R_Shadow_LoadWorldLightsIfNeeded(void)
754 if (r_shadow_reloadlights && cl.worldmodel)
756 R_Shadow_ClearWorldLights();
757 r_shadow_reloadlights = false;
758 R_Shadow_LoadWorldLights();
759 if (r_shadow_worldlightchain == NULL)
761 R_Shadow_LoadLightsFile();
762 if (r_shadow_worldlightchain == NULL)
763 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
768 void R_Shadow_Stage_ShadowVolumes(void)
771 memset(&m, 0, sizeof(m));
773 GL_Color(1, 1, 1, 1);
774 GL_ColorMask(0, 0, 0, 0);
775 GL_BlendFunc(GL_ONE, GL_ZERO);
778 qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
779 //if (r_shadow_polygonoffset.value != 0)
781 // qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
782 // qglEnable(GL_POLYGON_OFFSET_FILL);
785 // qglDisable(GL_POLYGON_OFFSET_FILL);
786 qglDepthFunc(GL_LESS);
787 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
788 qglEnable(GL_STENCIL_TEST);
789 qglStencilFunc(GL_ALWAYS, 128, ~0);
790 if (gl_ext_stenciltwoside.integer)
792 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
793 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
794 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
796 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
797 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
799 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
803 r_shadowstage = SHADOWSTAGE_STENCIL;
805 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
807 GL_Clear(GL_STENCIL_BUFFER_BIT);
809 // LordHavoc note: many shadow volumes reside entirely inside the world
810 // (that is to say they are entirely bounded by their lit surfaces),
811 // which can be optimized by handling things as an inverted light volume,
812 // with the shadow boundaries of the world being simulated by an altered
813 // (129) bias to stencil clearing on such lights
814 // FIXME: generate inverted light volumes for use as shadow volumes and
815 // optimize for them as noted above
818 void R_Shadow_Stage_LightWithoutShadows(void)
821 memset(&m, 0, sizeof(m));
823 GL_BlendFunc(GL_ONE, GL_ONE);
826 qglPolygonOffset(0, 0);
827 //qglDisable(GL_POLYGON_OFFSET_FILL);
828 GL_Color(1, 1, 1, 1);
829 GL_ColorMask(1, 1, 1, 1);
830 qglDepthFunc(GL_EQUAL);
831 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
832 qglDisable(GL_STENCIL_TEST);
833 if (gl_support_stenciltwoside)
834 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
836 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
837 qglStencilFunc(GL_EQUAL, 128, 0xFF);
838 r_shadowstage = SHADOWSTAGE_LIGHT;
842 void R_Shadow_Stage_LightWithShadows(void)
845 memset(&m, 0, sizeof(m));
847 GL_BlendFunc(GL_ONE, GL_ONE);
850 qglPolygonOffset(0, 0);
851 //qglDisable(GL_POLYGON_OFFSET_FILL);
852 GL_Color(1, 1, 1, 1);
853 GL_ColorMask(1, 1, 1, 1);
854 qglDepthFunc(GL_EQUAL);
855 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
856 qglEnable(GL_STENCIL_TEST);
857 if (gl_support_stenciltwoside)
858 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
860 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
861 // only draw light where this geometry was already rendered AND the
862 // stencil is 128 (values other than this mean shadow)
863 qglStencilFunc(GL_EQUAL, 128, 0xFF);
864 r_shadowstage = SHADOWSTAGE_LIGHT;
868 void R_Shadow_Stage_End(void)
871 memset(&m, 0, sizeof(m));
873 GL_BlendFunc(GL_ONE, GL_ZERO);
876 qglPolygonOffset(0, 0);
877 //qglDisable(GL_POLYGON_OFFSET_FILL);
878 GL_Color(1, 1, 1, 1);
879 GL_ColorMask(1, 1, 1, 1);
880 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
881 qglDepthFunc(GL_LEQUAL);
882 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
883 qglDisable(GL_STENCIL_TEST);
884 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
885 if (gl_support_stenciltwoside)
886 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
888 qglStencilFunc(GL_ALWAYS, 128, 0xFF);
889 r_shadowstage = SHADOWSTAGE_NONE;
892 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
894 int i, ix1, iy1, ix2, iy2;
895 float x1, y1, x2, y2, x, y, f;
898 if (!r_shadow_scissor.integer)
900 // if view is inside the box, just say yes it's visible
901 // LordHavoc: for some odd reason scissor seems broken without stencil
902 // (?!? seems like a driver bug) so abort if gl_stencil is false
903 if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
905 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
908 for (i = 0;i < 3;i++)
910 if (r_viewforward[i] >= 0)
921 f = DotProduct(r_viewforward, r_vieworigin) + 1;
922 if (DotProduct(r_viewforward, v2) <= f)
924 // entirely behind nearclip plane
927 if (DotProduct(r_viewforward, v) >= f)
929 // entirely infront of nearclip plane
930 x1 = y1 = x2 = y2 = 0;
931 for (i = 0;i < 8;i++)
933 v[0] = (i & 1) ? mins[0] : maxs[0];
934 v[1] = (i & 2) ? mins[1] : maxs[1];
935 v[2] = (i & 4) ? mins[2] : maxs[2];
937 GL_TransformToScreen(v, v2);
938 //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]);
957 // clipped by nearclip plane
958 // this is nasty and crude...
959 // create viewspace bbox
960 for (i = 0;i < 8;i++)
962 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
963 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
964 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
965 v2[0] = -DotProduct(v, r_viewleft);
966 v2[1] = DotProduct(v, r_viewup);
967 v2[2] = DotProduct(v, r_viewforward);
970 if (smins[0] > v2[0]) smins[0] = v2[0];
971 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
972 if (smins[1] > v2[1]) smins[1] = v2[1];
973 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
974 if (smins[2] > v2[2]) smins[2] = v2[2];
975 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
979 smins[0] = smaxs[0] = v2[0];
980 smins[1] = smaxs[1] = v2[1];
981 smins[2] = smaxs[2] = v2[2];
984 // now we have a bbox in viewspace
985 // clip it to the view plane
988 // return true if that culled the box
989 if (smins[2] >= smaxs[2])
991 // ok some of it is infront of the view, transform each corner back to
992 // worldspace and then to screenspace and make screen rect
993 // initialize these variables just to avoid compiler warnings
994 x1 = y1 = x2 = y2 = 0;
995 for (i = 0;i < 8;i++)
997 v2[0] = (i & 1) ? smins[0] : smaxs[0];
998 v2[1] = (i & 2) ? smins[1] : smaxs[1];
999 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1000 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1001 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1002 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1004 GL_TransformToScreen(v, v2);
1005 //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]);
1022 // this code doesn't handle boxes with any points behind view properly
1023 x1 = 1000;x2 = -1000;
1024 y1 = 1000;y2 = -1000;
1025 for (i = 0;i < 8;i++)
1027 v[0] = (i & 1) ? mins[0] : maxs[0];
1028 v[1] = (i & 2) ? mins[1] : maxs[1];
1029 v[2] = (i & 4) ? mins[2] : maxs[2];
1031 GL_TransformToScreen(v, v2);
1032 //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]);
1050 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1051 if (ix1 < r_view_x) ix1 = r_view_x;
1052 if (iy1 < r_view_y) iy1 = r_view_y;
1053 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1054 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1055 if (ix2 <= ix1 || iy2 <= iy1)
1057 // set up the scissor rectangle
1058 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1059 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1060 //qglEnable(GL_SCISSOR_TEST);
1065 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1067 float *color4f = varray_color4f;
1068 float dist, dot, intensity, v[3], n[3];
1069 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1071 Matrix4x4_Transform(m, vertex3f, v);
1072 if ((dist = DotProduct(v, v)) < 1)
1074 Matrix4x4_Transform3x3(m, normal3f, n);
1075 if ((dot = DotProduct(n, v)) > 0)
1078 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1079 VectorScale(lightcolor, intensity, color4f);
1084 VectorClear(color4f);
1090 VectorClear(color4f);
1096 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1098 float *color4f = varray_color4f;
1099 float dist, dot, intensity, v[3], n[3];
1100 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1102 Matrix4x4_Transform(m, vertex3f, v);
1103 if ((dist = fabs(v[2])) < 1)
1105 Matrix4x4_Transform3x3(m, normal3f, n);
1106 if ((dot = DotProduct(n, v)) > 0)
1108 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1109 VectorScale(lightcolor, intensity, color4f);
1114 VectorClear(color4f);
1120 VectorClear(color4f);
1126 // FIXME: this should be done in a vertex program when possible
1127 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1128 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1132 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1133 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1134 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1141 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1145 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1146 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1153 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)
1157 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1159 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1160 // the cubemap normalizes this for us
1161 out3f[0] = DotProduct(svector3f, lightdir);
1162 out3f[1] = DotProduct(tvector3f, lightdir);
1163 out3f[2] = DotProduct(normal3f, lightdir);
1167 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)
1170 float lightdir[3], eyedir[3], halfdir[3];
1171 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1173 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1174 VectorNormalizeFast(lightdir);
1175 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1176 VectorNormalizeFast(eyedir);
1177 VectorAdd(lightdir, eyedir, halfdir);
1178 // the cubemap normalizes this for us
1179 out3f[0] = DotProduct(svector3f, halfdir);
1180 out3f[1] = DotProduct(tvector3f, halfdir);
1181 out3f[2] = DotProduct(normal3f, halfdir);
1185 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)
1188 float color[3], color2[3];
1190 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1193 bumptexture = r_shadow_blankbumptexture;
1195 // colorscale accounts for how much we multiply the brightness during combine
1196 // mult is how many times the final pass of the lighting will be
1197 // performed to get more brightness than otherwise possible
1198 // limit mult to 64 for sanity sake
1199 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1201 // 3/2 3D combine path (Geforce3, Radeon 8500)
1202 memset(&m, 0, sizeof(m));
1203 m.pointer_vertex = vertex3f;
1204 m.tex[0] = R_GetTexture(bumptexture);
1205 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1206 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1207 m.texcombinergb[0] = GL_REPLACE;
1208 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1209 m.pointer_texcoord[0] = texcoord2f;
1210 m.pointer_texcoord[1] = varray_texcoord3f[1];
1211 m.pointer_texcoord[2] = varray_texcoord3f[2];
1213 GL_ColorMask(0,0,0,1);
1214 GL_BlendFunc(GL_ONE, GL_ZERO);
1215 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1216 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1217 GL_LockArrays(0, numverts);
1218 R_Mesh_Draw(numverts, numtriangles, elements);
1219 GL_LockArrays(0, 0);
1221 c_rt_lighttris += numtriangles;
1223 memset(&m, 0, sizeof(m));
1224 m.pointer_vertex = vertex3f;
1225 m.tex[0] = R_GetTexture(basetexture);
1226 m.pointer_texcoord[0] = texcoord2f;
1229 m.texcubemap[1] = R_GetTexture(lightcubemap);
1230 m.pointer_texcoord[1] = varray_texcoord3f[1];
1231 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1234 GL_LockArrays(0, numverts);
1235 GL_ColorMask(1,1,1,0);
1236 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1237 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1238 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1240 color[0] = bound(0, color2[0], 1);
1241 color[1] = bound(0, color2[1], 1);
1242 color[2] = bound(0, color2[2], 1);
1243 GL_Color(color[0], color[1], color[2], 1);
1244 R_Mesh_Draw(numverts, numtriangles, elements);
1246 c_rt_lighttris += numtriangles;
1248 GL_LockArrays(0, 0);
1250 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1252 // 1/2/2 3D combine path (original Radeon)
1253 memset(&m, 0, sizeof(m));
1254 m.pointer_vertex = vertex3f;
1255 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1256 m.pointer_texcoord[0] = varray_texcoord3f[0];
1258 GL_ColorMask(0,0,0,1);
1259 GL_BlendFunc(GL_ONE, GL_ZERO);
1260 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1261 GL_LockArrays(0, numverts);
1262 R_Mesh_Draw(numverts, numtriangles, elements);
1263 GL_LockArrays(0, 0);
1265 c_rt_lighttris += numtriangles;
1267 memset(&m, 0, sizeof(m));
1268 m.pointer_vertex = vertex3f;
1269 m.tex[0] = R_GetTexture(bumptexture);
1270 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1271 m.texcombinergb[0] = GL_REPLACE;
1272 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1273 m.pointer_texcoord[0] = texcoord2f;
1274 m.pointer_texcoord[1] = varray_texcoord3f[1];
1276 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1277 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1278 GL_LockArrays(0, numverts);
1279 R_Mesh_Draw(numverts, numtriangles, elements);
1280 GL_LockArrays(0, 0);
1282 c_rt_lighttris += numtriangles;
1284 memset(&m, 0, sizeof(m));
1285 m.pointer_vertex = vertex3f;
1286 m.tex[0] = R_GetTexture(basetexture);
1287 m.pointer_texcoord[0] = texcoord2f;
1290 m.texcubemap[1] = R_GetTexture(lightcubemap);
1291 m.pointer_texcoord[1] = varray_texcoord3f[1];
1292 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1295 GL_LockArrays(0, numverts);
1296 GL_ColorMask(1,1,1,0);
1297 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1298 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1299 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1301 color[0] = bound(0, color2[0], 1);
1302 color[1] = bound(0, color2[1], 1);
1303 color[2] = bound(0, color2[2], 1);
1304 GL_Color(color[0], color[1], color[2], 1);
1305 R_Mesh_Draw(numverts, numtriangles, elements);
1307 c_rt_lighttris += numtriangles;
1309 GL_LockArrays(0, 0);
1311 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1313 // 2/2 3D combine path (original Radeon)
1314 memset(&m, 0, sizeof(m));
1315 m.pointer_vertex = vertex3f;
1316 m.tex[0] = R_GetTexture(bumptexture);
1317 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1318 m.texcombinergb[0] = GL_REPLACE;
1319 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1320 m.pointer_texcoord[0] = texcoord2f;
1321 m.pointer_texcoord[1] = varray_texcoord3f[1];
1323 GL_ColorMask(0,0,0,1);
1324 GL_BlendFunc(GL_ONE, GL_ZERO);
1325 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1326 GL_LockArrays(0, numverts);
1327 R_Mesh_Draw(numverts, numtriangles, elements);
1328 GL_LockArrays(0, 0);
1330 c_rt_lighttris += numtriangles;
1332 memset(&m, 0, sizeof(m));
1333 m.pointer_vertex = vertex3f;
1334 m.tex[0] = R_GetTexture(basetexture);
1335 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1336 m.pointer_texcoord[0] = texcoord2f;
1337 m.pointer_texcoord[1] = varray_texcoord3f[1];
1339 GL_LockArrays(0, numverts);
1340 GL_ColorMask(1,1,1,0);
1341 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1342 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1343 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1344 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1346 color[0] = bound(0, color2[0], 1);
1347 color[1] = bound(0, color2[1], 1);
1348 color[2] = bound(0, color2[2], 1);
1349 GL_Color(color[0], color[1], color[2], 1);
1350 R_Mesh_Draw(numverts, numtriangles, elements);
1352 c_rt_lighttris += numtriangles;
1354 GL_LockArrays(0, 0);
1356 else if (r_textureunits.integer >= 4)
1358 // 4/2 2D combine path (Geforce3, Radeon 8500)
1359 memset(&m, 0, sizeof(m));
1360 m.pointer_vertex = vertex3f;
1361 m.tex[0] = R_GetTexture(bumptexture);
1362 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1363 m.texcombinergb[0] = GL_REPLACE;
1364 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1365 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1366 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1367 m.pointer_texcoord[0] = texcoord2f;
1368 m.pointer_texcoord[1] = varray_texcoord3f[1];
1369 m.pointer_texcoord[2] = varray_texcoord2f[2];
1370 m.pointer_texcoord[3] = varray_texcoord2f[3];
1372 GL_ColorMask(0,0,0,1);
1373 GL_BlendFunc(GL_ONE, GL_ZERO);
1374 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1375 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1376 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1377 GL_LockArrays(0, numverts);
1378 R_Mesh_Draw(numverts, numtriangles, elements);
1379 GL_LockArrays(0, 0);
1381 c_rt_lighttris += numtriangles;
1383 memset(&m, 0, sizeof(m));
1384 m.pointer_vertex = vertex3f;
1385 m.tex[0] = R_GetTexture(basetexture);
1386 m.pointer_texcoord[0] = texcoord2f;
1389 m.texcubemap[1] = R_GetTexture(lightcubemap);
1390 m.pointer_texcoord[1] = varray_texcoord3f[1];
1391 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1394 GL_LockArrays(0, numverts);
1395 GL_ColorMask(1,1,1,0);
1396 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1397 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1398 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1400 color[0] = bound(0, color2[0], 1);
1401 color[1] = bound(0, color2[1], 1);
1402 color[2] = bound(0, color2[2], 1);
1403 GL_Color(color[0], color[1], color[2], 1);
1404 R_Mesh_Draw(numverts, numtriangles, elements);
1406 c_rt_lighttris += numtriangles;
1408 GL_LockArrays(0, 0);
1412 // 2/2/2 2D combine path (any dot3 card)
1413 memset(&m, 0, sizeof(m));
1414 m.pointer_vertex = vertex3f;
1415 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1416 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1417 m.pointer_texcoord[0] = varray_texcoord2f[0];
1418 m.pointer_texcoord[1] = varray_texcoord2f[1];
1420 GL_ColorMask(0,0,0,1);
1421 GL_BlendFunc(GL_ONE, GL_ZERO);
1422 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1423 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1424 GL_LockArrays(0, numverts);
1425 R_Mesh_Draw(numverts, numtriangles, elements);
1426 GL_LockArrays(0, 0);
1428 c_rt_lighttris += numtriangles;
1430 memset(&m, 0, sizeof(m));
1431 m.pointer_vertex = vertex3f;
1432 m.tex[0] = R_GetTexture(bumptexture);
1433 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1434 m.texcombinergb[0] = GL_REPLACE;
1435 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1436 m.pointer_texcoord[0] = texcoord2f;
1437 m.pointer_texcoord[1] = varray_texcoord3f[1];
1439 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1440 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1441 GL_LockArrays(0, numverts);
1442 R_Mesh_Draw(numverts, numtriangles, elements);
1443 GL_LockArrays(0, 0);
1445 c_rt_lighttris += numtriangles;
1447 memset(&m, 0, sizeof(m));
1448 m.pointer_vertex = vertex3f;
1449 m.tex[0] = R_GetTexture(basetexture);
1450 m.pointer_texcoord[0] = texcoord2f;
1453 m.texcubemap[1] = R_GetTexture(lightcubemap);
1454 m.pointer_texcoord[1] = varray_texcoord3f[1];
1455 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1458 GL_LockArrays(0, numverts);
1459 GL_ColorMask(1,1,1,0);
1460 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1461 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1462 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1464 color[0] = bound(0, color2[0], 1);
1465 color[1] = bound(0, color2[1], 1);
1466 color[2] = bound(0, color2[2], 1);
1467 GL_Color(color[0], color[1], color[2], 1);
1468 R_Mesh_Draw(numverts, numtriangles, elements);
1470 c_rt_lighttris += numtriangles;
1472 GL_LockArrays(0, 0);
1477 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1478 GL_DepthMask(false);
1480 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1481 memset(&m, 0, sizeof(m));
1482 m.pointer_vertex = vertex3f;
1483 m.pointer_color = varray_color4f;
1484 m.tex[0] = R_GetTexture(basetexture);
1485 m.pointer_texcoord[0] = texcoord2f;
1486 if (r_textureunits.integer >= 2)
1489 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1490 m.pointer_texcoord[1] = varray_texcoord2f[1];
1491 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1494 GL_LockArrays(0, numverts);
1495 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1497 color[0] = bound(0, color2[0], 1);
1498 color[1] = bound(0, color2[1], 1);
1499 color[2] = bound(0, color2[2], 1);
1500 if (r_textureunits.integer >= 2)
1501 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1503 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1504 R_Mesh_Draw(numverts, numtriangles, elements);
1506 c_rt_lighttris += numtriangles;
1508 GL_LockArrays(0, 0);
1512 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)
1515 float color[3], color2[3], colorscale;
1517 if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1520 glosstexture = r_shadow_blankglosstexture;
1521 if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1523 colorscale = r_shadow_glossintensity.value;
1525 bumptexture = r_shadow_blankbumptexture;
1526 if (glosstexture == r_shadow_blankglosstexture)
1527 colorscale *= r_shadow_gloss2intensity.value;
1529 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1531 // 2/0/0/1/2 3D combine blendsquare path
1532 memset(&m, 0, sizeof(m));
1533 m.pointer_vertex = vertex3f;
1534 m.tex[0] = R_GetTexture(bumptexture);
1535 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1536 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1537 m.pointer_texcoord[0] = texcoord2f;
1538 m.pointer_texcoord[1] = varray_texcoord3f[1];
1540 GL_ColorMask(0,0,0,1);
1541 // this squares the result
1542 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1543 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1544 GL_LockArrays(0, numverts);
1545 R_Mesh_Draw(numverts, numtriangles, elements);
1546 GL_LockArrays(0, 0);
1548 c_rt_lighttris += numtriangles;
1550 memset(&m, 0, sizeof(m));
1551 m.pointer_vertex = vertex3f;
1553 GL_LockArrays(0, numverts);
1554 // square alpha in framebuffer a few times to make it shiny
1555 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1556 // these comments are a test run through this math for intensity 0.5
1557 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1558 // 0.25 * 0.25 = 0.0625 (this is another pass)
1559 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1560 R_Mesh_Draw(numverts, numtriangles, elements);
1562 c_rt_lighttris += numtriangles;
1563 R_Mesh_Draw(numverts, numtriangles, elements);
1565 c_rt_lighttris += numtriangles;
1566 GL_LockArrays(0, 0);
1568 memset(&m, 0, sizeof(m));
1569 m.pointer_vertex = vertex3f;
1570 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1571 m.pointer_texcoord[0] = varray_texcoord3f[0];
1573 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1574 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1575 GL_LockArrays(0, numverts);
1576 R_Mesh_Draw(numverts, numtriangles, elements);
1577 GL_LockArrays(0, 0);
1579 c_rt_lighttris += numtriangles;
1581 memset(&m, 0, sizeof(m));
1582 m.pointer_vertex = vertex3f;
1583 m.tex[0] = R_GetTexture(glosstexture);
1586 m.texcubemap[1] = R_GetTexture(lightcubemap);
1587 m.pointer_texcoord[1] = varray_texcoord3f[1];
1588 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1590 m.pointer_texcoord[0] = texcoord2f;
1592 GL_LockArrays(0, numverts);
1593 GL_ColorMask(1,1,1,0);
1594 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1595 VectorScale(lightcolor, colorscale, color2);
1596 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1598 color[0] = bound(0, color2[0], 1);
1599 color[1] = bound(0, color2[1], 1);
1600 color[2] = bound(0, color2[2], 1);
1601 GL_Color(color[0], color[1], color[2], 1);
1602 R_Mesh_Draw(numverts, numtriangles, elements);
1604 c_rt_lighttris += numtriangles;
1606 GL_LockArrays(0, 0);
1608 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1610 // 2/0/0/2 3D combine blendsquare path
1611 memset(&m, 0, sizeof(m));
1612 m.pointer_vertex = vertex3f;
1613 m.tex[0] = R_GetTexture(bumptexture);
1614 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1615 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1616 m.pointer_texcoord[0] = texcoord2f;
1617 m.pointer_texcoord[1] = varray_texcoord3f[1];
1619 GL_ColorMask(0,0,0,1);
1620 // this squares the result
1621 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1622 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1623 GL_LockArrays(0, numverts);
1624 R_Mesh_Draw(numverts, numtriangles, elements);
1625 GL_LockArrays(0, 0);
1627 c_rt_lighttris += numtriangles;
1629 memset(&m, 0, sizeof(m));
1630 m.pointer_vertex = vertex3f;
1632 GL_LockArrays(0, numverts);
1633 // square alpha in framebuffer a few times to make it shiny
1634 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1635 // these comments are a test run through this math for intensity 0.5
1636 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1637 // 0.25 * 0.25 = 0.0625 (this is another pass)
1638 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1639 R_Mesh_Draw(numverts, numtriangles, elements);
1641 c_rt_lighttris += numtriangles;
1642 R_Mesh_Draw(numverts, numtriangles, elements);
1644 c_rt_lighttris += numtriangles;
1645 GL_LockArrays(0, 0);
1647 memset(&m, 0, sizeof(m));
1648 m.pointer_vertex = vertex3f;
1649 m.tex[0] = R_GetTexture(glosstexture);
1650 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1651 m.pointer_texcoord[0] = texcoord2f;
1652 m.pointer_texcoord[1] = varray_texcoord3f[1];
1654 GL_LockArrays(0, numverts);
1655 GL_ColorMask(1,1,1,0);
1656 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1657 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1658 VectorScale(lightcolor, colorscale, color2);
1659 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1661 color[0] = bound(0, color2[0], 1);
1662 color[1] = bound(0, color2[1], 1);
1663 color[2] = bound(0, color2[2], 1);
1664 GL_Color(color[0], color[1], color[2], 1);
1665 R_Mesh_Draw(numverts, numtriangles, elements);
1667 c_rt_lighttris += numtriangles;
1669 GL_LockArrays(0, 0);
1671 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1673 // 2/0/0/2/2 2D combine blendsquare path
1674 memset(&m, 0, sizeof(m));
1675 m.pointer_vertex = vertex3f;
1676 m.tex[0] = R_GetTexture(bumptexture);
1677 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1678 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1679 m.pointer_texcoord[0] = texcoord2f;
1680 m.pointer_texcoord[1] = varray_texcoord3f[1];
1682 GL_ColorMask(0,0,0,1);
1683 // this squares the result
1684 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1685 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1686 GL_LockArrays(0, numverts);
1687 R_Mesh_Draw(numverts, numtriangles, elements);
1688 GL_LockArrays(0, 0);
1690 c_rt_lighttris += numtriangles;
1692 memset(&m, 0, sizeof(m));
1693 m.pointer_vertex = vertex3f;
1695 GL_LockArrays(0, numverts);
1696 // square alpha in framebuffer a few times to make it shiny
1697 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1698 // these comments are a test run through this math for intensity 0.5
1699 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1700 // 0.25 * 0.25 = 0.0625 (this is another pass)
1701 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1702 R_Mesh_Draw(numverts, numtriangles, elements);
1704 c_rt_lighttris += numtriangles;
1705 R_Mesh_Draw(numverts, numtriangles, elements);
1707 c_rt_lighttris += numtriangles;
1708 GL_LockArrays(0, 0);
1710 memset(&m, 0, sizeof(m));
1711 m.pointer_vertex = vertex3f;
1712 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1713 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1714 m.pointer_texcoord[0] = varray_texcoord2f[0];
1715 m.pointer_texcoord[1] = varray_texcoord2f[1];
1717 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1718 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1719 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1720 GL_LockArrays(0, numverts);
1721 R_Mesh_Draw(numverts, numtriangles, elements);
1722 GL_LockArrays(0, 0);
1724 c_rt_lighttris += numtriangles;
1726 memset(&m, 0, sizeof(m));
1727 m.pointer_vertex = vertex3f;
1728 m.tex[0] = R_GetTexture(glosstexture);
1731 m.texcubemap[1] = R_GetTexture(lightcubemap);
1732 m.pointer_texcoord[1] = varray_texcoord3f[1];
1733 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1735 m.pointer_texcoord[0] = texcoord2f;
1737 GL_LockArrays(0, numverts);
1738 GL_ColorMask(1,1,1,0);
1739 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1740 VectorScale(lightcolor, colorscale, color2);
1741 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1743 color[0] = bound(0, color2[0], 1);
1744 color[1] = bound(0, color2[1], 1);
1745 color[2] = bound(0, color2[2], 1);
1746 GL_Color(color[0], color[1], color[2], 1);
1747 R_Mesh_Draw(numverts, numtriangles, elements);
1749 c_rt_lighttris += numtriangles;
1751 GL_LockArrays(0, 0);
1756 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1760 R_RTLight_Uncompile(rtlight);
1761 memset(rtlight, 0, sizeof(*rtlight));
1763 VectorCopy(light->origin, rtlight->shadoworigin);
1764 VectorCopy(light->color, rtlight->color);
1765 rtlight->radius = light->radius;
1766 rtlight->cullradius = rtlight->radius;
1767 rtlight->cullradius2 = rtlight->cullradius * rtlight->cullradius;
1768 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->cullradius;
1769 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->cullradius;
1770 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->cullradius;
1771 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->cullradius;
1772 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->cullradius;
1773 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->cullradius;
1774 rtlight->cubemapname[0] = 0;
1775 if (light->cubemapname[0])
1776 strcpy(rtlight->cubemapname, light->cubemapname);
1777 else if (light->cubemapnum > 0)
1778 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1779 rtlight->shadow = light->shadow;
1780 rtlight->corona = light->corona;
1781 rtlight->style = light->style;
1782 rtlight->isstatic = isstatic;
1783 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1784 // ConcatScale won't work here because this needs to scale rotate and
1785 // translate, not just rotate
1786 scale = 1.0f / rtlight->radius;
1787 for (k = 0;k < 3;k++)
1788 for (j = 0;j < 4;j++)
1789 rtlight->matrix_worldtolight.m[k][j] *= scale;
1790 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1791 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1793 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1794 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1795 VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1796 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1799 // compiles rtlight geometry
1800 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1801 void R_RTLight_Compile(rtlight_t *rtlight)
1803 int i, j, k, l, maxverts = 256, tris;
1804 float *vertex3f = NULL, mins[3], maxs[3];
1805 shadowmesh_t *mesh, *castmesh = NULL;
1807 qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1808 qbyte lightfullpvs[(MAX_MAP_LEAFS + 7)/ 8];
1810 // compile the light
1811 rtlight->compiled = true;
1812 VectorCopy(rtlight->cullmins, mins);
1813 VectorCopy(rtlight->cullmaxs, maxs);
1814 if (rtlight->shadow)
1815 castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1816 rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1819 lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, rtlight->shadoworigin, 0, lightfullpvs, sizeof(lightfullpvs));
1820 memset(lightpvs, 0, lightpvsbytes);
1821 if (cl.worldmodel->brushq3.num_leafs)
1826 // make a pvs that only includes things within the box
1827 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1828 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1829 SETPVSBIT(lightpvs, leaf->clusterindex);
1831 // make a cluster list for fast visibility checking during rendering
1832 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1833 if (CHECKPVSBIT(lightpvs, i))
1834 rtlight->static_numclusters++;
1835 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1836 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1837 if (CHECKPVSBIT(lightpvs, i))
1838 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1840 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1841 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1842 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1843 face->lighttemp_castshadow = false;
1844 for (i = 0, leaf = cl.worldmodel->brushq3.data_leafs;i < cl.worldmodel->brushq3.num_leafs;i++, leaf++)
1846 if (CHECKPVSBIT(lightpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1848 for (k = 0;k < 3;k++)
1850 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1851 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1853 for (j = 0;j < leaf->numleaffaces;j++)
1855 face = leaf->firstleafface[j];
1856 if (BoxesOverlap(face->mins, face->maxs, mins, maxs))
1857 face->lighttemp_castshadow = true;
1862 // add surfaces to shadow casting mesh and light mesh
1863 for (i = 0, face = cl.worldmodel->brushq3.data_thismodel->firstface;i < cl.worldmodel->brushq3.data_thismodel->numfaces;i++, face++)
1865 if (face->lighttemp_castshadow)
1867 face->lighttemp_castshadow = false;
1868 if (!(face->texture->surfaceflags & (Q3SURFACEFLAG_NODRAW | Q3SURFACEFLAG_SKY)))
1870 if (rtlight->shadow)
1871 if (!(face->texture->nativecontents & CONTENTSQ3_TRANSLUCENT))
1872 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, NULL, NULL, NULL, face->data_vertex3f, NULL, NULL, NULL, NULL, face->num_triangles, face->data_element3i);
1873 if (!(face->texture->surfaceflags & Q3SURFACEFLAG_SKY))
1874 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);
1879 else if (cl.worldmodel->brushq1.num_leafs)
1883 VectorCopy(rtlight->shadoworigin, rtlight->cullmins);
1884 VectorCopy(rtlight->shadoworigin, rtlight->cullmaxs);
1885 i = CL_PointQ1Contents(rtlight->shadoworigin);
1887 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1888 surf->lighttemp_castshadow = false;
1890 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1893 qbyte *bytesurfacepvs;
1895 byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.num_leafs);
1896 bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1898 Portal_Visibility(cl.worldmodel, rtlight->shadoworigin, byteleafpvs, bytesurfacepvs, NULL, 0, true, mins, maxs, rtlight->cullmins, rtlight->cullmaxs);
1900 // make a pvs that only includes things within the box
1901 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1903 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1905 SETPVSBIT(lightpvs, leaf->clusterindex);
1906 for (k = 0;k < 3;k++)
1908 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1909 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1914 for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1915 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1916 surf->lighttemp_castshadow = true;
1918 Mem_Free(byteleafpvs);
1919 Mem_Free(bytesurfacepvs);
1921 // make a cluster list for fast visibility checking during rendering
1922 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1923 if (CHECKPVSBIT(lightpvs, i))
1924 rtlight->static_numclusters++;
1925 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1926 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1927 if (CHECKPVSBIT(lightpvs, i))
1928 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1932 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1934 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1936 // make a pvs that only includes things within the box
1937 SETPVSBIT(lightpvs, leaf->clusterindex);
1938 for (k = 0;k < 3;k++)
1940 if (rtlight->cullmins[k] > leaf->mins[k]) rtlight->cullmins[k] = leaf->mins[k];
1941 if (rtlight->cullmaxs[k] < leaf->maxs[k]) rtlight->cullmaxs[k] = leaf->maxs[k];
1943 for (j = 0;j < leaf->nummarksurfaces;j++)
1945 surf = cl.worldmodel->brushq1.surfaces + leaf->firstmarksurface[j];
1946 if (!surf->lighttemp_castshadow && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1947 surf->lighttemp_castshadow = true;
1952 // make a pvs that only includes things within the box
1953 for (i = 0, leaf = cl.worldmodel->brushq1.data_leafs;i < cl.worldmodel->brushq1.num_leafs;i++, leaf++)
1954 if (CHECKPVSBIT(lightfullpvs, leaf->clusterindex) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1955 SETPVSBIT(lightpvs, leaf->clusterindex);
1957 // make a cluster list for fast visibility checking during rendering
1958 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1959 if (CHECKPVSBIT(lightpvs, i))
1960 rtlight->static_numclusters++;
1961 rtlight->static_clusterindices = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(int));
1962 for (i = 0, rtlight->static_numclusters = 0;i < cl.worldmodel->brush.num_pvsclusters;i++)
1963 if (CHECKPVSBIT(lightpvs, i))
1964 rtlight->static_clusterindices[rtlight->static_numclusters++] = i;
1967 // add surfaces to shadow casting mesh and light mesh
1968 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1970 if (surf->lighttemp_castshadow)
1972 surf->lighttemp_castshadow = false;
1973 if (rtlight->shadow && (surf->flags & SURF_SHADOWCAST))
1974 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);
1975 if (!(surf->flags & SURF_DRAWSKY))
1976 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);
1982 // limit box to light bounds (in case it grew larger)
1983 for (k = 0;k < 3;k++)
1985 if (rtlight->cullmins[k] < rtlight->shadoworigin[k] - rtlight->radius) rtlight->cullmins[k] = rtlight->shadoworigin[k] - rtlight->radius;
1986 if (rtlight->cullmaxs[k] > rtlight->shadoworigin[k] + rtlight->radius) rtlight->cullmaxs[k] = rtlight->shadoworigin[k] + rtlight->radius;
1988 rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1989 rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1991 // cast shadow volume from castmesh
1992 castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh, false, true);
1996 for (mesh = castmesh;mesh;mesh = mesh->next)
1998 R_Shadow_ResizeShadowElements(mesh->numtriangles);
1999 maxverts = max(maxverts, mesh->numverts * 2);
2004 vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2005 // now that we have the buffers big enough, construct and add
2006 // the shadow volume mesh
2007 if (rtlight->shadow)
2008 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2009 for (mesh = castmesh;mesh;mesh = mesh->next)
2011 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
2012 R_Shadow_PrepareShadowMark(mesh->numtriangles);
2013 for (i = 0;i < mesh->numtriangles;i++)
2016 v[0] = mesh->vertex3f + mesh->element3i[i*3+0] * 3;
2017 v[1] = mesh->vertex3f + mesh->element3i[i*3+1] * 3;
2018 v[2] = mesh->vertex3f + mesh->element3i[i*3+2] * 3;
2019 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])))
2020 shadowmarklist[numshadowmark++] = i;
2022 if (maxshadowelements < numshadowmark * 24)
2023 R_Shadow_ResizeShadowElements((numshadowmark + 256) * 24);
2024 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)))
2025 Mod_ShadowMesh_AddMesh(r_shadow_mempool, rtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, tris, shadowelements);
2030 // we're done with castmesh now
2031 Mod_ShadowMesh_Free(castmesh);
2034 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
2035 rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
2038 if (rtlight->static_meshchain_shadow)
2039 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2040 k += mesh->numtriangles;
2042 if (rtlight->static_meshchain_light)
2043 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2044 l += mesh->numtriangles;
2045 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);
2048 void R_RTLight_Uncompile(rtlight_t *rtlight)
2050 if (rtlight->compiled)
2052 if (rtlight->static_meshchain_shadow)
2053 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2054 rtlight->static_meshchain_shadow = NULL;
2055 if (rtlight->static_meshchain_light)
2056 Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2057 rtlight->static_meshchain_light = NULL;
2058 if (rtlight->static_clusterindices)
2059 Mem_Free(rtlight->static_clusterindices);
2060 rtlight->static_clusterindices = NULL;
2061 rtlight->static_numclusters = 0;
2062 rtlight->compiled = false;
2066 int shadowframecount = 0;
2068 void R_TestAndDrawShadowVolume(entity_render_t *ent, vec3_t shadoworigin, vec_t shadowradius, vec3_t cullmins, vec3_t cullmaxs)
2071 if ((BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) || !r_shadow_cull.integer) && (ent->flags & RENDER_SHADOW) && ent->model && ent->model->DrawShadowVolume)
2073 vec3_t relativeshadoworigin;
2074 Matrix4x4_Transform(&ent->inversematrix, shadoworigin, relativeshadoworigin);
2075 ent->model->DrawShadowVolume (ent, relativeshadoworigin, shadowradius);
2079 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2081 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2084 entity_render_t *ent;
2086 vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2087 rtexture_t *cubemaptexture;
2088 matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2090 if (d_lightstylevalue[rtlight->style] <= 0)
2092 if (rtlight->compiled)
2094 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2096 for (i = 0;i < rtlight->static_numclusters;i++)
2097 if (CHECKPVSBIT(r_pvsbits, rtlight->static_clusterindices[i]))
2099 if (i == rtlight->static_numclusters)
2102 else if (VIS_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2104 if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2107 if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2108 R_RTLight_Compile(rtlight);
2110 f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2111 VectorScale(rtlight->color, f, lightcolor);
2113 if (rtlight->selected)
2115 f = 2 + sin(realtime * M_PI * 4.0);
2116 VectorScale(lightcolor, f, lightcolor);
2120 if (rtlight->cubemapname[0])
2121 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2123 cubemaptexture = NULL;
2125 shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2126 if (shadow && (gl_stencil || visiblevolumes))
2128 if (!visiblevolumes)
2129 R_Shadow_Stage_ShadowVolumes();
2130 ent = &cl_entities[0].render;
2131 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2133 R_Mesh_Matrix(&ent->matrix);
2134 R_Shadow_RenderShadowMeshVolume(rtlight->static_meshchain_shadow);
2137 R_TestAndDrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2138 if (r_drawentities.integer)
2139 for (i = 0;i < r_refdef.numentities;i++)
2140 if (r_refdef.entities[i]->flags & RENDER_SHADOW)
2141 R_TestAndDrawShadowVolume(r_refdef.entities[i], rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs);
2144 if (!visiblevolumes)
2146 if (shadow && gl_stencil)
2147 R_Shadow_Stage_LightWithShadows();
2149 R_Shadow_Stage_LightWithoutShadows();
2151 ent = &cl_entities[0].render;
2152 if (ent->model && ent->model->DrawLight)
2154 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2155 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2156 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2157 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2158 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2159 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2161 //R_Shadow_DrawStaticWorldLight_Light(rtlight, &ent->matrix, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2163 R_Mesh_Matrix(&ent->matrix);
2164 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2166 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);
2167 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);
2171 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2173 if (r_drawentities.integer)
2175 for (i = 0;i < r_refdef.numentities;i++)
2177 ent = r_refdef.entities[i];
2178 if (ent->visframe == r_framecount && ent->model && ent->model->DrawLight
2179 && BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2180 && (ent->flags & RENDER_LIGHT))
2182 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2183 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2184 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2185 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2186 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2187 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture);
2194 void R_ShadowVolumeLighting(int visiblevolumes)
2202 memset(&m, 0, sizeof(m));
2205 GL_BlendFunc(GL_ONE, GL_ONE);
2206 GL_DepthMask(false);
2207 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2208 qglDisable(GL_CULL_FACE);
2209 GL_Color(0.0, 0.0125, 0.1, 1);
2212 R_Shadow_Stage_Begin();
2214 if (r_shadow_realtime_world.integer)
2216 R_Shadow_LoadWorldLightsIfNeeded();
2217 if (r_shadow_debuglight.integer >= 0)
2219 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2220 if (lnum == r_shadow_debuglight.integer)
2221 R_DrawRTLight(&light->rtlight, visiblevolumes);
2224 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2225 R_DrawRTLight(&light->rtlight, visiblevolumes);
2227 if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2228 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2229 R_DrawRTLight(&light->rtlight, visiblevolumes);
2233 qglEnable(GL_CULL_FACE);
2234 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2237 R_Shadow_Stage_End();
2240 cvar_t r_editlights = {0, "r_editlights", "0"};
2241 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2242 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2243 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2244 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2245 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2246 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2247 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2248 dlight_t *r_shadow_worldlightchain;
2249 dlight_t *r_shadow_selectedlight;
2250 vec3_t r_editlights_cursorlocation;
2252 typedef struct cubemapinfo_s
2255 rtexture_t *texture;
2259 #define MAX_CUBEMAPS 128
2260 static int numcubemaps;
2261 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2263 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2264 typedef struct suffixinfo_s
2267 int flipx, flipy, flipdiagonal;
2270 static suffixinfo_t suffix[3][6] =
2273 {"posx", false, false, false},
2274 {"negx", false, false, false},
2275 {"posy", false, false, false},
2276 {"negy", false, false, false},
2277 {"posz", false, false, false},
2278 {"negz", false, false, false}
2281 {"px", false, false, false},
2282 {"nx", false, false, false},
2283 {"py", false, false, false},
2284 {"ny", false, false, false},
2285 {"pz", false, false, false},
2286 {"nz", false, false, false}
2289 {"ft", true, false, true},
2290 {"bk", false, true, true},
2291 {"lf", true, true, false},
2292 {"rt", false, false, false},
2293 {"up", false, false, false},
2294 {"dn", false, false, false}
2298 static int componentorder[4] = {0, 1, 2, 3};
2300 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2302 int i, j, cubemapsize;
2303 qbyte *cubemappixels, *image_rgba;
2304 rtexture_t *cubemaptexture;
2306 // must start 0 so the first loadimagepixels has no requested width/height
2308 cubemappixels = NULL;
2309 cubemaptexture = NULL;
2310 for (j = 0;j < 3 && !cubemappixels;j++)
2312 for (i = 0;i < 6;i++)
2314 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2315 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2317 if (image_width == image_height)
2319 if (!cubemappixels && image_width >= 1)
2321 cubemapsize = image_width;
2322 // note this clears to black, so unavailable sizes are black
2323 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2326 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);
2329 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2330 Mem_Free(image_rgba);
2336 if (!r_shadow_filters_texturepool)
2337 r_shadow_filters_texturepool = R_AllocTexturePool();
2338 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2339 Mem_Free(cubemappixels);
2343 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2344 for (j = 0;j < 3;j++)
2345 for (i = 0;i < 6;i++)
2346 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2347 Con_Print(" and was unable to find any of them.\n");
2349 return cubemaptexture;
2352 rtexture_t *R_Shadow_Cubemap(const char *basename)
2355 for (i = 0;i < numcubemaps;i++)
2356 if (!strcasecmp(cubemaps[i].basename, basename))
2357 return cubemaps[i].texture;
2358 if (i >= MAX_CUBEMAPS)
2361 strcpy(cubemaps[i].basename, basename);
2362 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2363 return cubemaps[i].texture;
2366 void R_Shadow_FreeCubemaps(void)
2369 R_FreeTexturePool(&r_shadow_filters_texturepool);
2372 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)
2376 if (radius < 15 || DotProduct(color, color) < 0.03)
2378 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2382 light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2383 VectorCopy(origin, light->origin);
2384 VectorCopy(angles, light->angles);
2385 VectorCopy(color, light->color);
2386 light->radius = radius;
2387 light->style = style;
2388 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2390 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2393 light->shadow = shadowenable;
2394 light->corona = corona;
2395 if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2396 strcpy(light->cubemapname, cubemapname);
2397 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2398 light->next = r_shadow_worldlightchain;
2399 r_shadow_worldlightchain = light;
2401 R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2402 if (r_shadow_staticworldlights.integer)
2403 R_RTLight_Compile(&light->rtlight);
2406 void R_Shadow_FreeWorldLight(dlight_t *light)
2408 dlight_t **lightpointer;
2409 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2410 if (*lightpointer != light)
2411 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2412 *lightpointer = light->next;
2413 R_RTLight_Uncompile(&light->rtlight);
2417 void R_Shadow_ClearWorldLights(void)
2419 while (r_shadow_worldlightchain)
2420 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2421 r_shadow_selectedlight = NULL;
2422 R_Shadow_FreeCubemaps();
2425 void R_Shadow_SelectLight(dlight_t *light)
2427 if (r_shadow_selectedlight)
2428 r_shadow_selectedlight->selected = false;
2429 r_shadow_selectedlight = light;
2430 if (r_shadow_selectedlight)
2431 r_shadow_selectedlight->selected = true;
2434 rtexture_t *lighttextures[5];
2436 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2438 float scale = r_editlights_cursorgrid.value * 0.5f;
2439 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);
2442 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2445 const dlight_t *light;
2448 if (light->selected)
2449 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2452 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);
2455 void R_Shadow_DrawLightSprites(void)
2461 for (i = 0;i < 5;i++)
2463 lighttextures[i] = NULL;
2464 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2465 lighttextures[i] = pic->tex;
2468 for (light = r_shadow_worldlightchain;light;light = light->next)
2469 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2470 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2473 void R_Shadow_SelectLightInView(void)
2475 float bestrating, rating, temp[3];
2476 dlight_t *best, *light;
2479 for (light = r_shadow_worldlightchain;light;light = light->next)
2481 VectorSubtract(light->origin, r_vieworigin, temp);
2482 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2485 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2486 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2488 bestrating = rating;
2493 R_Shadow_SelectLight(best);
2496 void R_Shadow_LoadWorldLights(void)
2498 int n, a, style, shadow;
2499 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2500 float origin[3], radius, color[3], angles[3], corona;
2501 if (cl.worldmodel == NULL)
2503 Con_Print("No map loaded.\n");
2506 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2507 strlcat (name, ".rtlights", sizeof (name));
2508 lightsstring = FS_LoadFile(name, false);
2518 for (;COM_Parse(t, true) && strcmp(
2519 if (COM_Parse(t, true))
2521 if (com_token[0] == '!')
2524 origin[0] = atof(com_token+1);
2527 origin[0] = atof(com_token);
2532 while (*s && *s != '\n')
2538 // check for modifier flags
2544 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]);
2546 VectorClear(angles);
2549 if (a < 9 || !strcmp(cubemapname, "\"\""))
2554 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);
2557 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2558 radius *= r_editlights_rtlightssizescale.value;
2559 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2564 Con_Printf("invalid rtlights file \"%s\"\n", name);
2565 Mem_Free(lightsstring);
2569 void R_Shadow_SaveWorldLights(void)
2572 int bufchars, bufmaxchars;
2574 char name[MAX_QPATH];
2576 if (!r_shadow_worldlightchain)
2578 if (cl.worldmodel == NULL)
2580 Con_Print("No map loaded.\n");
2583 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2584 strlcat (name, ".rtlights", sizeof (name));
2585 bufchars = bufmaxchars = 0;
2587 for (light = r_shadow_worldlightchain;light;light = light->next)
2589 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]);
2590 if (bufchars + (int) strlen(line) > bufmaxchars)
2592 bufmaxchars = bufchars + strlen(line) + 2048;
2594 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2598 memcpy(buf, oldbuf, bufchars);
2604 memcpy(buf + bufchars, line, strlen(line));
2605 bufchars += strlen(line);
2609 FS_WriteFile(name, buf, bufchars);
2614 void R_Shadow_LoadLightsFile(void)
2617 char name[MAX_QPATH], *lightsstring, *s, *t;
2618 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2619 if (cl.worldmodel == NULL)
2621 Con_Print("No map loaded.\n");
2624 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2625 strlcat (name, ".lights", sizeof (name));
2626 lightsstring = FS_LoadFile(name, false);
2634 while (*s && *s != '\n')
2639 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);
2643 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);
2646 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2647 radius = bound(15, radius, 4096);
2648 VectorScale(color, (2.0f / (8388608.0f)), color);
2649 R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2654 Con_Printf("invalid lights file \"%s\"\n", name);
2655 Mem_Free(lightsstring);
2659 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2661 int entnum, style, islight, skin, pflags, effects;
2662 char key[256], value[1024];
2663 float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2666 if (cl.worldmodel == NULL)
2668 Con_Print("No map loaded.\n");
2671 data = cl.worldmodel->brush.entities;
2674 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2677 origin[0] = origin[1] = origin[2] = 0;
2678 originhack[0] = originhack[1] = originhack[2] = 0;
2679 angles[0] = angles[1] = angles[2] = 0;
2680 color[0] = color[1] = color[2] = 1;
2681 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2691 if (!COM_ParseToken(&data, false))
2693 if (com_token[0] == '}')
2694 break; // end of entity
2695 if (com_token[0] == '_')
2696 strcpy(key, com_token + 1);
2698 strcpy(key, com_token);
2699 while (key[strlen(key)-1] == ' ') // remove trailing spaces
2700 key[strlen(key)-1] = 0;
2701 if (!COM_ParseToken(&data, false))
2703 strcpy(value, com_token);
2705 // now that we have the key pair worked out...
2706 if (!strcmp("light", key))
2707 light = atof(value);
2708 else if (!strcmp("origin", key))
2709 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2710 else if (!strcmp("angle", key))
2711 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2712 else if (!strcmp("angles", key))
2713 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2714 else if (!strcmp("color", key))
2715 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2716 else if (!strcmp("wait", key))
2717 fadescale = atof(value);
2718 else if (!strcmp("classname", key))
2720 if (!strncmp(value, "light", 5))
2723 if (!strcmp(value, "light_fluoro"))
2728 overridecolor[0] = 1;
2729 overridecolor[1] = 1;
2730 overridecolor[2] = 1;
2732 if (!strcmp(value, "light_fluorospark"))
2737 overridecolor[0] = 1;
2738 overridecolor[1] = 1;
2739 overridecolor[2] = 1;
2741 if (!strcmp(value, "light_globe"))
2746 overridecolor[0] = 1;
2747 overridecolor[1] = 0.8;
2748 overridecolor[2] = 0.4;
2750 if (!strcmp(value, "light_flame_large_yellow"))
2755 overridecolor[0] = 1;
2756 overridecolor[1] = 0.5;
2757 overridecolor[2] = 0.1;
2759 if (!strcmp(value, "light_flame_small_yellow"))
2764 overridecolor[0] = 1;
2765 overridecolor[1] = 0.5;
2766 overridecolor[2] = 0.1;
2768 if (!strcmp(value, "light_torch_small_white"))
2773 overridecolor[0] = 1;
2774 overridecolor[1] = 0.5;
2775 overridecolor[2] = 0.1;
2777 if (!strcmp(value, "light_torch_small_walltorch"))
2782 overridecolor[0] = 1;
2783 overridecolor[1] = 0.5;
2784 overridecolor[2] = 0.1;
2788 else if (!strcmp("style", key))
2789 style = atoi(value);
2790 else if (cl.worldmodel->type == mod_brushq3)
2792 if (!strcmp("scale", key))
2793 lightscale = atof(value);
2794 if (!strcmp("fade", key))
2795 fadescale = atof(value);
2797 else if (!strcmp("skin", key))
2798 skin = (int)atof(value);
2799 else if (!strcmp("pflags", key))
2800 pflags = (int)atof(value);
2801 else if (!strcmp("effects", key))
2802 effects = (int)atof(value);
2804 if (light <= 0 && islight)
2806 if (lightscale <= 0)
2810 if (gamemode == GAME_TENEBRAE)
2812 if (effects & EF_NODRAW)
2814 pflags |= PFLAGS_FULLDYNAMIC;
2815 effects &= ~EF_NODRAW;
2818 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2819 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2820 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2821 VectorCopy(overridecolor, color);
2822 VectorScale(color, light, color);
2823 VectorAdd(origin, originhack, origin);
2824 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2825 R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2830 void R_Shadow_SetCursorLocationForView(void)
2832 vec_t dist, push, frac;
2833 vec3_t dest, endpos, normal;
2834 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2835 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2838 dist = frac * r_editlights_cursordistance.value;
2839 push = r_editlights_cursorpushback.value;
2843 VectorMA(endpos, push, r_viewforward, endpos);
2844 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2846 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2847 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2848 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2851 void R_Shadow_UpdateWorldLightSelection(void)
2853 if (r_editlights.integer)
2855 R_Shadow_SetCursorLocationForView();
2856 R_Shadow_SelectLightInView();
2857 R_Shadow_DrawLightSprites();
2860 R_Shadow_SelectLight(NULL);
2863 void R_Shadow_EditLights_Clear_f(void)
2865 R_Shadow_ClearWorldLights();
2868 void R_Shadow_EditLights_Reload_f(void)
2870 r_shadow_reloadlights = true;
2873 void R_Shadow_EditLights_Save_f(void)
2876 R_Shadow_SaveWorldLights();
2879 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2881 R_Shadow_ClearWorldLights();
2882 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2885 void R_Shadow_EditLights_ImportLightsFile_f(void)
2887 R_Shadow_ClearWorldLights();
2888 R_Shadow_LoadLightsFile();
2891 void R_Shadow_EditLights_Spawn_f(void)
2894 if (!r_editlights.integer)
2896 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2899 if (Cmd_Argc() != 1)
2901 Con_Print("r_editlights_spawn does not take parameters\n");
2904 color[0] = color[1] = color[2] = 1;
2905 R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2908 void R_Shadow_EditLights_Edit_f(void)
2910 vec3_t origin, angles, color;
2911 vec_t radius, corona;
2913 char cubemapname[1024];
2914 if (!r_editlights.integer)
2916 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
2919 if (!r_shadow_selectedlight)
2921 Con_Print("No selected light.\n");
2924 VectorCopy(r_shadow_selectedlight->origin, origin);
2925 VectorCopy(r_shadow_selectedlight->angles, angles);
2926 VectorCopy(r_shadow_selectedlight->color, color);
2927 radius = r_shadow_selectedlight->radius;
2928 style = r_shadow_selectedlight->style;
2929 if (r_shadow_selectedlight->cubemapname)
2930 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2933 shadows = r_shadow_selectedlight->shadow;
2934 corona = r_shadow_selectedlight->corona;
2935 if (!strcmp(Cmd_Argv(1), "origin"))
2937 if (Cmd_Argc() != 5)
2939 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2942 origin[0] = atof(Cmd_Argv(2));
2943 origin[1] = atof(Cmd_Argv(3));
2944 origin[2] = atof(Cmd_Argv(4));
2946 else if (!strcmp(Cmd_Argv(1), "originx"))
2948 if (Cmd_Argc() != 3)
2950 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2953 origin[0] = atof(Cmd_Argv(2));
2955 else if (!strcmp(Cmd_Argv(1), "originy"))
2957 if (Cmd_Argc() != 3)
2959 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2962 origin[1] = atof(Cmd_Argv(2));
2964 else if (!strcmp(Cmd_Argv(1), "originz"))
2966 if (Cmd_Argc() != 3)
2968 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2971 origin[2] = atof(Cmd_Argv(2));
2973 else if (!strcmp(Cmd_Argv(1), "move"))
2975 if (Cmd_Argc() != 5)
2977 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2980 origin[0] += atof(Cmd_Argv(2));
2981 origin[1] += atof(Cmd_Argv(3));
2982 origin[2] += atof(Cmd_Argv(4));
2984 else if (!strcmp(Cmd_Argv(1), "movex"))
2986 if (Cmd_Argc() != 3)
2988 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2991 origin[0] += atof(Cmd_Argv(2));
2993 else if (!strcmp(Cmd_Argv(1), "movey"))
2995 if (Cmd_Argc() != 3)
2997 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3000 origin[1] += atof(Cmd_Argv(2));
3002 else if (!strcmp(Cmd_Argv(1), "movez"))
3004 if (Cmd_Argc() != 3)
3006 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3009 origin[2] += atof(Cmd_Argv(2));
3011 else if (!strcmp(Cmd_Argv(1), "angles"))
3013 if (Cmd_Argc() != 5)
3015 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3018 angles[0] = atof(Cmd_Argv(2));
3019 angles[1] = atof(Cmd_Argv(3));
3020 angles[2] = atof(Cmd_Argv(4));
3022 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3024 if (Cmd_Argc() != 3)
3026 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3029 angles[0] = atof(Cmd_Argv(2));
3031 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3033 if (Cmd_Argc() != 3)
3035 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3038 angles[1] = atof(Cmd_Argv(2));
3040 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3042 if (Cmd_Argc() != 3)
3044 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3047 angles[2] = atof(Cmd_Argv(2));
3049 else if (!strcmp(Cmd_Argv(1), "color"))
3051 if (Cmd_Argc() != 5)
3053 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3056 color[0] = atof(Cmd_Argv(2));
3057 color[1] = atof(Cmd_Argv(3));
3058 color[2] = atof(Cmd_Argv(4));
3060 else if (!strcmp(Cmd_Argv(1), "radius"))
3062 if (Cmd_Argc() != 3)
3064 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3067 radius = atof(Cmd_Argv(2));
3069 else if (!strcmp(Cmd_Argv(1), "style"))
3071 if (Cmd_Argc() != 3)
3073 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3076 style = atoi(Cmd_Argv(2));
3078 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3082 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3085 if (Cmd_Argc() == 3)
3086 strcpy(cubemapname, Cmd_Argv(2));
3090 else if (!strcmp(Cmd_Argv(1), "shadows"))
3092 if (Cmd_Argc() != 3)
3094 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3097 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3099 else if (!strcmp(Cmd_Argv(1), "corona"))
3101 if (Cmd_Argc() != 3)
3103 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3106 corona = atof(Cmd_Argv(2));
3110 Con_Print("usage: r_editlights_edit [property] [value]\n");
3111 Con_Print("Selected light's properties:\n");
3112 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3113 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3114 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3115 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3116 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3117 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3118 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3119 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3122 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3123 r_shadow_selectedlight = NULL;
3124 R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3127 extern int con_vislines;
3128 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3132 if (r_shadow_selectedlight == NULL)
3136 sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3137 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;
3138 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;
3139 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;
3140 sprintf(temp, "Radius %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3141 sprintf(temp, "Corona %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3142 sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3143 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;
3144 sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3147 void R_Shadow_EditLights_ToggleShadow_f(void)
3149 if (!r_editlights.integer)
3151 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3154 if (!r_shadow_selectedlight)
3156 Con_Print("No selected light.\n");
3159 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);
3160 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3161 r_shadow_selectedlight = NULL;
3164 void R_Shadow_EditLights_ToggleCorona_f(void)
3166 if (!r_editlights.integer)
3168 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3171 if (!r_shadow_selectedlight)
3173 Con_Print("No selected light.\n");
3176 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);
3177 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3178 r_shadow_selectedlight = NULL;
3181 void R_Shadow_EditLights_Remove_f(void)
3183 if (!r_editlights.integer)
3185 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3188 if (!r_shadow_selectedlight)
3190 Con_Print("No selected light.\n");
3193 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3194 r_shadow_selectedlight = NULL;
3197 void R_Shadow_EditLights_Help_f(void)
3200 "Documentation on r_editlights system:\n"
3202 "r_editlights : enable/disable editing mode\n"
3203 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3204 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3205 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3206 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3207 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3208 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3209 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3211 "r_editlights_help : this help\n"
3212 "r_editlights_clear : remove all lights\n"
3213 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3214 "r_editlights_save : save to .rtlights file\n"
3215 "r_editlights_spawn : create a light with default settings\n"
3216 "r_editlights_edit command : edit selected light - more documentation below\n"
3217 "r_editlights_remove : remove selected light\n"
3218 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3219 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3220 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3222 "origin x y z : set light location\n"
3223 "originx x: set x component of light location\n"
3224 "originy y: set y component of light location\n"
3225 "originz z: set z component of light location\n"
3226 "move x y z : adjust light location\n"
3227 "movex x: adjust x component of light location\n"
3228 "movey y: adjust y component of light location\n"
3229 "movez z: adjust z component of light location\n"
3230 "angles x y z : set light angles\n"
3231 "anglesx x: set x component of light angles\n"
3232 "anglesy y: set y component of light angles\n"
3233 "anglesz z: set z component of light angles\n"
3234 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3235 "radius radius : set radius (size) of light\n"
3236 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3237 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3238 "shadows 1/0 : turn on/off shadows\n"
3239 "corona n : set corona intensity\n"
3240 "<nothing> : print light properties to console\n"
3244 void R_Shadow_EditLights_Init(void)
3246 Cvar_RegisterVariable(&r_editlights);
3247 Cvar_RegisterVariable(&r_editlights_cursordistance);
3248 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3249 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3250 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3251 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3252 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3253 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3254 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3255 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3256 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3257 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3258 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3259 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3260 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3261 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3262 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3263 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3264 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);