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;
129 mempool_t *r_shadow_mempool;
131 int maxshadowelements;
145 int r_shadow_buffer_numclusterpvsbytes;
146 qbyte *r_shadow_buffer_clusterpvs;
147 int *r_shadow_buffer_clusterlist;
149 int r_shadow_buffer_numsurfacepvsbytes;
150 qbyte *r_shadow_buffer_surfacepvs;
151 int *r_shadow_buffer_surfacelist;
153 rtexturepool_t *r_shadow_texturepool;
154 rtexture_t *r_shadow_normalcubetexture;
155 rtexture_t *r_shadow_attenuation2dtexture;
156 rtexture_t *r_shadow_attenuation3dtexture;
157 rtexture_t *r_shadow_blankbumptexture;
158 rtexture_t *r_shadow_blankglosstexture;
159 rtexture_t *r_shadow_blankwhitetexture;
161 // lights are reloaded when this changes
162 char r_shadow_mapname[MAX_QPATH];
164 // used only for light filters (cubemaps)
165 rtexturepool_t *r_shadow_filters_texturepool;
167 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
168 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
169 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
170 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
171 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1"};
172 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
173 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
174 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
175 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
176 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
177 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
178 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
179 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1"};
180 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "0"};
181 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0"};
182 cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1"};
183 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0"};
184 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1"};
185 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
186 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
187 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
188 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
189 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
190 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
191 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
192 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
193 cvar_t r_editlights = {0, "r_editlights", "0"};
194 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
195 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
196 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
197 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
198 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
199 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
200 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
202 int c_rt_lights, c_rt_clears, c_rt_scissored;
203 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
204 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
206 float r_shadow_attenpower, r_shadow_attenscale;
208 rtlight_t *r_shadow_compilingrtlight;
209 dlight_t *r_shadow_worldlightchain;
210 dlight_t *r_shadow_selectedlight;
211 dlight_t r_shadow_bufferlight;
212 vec3_t r_editlights_cursorlocation;
214 rtexture_t *lighttextures[5];
216 extern int con_vislines;
218 typedef struct cubemapinfo_s
225 #define MAX_CUBEMAPS 256
226 static int numcubemaps;
227 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
229 void R_Shadow_UncompileWorldLights(void);
230 void R_Shadow_ClearWorldLights(void);
231 void R_Shadow_SaveWorldLights(void);
232 void R_Shadow_LoadWorldLights(void);
233 void R_Shadow_LoadLightsFile(void);
234 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
235 void R_Shadow_EditLights_Reload_f(void);
236 void R_Shadow_ValidateCvars(void);
237 static void R_Shadow_MakeTextures(void);
238 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
240 void r_shadow_start(void)
242 // allocate vertex processing arrays
244 r_shadow_normalcubetexture = NULL;
245 r_shadow_attenuation2dtexture = NULL;
246 r_shadow_attenuation3dtexture = NULL;
247 r_shadow_blankbumptexture = NULL;
248 r_shadow_blankglosstexture = NULL;
249 r_shadow_blankwhitetexture = NULL;
250 r_shadow_texturepool = NULL;
251 r_shadow_filters_texturepool = NULL;
252 R_Shadow_ValidateCvars();
253 R_Shadow_MakeTextures();
254 maxshadowelements = 0;
255 shadowelements = NULL;
263 shadowmarklist = NULL;
265 r_shadow_buffer_numclusterpvsbytes = 0;
266 r_shadow_buffer_clusterpvs = NULL;
267 r_shadow_buffer_clusterlist = NULL;
268 r_shadow_buffer_numsurfacepvsbytes = 0;
269 r_shadow_buffer_surfacepvs = NULL;
270 r_shadow_buffer_surfacelist = NULL;
273 void r_shadow_shutdown(void)
275 R_Shadow_UncompileWorldLights();
277 r_shadow_normalcubetexture = NULL;
278 r_shadow_attenuation2dtexture = NULL;
279 r_shadow_attenuation3dtexture = NULL;
280 r_shadow_blankbumptexture = NULL;
281 r_shadow_blankglosstexture = NULL;
282 r_shadow_blankwhitetexture = NULL;
283 R_FreeTexturePool(&r_shadow_texturepool);
284 R_FreeTexturePool(&r_shadow_filters_texturepool);
285 maxshadowelements = 0;
287 Mem_Free(shadowelements);
288 shadowelements = NULL;
291 Mem_Free(vertexupdate);
294 Mem_Free(vertexremap);
300 Mem_Free(shadowmark);
303 Mem_Free(shadowmarklist);
304 shadowmarklist = NULL;
306 r_shadow_buffer_numclusterpvsbytes = 0;
307 if (r_shadow_buffer_clusterpvs)
308 Mem_Free(r_shadow_buffer_clusterpvs);
309 r_shadow_buffer_clusterpvs = NULL;
310 if (r_shadow_buffer_clusterlist)
311 Mem_Free(r_shadow_buffer_clusterlist);
312 r_shadow_buffer_clusterlist = NULL;
313 r_shadow_buffer_numsurfacepvsbytes = 0;
314 if (r_shadow_buffer_surfacepvs)
315 Mem_Free(r_shadow_buffer_surfacepvs);
316 r_shadow_buffer_surfacepvs = NULL;
317 if (r_shadow_buffer_surfacelist)
318 Mem_Free(r_shadow_buffer_surfacelist);
319 r_shadow_buffer_surfacelist = NULL;
322 void r_shadow_newmap(void)
326 void R_Shadow_Help_f(void)
329 "Documentation on r_shadow system:\n"
331 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
332 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
333 "r_shadow_debuglight : render only this light number (-1 = all)\n"
334 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
335 "r_shadow_gloss2intensity : brightness of forced gloss\n"
336 "r_shadow_glossintensity : brightness of textured gloss\n"
337 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
338 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
339 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
340 "r_shadow_portallight : use portal visibility for static light precomputation\n"
341 "r_shadow_projectdistance : shadow volume projection distance\n"
342 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
343 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
344 "r_shadow_realtime_world : use high quality world lighting mode\n"
345 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
346 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
347 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
348 "r_shadow_scissor : use scissor optimization\n"
349 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
350 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
351 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
352 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
353 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
355 "r_shadow_help : this help\n"
359 void R_Shadow_Init(void)
361 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
362 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
363 Cvar_RegisterVariable(&r_shadow_cull);
364 Cvar_RegisterVariable(&r_shadow_debuglight);
365 Cvar_RegisterVariable(&r_shadow_gloss);
366 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
367 Cvar_RegisterVariable(&r_shadow_glossintensity);
368 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
369 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
370 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
371 Cvar_RegisterVariable(&r_shadow_portallight);
372 Cvar_RegisterVariable(&r_shadow_projectdistance);
373 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
374 Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
375 Cvar_RegisterVariable(&r_shadow_realtime_world);
376 Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
377 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
378 Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
379 Cvar_RegisterVariable(&r_shadow_scissor);
380 Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
381 Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
382 Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
383 Cvar_RegisterVariable(&r_shadow_staticworldlights);
384 Cvar_RegisterVariable(&r_shadow_texture3d);
385 Cvar_RegisterVariable(&r_shadow_visiblevolumes);
386 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
387 if (gamemode == GAME_TENEBRAE)
389 Cvar_SetValue("r_shadow_gloss", 2);
390 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
392 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
393 R_Shadow_EditLights_Init();
394 r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
395 r_shadow_worldlightchain = NULL;
396 maxshadowelements = 0;
397 shadowelements = NULL;
405 shadowmarklist = NULL;
407 r_shadow_buffer_numclusterpvsbytes = 0;
408 r_shadow_buffer_clusterpvs = NULL;
409 r_shadow_buffer_clusterlist = NULL;
410 r_shadow_buffer_numsurfacepvsbytes = 0;
411 r_shadow_buffer_surfacepvs = NULL;
412 r_shadow_buffer_surfacelist = NULL;
413 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
416 matrix4x4_t matrix_attenuationxyz =
419 {0.5, 0.0, 0.0, 0.5},
420 {0.0, 0.5, 0.0, 0.5},
421 {0.0, 0.0, 0.5, 0.5},
426 matrix4x4_t matrix_attenuationz =
429 {0.0, 0.0, 0.5, 0.5},
430 {0.0, 0.0, 0.0, 0.5},
431 {0.0, 0.0, 0.0, 0.5},
436 int *R_Shadow_ResizeShadowElements(int numtris)
438 // make sure shadowelements is big enough for this volume
439 if (maxshadowelements < numtris * 24)
441 maxshadowelements = numtris * 24;
443 Mem_Free(shadowelements);
444 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
446 return shadowelements;
449 void R_Shadow_EnlargeClusterBuffer(int numclusters)
451 int numclusterpvsbytes = (((numclusters + 7) >> 3) + 255) & ~255;
452 if (r_shadow_buffer_numclusterpvsbytes < numclusterpvsbytes)
454 if (r_shadow_buffer_clusterpvs)
455 Mem_Free(r_shadow_buffer_clusterpvs);
456 if (r_shadow_buffer_clusterlist)
457 Mem_Free(r_shadow_buffer_clusterlist);
458 r_shadow_buffer_numclusterpvsbytes = numclusterpvsbytes;
459 r_shadow_buffer_clusterpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes);
460 r_shadow_buffer_clusterlist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes * 8 * sizeof(*r_shadow_buffer_clusterlist));
464 void R_Shadow_EnlargeSurfaceBuffer(int numsurfaces)
466 int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
467 if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
469 if (r_shadow_buffer_surfacepvs)
470 Mem_Free(r_shadow_buffer_surfacepvs);
471 if (r_shadow_buffer_surfacelist)
472 Mem_Free(r_shadow_buffer_surfacelist);
473 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
474 r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
475 r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
479 void R_Shadow_PrepareShadowMark(int numtris)
481 // make sure shadowmark is big enough for this volume
482 if (maxshadowmark < numtris)
484 maxshadowmark = numtris;
486 Mem_Free(shadowmark);
488 Mem_Free(shadowmarklist);
489 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
490 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
494 // if shadowmarkcount wrapped we clear the array and adjust accordingly
495 if (shadowmarkcount == 0)
498 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
503 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)
505 int i, j, tris = 0, vr[3], t, outvertices = 0;
510 if (maxvertexupdate < innumvertices)
512 maxvertexupdate = innumvertices;
514 Mem_Free(vertexupdate);
516 Mem_Free(vertexremap);
517 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
518 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
522 if (vertexupdatenum == 0)
525 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
526 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
529 for (i = 0;i < numshadowmarktris;i++)
530 shadowmark[shadowmarktris[i]] = shadowmarkcount;
532 for (i = 0;i < numshadowmarktris;i++)
534 t = shadowmarktris[i];
535 e = inelement3i + t * 3;
536 // make sure the vertices are created
537 for (j = 0;j < 3;j++)
539 if (vertexupdate[e[j]] != vertexupdatenum)
541 vertexupdate[e[j]] = vertexupdatenum;
542 vertexremap[e[j]] = outvertices;
543 v = invertex3f + e[j] * 3;
544 // project one copy of the vertex to the sphere radius of the light
545 // (FIXME: would projecting it to the light box be better?)
546 VectorSubtract(v, projectorigin, temp);
547 f = projectdistance / VectorLength(temp);
548 VectorCopy(v, outvertex3f);
549 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
556 for (i = 0;i < numshadowmarktris;i++)
558 t = shadowmarktris[i];
559 e = inelement3i + t * 3;
560 n = inneighbor3i + t * 3;
561 // output the front and back triangles
562 outelement3i[0] = vertexremap[e[0]];
563 outelement3i[1] = vertexremap[e[1]];
564 outelement3i[2] = vertexremap[e[2]];
565 outelement3i[3] = vertexremap[e[2]] + 1;
566 outelement3i[4] = vertexremap[e[1]] + 1;
567 outelement3i[5] = vertexremap[e[0]] + 1;
570 // output the sides (facing outward from this triangle)
571 if (shadowmark[n[0]] != shadowmarkcount)
573 vr[0] = vertexremap[e[0]];
574 vr[1] = vertexremap[e[1]];
575 outelement3i[0] = vr[1];
576 outelement3i[1] = vr[0];
577 outelement3i[2] = vr[0] + 1;
578 outelement3i[3] = vr[1];
579 outelement3i[4] = vr[0] + 1;
580 outelement3i[5] = vr[1] + 1;
584 if (shadowmark[n[1]] != shadowmarkcount)
586 vr[1] = vertexremap[e[1]];
587 vr[2] = vertexremap[e[2]];
588 outelement3i[0] = vr[2];
589 outelement3i[1] = vr[1];
590 outelement3i[2] = vr[1] + 1;
591 outelement3i[3] = vr[2];
592 outelement3i[4] = vr[1] + 1;
593 outelement3i[5] = vr[2] + 1;
597 if (shadowmark[n[2]] != shadowmarkcount)
599 vr[0] = vertexremap[e[0]];
600 vr[2] = vertexremap[e[2]];
601 outelement3i[0] = vr[0];
602 outelement3i[1] = vr[2];
603 outelement3i[2] = vr[2] + 1;
604 outelement3i[3] = vr[0];
605 outelement3i[4] = vr[2] + 1;
606 outelement3i[5] = vr[0] + 1;
612 *outnumvertices = outvertices;
616 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)
619 if (projectdistance < 0.1)
621 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
624 if (!numverts || !nummarktris)
626 // make sure shadowelements is big enough for this volume
627 if (maxshadowelements < nummarktris * 24)
628 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
629 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
630 R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
633 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, vec3_t lightmins, vec3_t lightmaxs, vec3_t surfacemins, vec3_t surfacemaxs)
638 if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
640 tend = firsttriangle + numtris;
641 if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
642 && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
643 && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
645 // surface box entirely inside light box, no box cull
646 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
647 if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
648 shadowmarklist[numshadowmark++] = t;
652 // surface box not entirely inside light box, cull each triangle
653 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
655 v[0] = invertex3f + e[0] * 3;
656 v[1] = invertex3f + e[1] * 3;
657 v[2] = invertex3f + e[2] * 3;
658 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
659 && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
660 && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
661 && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
662 && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
663 && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
664 && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
665 shadowmarklist[numshadowmark++] = t;
670 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
673 if (r_shadow_compilingrtlight)
675 // if we're compiling an rtlight, capture the mesh
676 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
679 memset(&m, 0, sizeof(m));
680 m.pointer_vertex = vertex3f;
682 GL_LockArrays(0, numvertices);
683 if (r_shadowstage == SHADOWSTAGE_STENCIL)
685 // increment stencil if backface is behind depthbuffer
686 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
687 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
688 R_Mesh_Draw(numvertices, numtriangles, element3i);
690 c_rt_shadowtris += numtriangles;
691 // decrement stencil if frontface is behind depthbuffer
692 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
693 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
695 R_Mesh_Draw(numvertices, numtriangles, element3i);
697 c_rt_shadowtris += numtriangles;
701 static void R_Shadow_MakeTextures(void)
703 int x, y, z, d, side;
704 float v[3], s, t, intensity;
706 R_FreeTexturePool(&r_shadow_texturepool);
707 r_shadow_texturepool = R_AllocTexturePool();
708 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
709 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
711 #define ATTEN2DSIZE 64
712 #define ATTEN3DSIZE 32
713 data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
718 r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
723 r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
728 r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
729 if (gl_texturecubemap)
731 for (side = 0;side < 6;side++)
733 for (y = 0;y < NORMSIZE;y++)
735 for (x = 0;x < NORMSIZE;x++)
737 s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
738 t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
772 intensity = 127.0f / sqrt(DotProduct(v, v));
773 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
774 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
775 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
776 data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
780 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
783 r_shadow_normalcubetexture = NULL;
784 for (y = 0;y < ATTEN2DSIZE;y++)
786 for (x = 0;x < ATTEN2DSIZE;x++)
788 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
789 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
791 intensity = 1.0f - sqrt(DotProduct(v, v));
793 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
794 d = bound(0, intensity, 255);
795 data[(y*ATTEN2DSIZE+x)*4+0] = d;
796 data[(y*ATTEN2DSIZE+x)*4+1] = d;
797 data[(y*ATTEN2DSIZE+x)*4+2] = d;
798 data[(y*ATTEN2DSIZE+x)*4+3] = d;
801 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
802 if (r_shadow_texture3d.integer)
804 for (z = 0;z < ATTEN3DSIZE;z++)
806 for (y = 0;y < ATTEN3DSIZE;y++)
808 for (x = 0;x < ATTEN3DSIZE;x++)
810 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
811 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
812 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
813 intensity = 1.0f - sqrt(DotProduct(v, v));
815 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
816 d = bound(0, intensity, 255);
817 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
818 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
819 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
820 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
824 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
829 void R_Shadow_ValidateCvars(void)
831 if (r_shadow_texture3d.integer && !gl_texture3d)
832 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
833 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
834 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
837 void R_Shadow_Stage_Begin(void)
841 R_Shadow_ValidateCvars();
843 if (!r_shadow_attenuation2dtexture
844 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
845 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
846 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
847 R_Shadow_MakeTextures();
849 memset(&m, 0, sizeof(m));
850 GL_BlendFunc(GL_ONE, GL_ZERO);
854 GL_Color(0, 0, 0, 1);
855 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
856 qglEnable(GL_CULL_FACE);
857 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
858 r_shadowstage = SHADOWSTAGE_NONE;
860 c_rt_lights = c_rt_clears = c_rt_scissored = 0;
861 c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
862 c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
865 void R_Shadow_Stage_ShadowVolumes(void)
868 memset(&m, 0, sizeof(m));
870 GL_Color(1, 1, 1, 1);
871 GL_ColorMask(0, 0, 0, 0);
872 GL_BlendFunc(GL_ONE, GL_ZERO);
875 qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
876 //if (r_shadow_shadow_polygonoffset.value != 0)
878 // qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
879 // qglEnable(GL_POLYGON_OFFSET_FILL);
882 // qglDisable(GL_POLYGON_OFFSET_FILL);
883 qglDepthFunc(GL_LESS);
884 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
885 qglEnable(GL_STENCIL_TEST);
886 qglStencilFunc(GL_ALWAYS, 128, ~0);
887 if (gl_ext_stenciltwoside.integer)
889 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
890 qglDisable(GL_CULL_FACE);
891 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
892 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
894 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
895 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
897 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
901 r_shadowstage = SHADOWSTAGE_STENCIL;
902 qglEnable(GL_CULL_FACE);
904 // this is changed by every shadow render so its value here is unimportant
905 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
907 GL_Clear(GL_STENCIL_BUFFER_BIT);
909 // LordHavoc note: many shadow volumes reside entirely inside the world
910 // (that is to say they are entirely bounded by their lit surfaces),
911 // which can be optimized by handling things as an inverted light volume,
912 // with the shadow boundaries of the world being simulated by an altered
913 // (129) bias to stencil clearing on such lights
914 // FIXME: generate inverted light volumes for use as shadow volumes and
915 // optimize for them as noted above
918 void R_Shadow_Stage_Light(int shadowtest)
921 memset(&m, 0, sizeof(m));
923 GL_BlendFunc(GL_ONE, GL_ONE);
926 qglPolygonOffset(0, 0);
927 //qglDisable(GL_POLYGON_OFFSET_FILL);
928 GL_Color(1, 1, 1, 1);
929 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
930 qglDepthFunc(GL_EQUAL);
931 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
932 qglEnable(GL_CULL_FACE);
934 qglEnable(GL_STENCIL_TEST);
936 qglDisable(GL_STENCIL_TEST);
937 if (gl_support_stenciltwoside)
938 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
940 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
941 // only draw light where this geometry was already rendered AND the
942 // stencil is 128 (values other than this mean shadow)
943 qglStencilFunc(GL_EQUAL, 128, ~0);
944 r_shadowstage = SHADOWSTAGE_LIGHT;
948 void R_Shadow_Stage_End(void)
951 memset(&m, 0, sizeof(m));
953 GL_BlendFunc(GL_ONE, GL_ZERO);
956 qglPolygonOffset(0, 0);
957 //qglDisable(GL_POLYGON_OFFSET_FILL);
958 GL_Color(1, 1, 1, 1);
959 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
960 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
961 qglDepthFunc(GL_LEQUAL);
962 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
963 qglDisable(GL_STENCIL_TEST);
964 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
965 if (gl_support_stenciltwoside)
966 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
968 qglStencilFunc(GL_ALWAYS, 128, ~0);
969 r_shadowstage = SHADOWSTAGE_NONE;
972 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
974 int i, ix1, iy1, ix2, iy2;
975 float x1, y1, x2, y2, x, y, f;
978 if (!r_shadow_scissor.integer)
980 // if view is inside the box, just say yes it's visible
981 if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
983 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
986 for (i = 0;i < 3;i++)
988 if (r_viewforward[i] >= 0)
999 f = DotProduct(r_viewforward, r_vieworigin) + 1;
1000 if (DotProduct(r_viewforward, v2) <= f)
1002 // entirely behind nearclip plane
1005 if (DotProduct(r_viewforward, v) >= f)
1007 // entirely infront of nearclip plane
1008 x1 = y1 = x2 = y2 = 0;
1009 for (i = 0;i < 8;i++)
1011 v[0] = (i & 1) ? mins[0] : maxs[0];
1012 v[1] = (i & 2) ? mins[1] : maxs[1];
1013 v[2] = (i & 4) ? mins[2] : maxs[2];
1015 GL_TransformToScreen(v, v2);
1016 //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]);
1035 // clipped by nearclip plane
1036 // this is nasty and crude...
1037 // create viewspace bbox
1038 for (i = 0;i < 8;i++)
1040 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
1041 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
1042 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
1043 v2[0] = -DotProduct(v, r_viewleft);
1044 v2[1] = DotProduct(v, r_viewup);
1045 v2[2] = DotProduct(v, r_viewforward);
1048 if (smins[0] > v2[0]) smins[0] = v2[0];
1049 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1050 if (smins[1] > v2[1]) smins[1] = v2[1];
1051 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1052 if (smins[2] > v2[2]) smins[2] = v2[2];
1053 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1057 smins[0] = smaxs[0] = v2[0];
1058 smins[1] = smaxs[1] = v2[1];
1059 smins[2] = smaxs[2] = v2[2];
1062 // now we have a bbox in viewspace
1063 // clip it to the view plane
1066 // return true if that culled the box
1067 if (smins[2] >= smaxs[2])
1069 // ok some of it is infront of the view, transform each corner back to
1070 // worldspace and then to screenspace and make screen rect
1071 // initialize these variables just to avoid compiler warnings
1072 x1 = y1 = x2 = y2 = 0;
1073 for (i = 0;i < 8;i++)
1075 v2[0] = (i & 1) ? smins[0] : smaxs[0];
1076 v2[1] = (i & 2) ? smins[1] : smaxs[1];
1077 v2[2] = (i & 4) ? smins[2] : smaxs[2];
1078 v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1079 v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1080 v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1082 GL_TransformToScreen(v, v2);
1083 //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]);
1100 // this code doesn't handle boxes with any points behind view properly
1101 x1 = 1000;x2 = -1000;
1102 y1 = 1000;y2 = -1000;
1103 for (i = 0;i < 8;i++)
1105 v[0] = (i & 1) ? mins[0] : maxs[0];
1106 v[1] = (i & 2) ? mins[1] : maxs[1];
1107 v[2] = (i & 4) ? mins[2] : maxs[2];
1109 GL_TransformToScreen(v, v2);
1110 //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]);
1128 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1129 if (ix1 < r_view_x) ix1 = r_view_x;
1130 if (iy1 < r_view_y) iy1 = r_view_y;
1131 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1132 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1133 if (ix2 <= ix1 || iy2 <= iy1)
1135 // set up the scissor rectangle
1136 GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1137 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1138 //qglEnable(GL_SCISSOR_TEST);
1143 static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1145 float *color4f = varray_color4f;
1146 float dist, dot, intensity, v[3], n[3];
1147 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1149 Matrix4x4_Transform(m, vertex3f, v);
1150 if ((dist = DotProduct(v, v)) < 1)
1152 Matrix4x4_Transform3x3(m, normal3f, n);
1153 if ((dot = DotProduct(n, v)) > 0)
1156 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1157 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1158 VectorScale(lightcolor, intensity, color4f);
1163 VectorClear(color4f);
1169 VectorClear(color4f);
1175 static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1177 float *color4f = varray_color4f;
1178 float dist, dot, intensity, v[3], n[3];
1179 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1181 Matrix4x4_Transform(m, vertex3f, v);
1182 if ((dist = fabs(v[2])) < 1)
1184 Matrix4x4_Transform3x3(m, normal3f, n);
1185 if ((dot = DotProduct(n, v)) > 0)
1187 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1188 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1189 VectorScale(lightcolor, intensity, color4f);
1194 VectorClear(color4f);
1200 VectorClear(color4f);
1206 static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1208 float *color4f = varray_color4f;
1209 float dot, intensity, v[3], n[3];
1210 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1212 Matrix4x4_Transform(m, vertex3f, v);
1213 Matrix4x4_Transform3x3(m, normal3f, n);
1214 if ((dot = DotProduct(n, v)) > 0)
1216 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1217 VectorScale(lightcolor, intensity, color4f);
1222 VectorClear(color4f);
1228 static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
1230 float *color4f = varray_color4f;
1231 float dist, intensity, v[3];
1232 for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1234 Matrix4x4_Transform(m, vertex3f, v);
1235 if ((dist = DotProduct(v, v)) < 1)
1238 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1239 VectorScale(lightcolor, intensity, color4f);
1244 VectorClear(color4f);
1250 static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor, const matrix4x4_t *m)
1252 float *color4f = varray_color4f;
1253 float dist, intensity, v[3];
1254 for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1256 Matrix4x4_Transform(m, vertex3f, v);
1257 if ((dist = fabs(v[2])) < 1)
1259 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1260 VectorScale(lightcolor, intensity, color4f);
1265 VectorClear(color4f);
1271 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1272 #define USETEXMATRIX
1274 #ifndef USETEXMATRIX
1275 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1276 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1277 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1281 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1282 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1283 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1290 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1294 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1295 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1303 static 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)
1307 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1309 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1310 // the cubemap normalizes this for us
1311 out3f[0] = DotProduct(svector3f, lightdir);
1312 out3f[1] = DotProduct(tvector3f, lightdir);
1313 out3f[2] = DotProduct(normal3f, lightdir);
1317 static 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)
1320 float lightdir[3], eyedir[3], halfdir[3];
1321 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1323 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1324 VectorNormalizeFast(lightdir);
1325 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1326 VectorNormalizeFast(eyedir);
1327 VectorAdd(lightdir, eyedir, halfdir);
1328 // the cubemap normalizes this for us
1329 out3f[0] = DotProduct(svector3f, halfdir);
1330 out3f[1] = DotProduct(tvector3f, halfdir);
1331 out3f[2] = DotProduct(normal3f, halfdir);
1335 void R_Shadow_RenderLighting(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 *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, vec_t ambientscale, vec_t diffusescale, vec_t specularscale)
1338 float color[3], color2[3], colorscale;
1341 bumptexture = r_shadow_blankbumptexture;
1343 glosstexture = r_shadow_blankglosstexture;
1344 // FIXME: support EF_NODEPTHTEST
1345 GL_DepthMask(false);
1347 if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1352 colorscale = r_shadow_lightintensityscale.value * ambientscale;
1353 // colorscale accounts for how much we multiply the brightness
1356 // mult is how many times the final pass of the lighting will be
1357 // performed to get more brightness than otherwise possible.
1359 // Limit mult to 64 for sanity sake.
1360 if (r_shadow_texture3d.integer && lightcubemap && r_textureunits.integer >= 4)
1362 // 3 3D combine path (Geforce3, Radeon 8500)
1363 memset(&m, 0, sizeof(m));
1364 m.pointer_vertex = vertex3f;
1365 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1367 m.pointer_texcoord3f[0] = vertex3f;
1368 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1370 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1371 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1373 m.tex[1] = R_GetTexture(basetexture);
1374 m.pointer_texcoord[1] = texcoord2f;
1375 m.texcubemap[2] = R_GetTexture(lightcubemap);
1377 m.pointer_texcoord3f[2] = vertex3f;
1378 m.texmatrix[2] = *matrix_modeltolight;
1380 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1381 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltolight);
1384 else if (r_shadow_texture3d.integer && !lightcubemap && r_textureunits.integer >= 2)
1386 // 2 3D combine path (Geforce3, original Radeon)
1387 memset(&m, 0, sizeof(m));
1388 m.pointer_vertex = vertex3f;
1389 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1391 m.pointer_texcoord3f[0] = vertex3f;
1392 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1394 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1395 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1397 m.tex[1] = R_GetTexture(basetexture);
1398 m.pointer_texcoord[1] = texcoord2f;
1400 else if (r_textureunits.integer >= 4 && lightcubemap)
1402 // 4 2D combine path (Geforce3, Radeon 8500)
1403 memset(&m, 0, sizeof(m));
1404 m.pointer_vertex = vertex3f;
1405 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1407 m.pointer_texcoord3f[0] = vertex3f;
1408 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1410 m.pointer_texcoord[0] = varray_texcoord2f[0];
1411 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1413 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1415 m.pointer_texcoord3f[1] = vertex3f;
1416 m.texmatrix[1] = *matrix_modeltoattenuationz;
1418 m.pointer_texcoord[1] = varray_texcoord2f[1];
1419 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1421 m.tex[2] = R_GetTexture(basetexture);
1422 m.pointer_texcoord[2] = texcoord2f;
1425 m.texcubemap[3] = R_GetTexture(lightcubemap);
1427 m.pointer_texcoord3f[3] = vertex3f;
1428 m.texmatrix[3] = *matrix_modeltolight;
1430 m.pointer_texcoord3f[3] = varray_texcoord3f[3];
1431 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3], numverts, vertex3f, matrix_modeltolight);
1435 else if (r_textureunits.integer >= 3 && !lightcubemap)
1437 // 3 2D combine path (Geforce3, original Radeon)
1438 memset(&m, 0, sizeof(m));
1439 m.pointer_vertex = vertex3f;
1440 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1442 m.pointer_texcoord3f[0] = vertex3f;
1443 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1445 m.pointer_texcoord[0] = varray_texcoord2f[0];
1446 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1448 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1450 m.pointer_texcoord3f[1] = vertex3f;
1451 m.texmatrix[1] = *matrix_modeltoattenuationz;
1453 m.pointer_texcoord[1] = varray_texcoord2f[1];
1454 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1456 m.tex[2] = R_GetTexture(basetexture);
1457 m.pointer_texcoord[2] = texcoord2f;
1461 // 2/2/2 2D combine path (any dot3 card)
1462 memset(&m, 0, sizeof(m));
1463 m.pointer_vertex = vertex3f;
1464 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1466 m.pointer_texcoord3f[0] = vertex3f;
1467 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1469 m.pointer_texcoord[0] = varray_texcoord2f[0];
1470 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1472 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1474 m.pointer_texcoord3f[1] = vertex3f;
1475 m.texmatrix[1] = *matrix_modeltoattenuationz;
1477 m.pointer_texcoord[1] = varray_texcoord2f[1];
1478 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1481 GL_ColorMask(0,0,0,1);
1482 GL_BlendFunc(GL_ONE, GL_ZERO);
1483 GL_LockArrays(0, numverts);
1484 R_Mesh_Draw(numverts, numtriangles, elements);
1485 GL_LockArrays(0, 0);
1487 c_rt_lighttris += numtriangles;
1489 memset(&m, 0, sizeof(m));
1490 m.pointer_vertex = vertex3f;
1491 m.tex[0] = R_GetTexture(basetexture);
1492 m.pointer_texcoord[0] = texcoord2f;
1495 m.texcubemap[1] = R_GetTexture(lightcubemap);
1497 m.pointer_texcoord3f[1] = vertex3f;
1498 m.texmatrix[1] = *matrix_modeltolight;
1500 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1501 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1505 // this final code is shared
1507 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1508 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1509 VectorScale(lightcolor, colorscale, color2);
1510 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1512 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1513 GL_LockArrays(0, numverts);
1514 R_Mesh_Draw(numverts, numtriangles, elements);
1515 GL_LockArrays(0, 0);
1517 c_rt_lighttris += numtriangles;
1523 colorscale = r_shadow_lightintensityscale.value * diffusescale;
1524 // colorscale accounts for how much we multiply the brightness
1527 // mult is how many times the final pass of the lighting will be
1528 // performed to get more brightness than otherwise possible.
1530 // Limit mult to 64 for sanity sake.
1531 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1533 // 3/2 3D combine path (Geforce3, Radeon 8500)
1534 memset(&m, 0, sizeof(m));
1535 m.pointer_vertex = vertex3f;
1536 m.tex[0] = R_GetTexture(bumptexture);
1537 m.texcombinergb[0] = GL_REPLACE;
1538 m.pointer_texcoord[0] = texcoord2f;
1539 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1540 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1541 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1542 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1543 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1545 m.pointer_texcoord3f[2] = vertex3f;
1546 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1548 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1549 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1552 GL_ColorMask(0,0,0,1);
1553 GL_BlendFunc(GL_ONE, GL_ZERO);
1554 GL_LockArrays(0, numverts);
1555 R_Mesh_Draw(numverts, numtriangles, elements);
1556 GL_LockArrays(0, 0);
1558 c_rt_lighttris += numtriangles;
1560 memset(&m, 0, sizeof(m));
1561 m.pointer_vertex = vertex3f;
1562 m.tex[0] = R_GetTexture(basetexture);
1563 m.pointer_texcoord[0] = texcoord2f;
1566 m.texcubemap[1] = R_GetTexture(lightcubemap);
1568 m.pointer_texcoord3f[1] = vertex3f;
1569 m.texmatrix[1] = *matrix_modeltolight;
1571 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1572 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1576 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1578 // 1/2/2 3D combine path (original Radeon)
1579 memset(&m, 0, sizeof(m));
1580 m.pointer_vertex = vertex3f;
1581 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1583 m.pointer_texcoord3f[0] = vertex3f;
1584 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1586 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1587 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1590 GL_ColorMask(0,0,0,1);
1591 GL_BlendFunc(GL_ONE, GL_ZERO);
1592 GL_LockArrays(0, numverts);
1593 R_Mesh_Draw(numverts, numtriangles, elements);
1594 GL_LockArrays(0, 0);
1596 c_rt_lighttris += numtriangles;
1598 memset(&m, 0, sizeof(m));
1599 m.pointer_vertex = vertex3f;
1600 m.tex[0] = R_GetTexture(bumptexture);
1601 m.texcombinergb[0] = GL_REPLACE;
1602 m.pointer_texcoord[0] = texcoord2f;
1603 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1604 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1605 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1606 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1608 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1609 GL_LockArrays(0, numverts);
1610 R_Mesh_Draw(numverts, numtriangles, elements);
1611 GL_LockArrays(0, 0);
1613 c_rt_lighttris += numtriangles;
1615 memset(&m, 0, sizeof(m));
1616 m.pointer_vertex = vertex3f;
1617 m.tex[0] = R_GetTexture(basetexture);
1618 m.pointer_texcoord[0] = texcoord2f;
1621 m.texcubemap[1] = R_GetTexture(lightcubemap);
1623 m.pointer_texcoord3f[1] = vertex3f;
1624 m.texmatrix[1] = *matrix_modeltolight;
1626 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1627 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1631 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1633 // 2/2 3D combine path (original Radeon)
1634 memset(&m, 0, sizeof(m));
1635 m.pointer_vertex = vertex3f;
1636 m.tex[0] = R_GetTexture(bumptexture);
1637 m.texcombinergb[0] = GL_REPLACE;
1638 m.pointer_texcoord[0] = texcoord2f;
1639 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1640 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1641 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1642 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1644 GL_ColorMask(0,0,0,1);
1645 GL_BlendFunc(GL_ONE, GL_ZERO);
1646 GL_LockArrays(0, numverts);
1647 R_Mesh_Draw(numverts, numtriangles, elements);
1648 GL_LockArrays(0, 0);
1650 c_rt_lighttris += numtriangles;
1652 memset(&m, 0, sizeof(m));
1653 m.pointer_vertex = vertex3f;
1654 m.tex[0] = R_GetTexture(basetexture);
1655 m.pointer_texcoord[0] = texcoord2f;
1656 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1658 m.pointer_texcoord3f[1] = vertex3f;
1659 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1661 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1662 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1665 else if (r_textureunits.integer >= 4)
1667 // 4/2 2D combine path (Geforce3, Radeon 8500)
1668 memset(&m, 0, sizeof(m));
1669 m.pointer_vertex = vertex3f;
1670 m.tex[0] = R_GetTexture(bumptexture);
1671 m.texcombinergb[0] = GL_REPLACE;
1672 m.pointer_texcoord[0] = texcoord2f;
1673 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1674 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1675 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1676 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1677 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1679 m.pointer_texcoord3f[2] = vertex3f;
1680 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1682 m.pointer_texcoord[2] = varray_texcoord2f[2];
1683 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1685 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1687 m.pointer_texcoord3f[3] = vertex3f;
1688 m.texmatrix[3] = *matrix_modeltoattenuationz;
1690 m.pointer_texcoord[3] = varray_texcoord2f[3];
1691 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1694 GL_ColorMask(0,0,0,1);
1695 GL_BlendFunc(GL_ONE, GL_ZERO);
1696 GL_LockArrays(0, numverts);
1697 R_Mesh_Draw(numverts, numtriangles, elements);
1698 GL_LockArrays(0, 0);
1700 c_rt_lighttris += numtriangles;
1702 memset(&m, 0, sizeof(m));
1703 m.pointer_vertex = vertex3f;
1704 m.tex[0] = R_GetTexture(basetexture);
1705 m.pointer_texcoord[0] = texcoord2f;
1708 m.texcubemap[1] = R_GetTexture(lightcubemap);
1710 m.pointer_texcoord3f[1] = vertex3f;
1711 m.texmatrix[1] = *matrix_modeltolight;
1713 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1714 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1720 // 2/2/2 2D combine path (any dot3 card)
1721 memset(&m, 0, sizeof(m));
1722 m.pointer_vertex = vertex3f;
1723 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1725 m.pointer_texcoord3f[0] = vertex3f;
1726 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1728 m.pointer_texcoord[0] = varray_texcoord2f[0];
1729 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1731 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1733 m.pointer_texcoord3f[1] = vertex3f;
1734 m.texmatrix[1] = *matrix_modeltoattenuationz;
1736 m.pointer_texcoord[1] = varray_texcoord2f[1];
1737 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1740 GL_ColorMask(0,0,0,1);
1741 GL_BlendFunc(GL_ONE, GL_ZERO);
1742 GL_LockArrays(0, numverts);
1743 R_Mesh_Draw(numverts, numtriangles, elements);
1744 GL_LockArrays(0, 0);
1746 c_rt_lighttris += numtriangles;
1748 memset(&m, 0, sizeof(m));
1749 m.pointer_vertex = vertex3f;
1750 m.tex[0] = R_GetTexture(bumptexture);
1751 m.texcombinergb[0] = GL_REPLACE;
1752 m.pointer_texcoord[0] = texcoord2f;
1753 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1754 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1755 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1756 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1758 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1759 GL_LockArrays(0, numverts);
1760 R_Mesh_Draw(numverts, numtriangles, elements);
1761 GL_LockArrays(0, 0);
1763 c_rt_lighttris += numtriangles;
1765 memset(&m, 0, sizeof(m));
1766 m.pointer_vertex = vertex3f;
1767 m.tex[0] = R_GetTexture(basetexture);
1768 m.pointer_texcoord[0] = texcoord2f;
1771 m.texcubemap[1] = R_GetTexture(lightcubemap);
1773 m.pointer_texcoord3f[1] = vertex3f;
1774 m.texmatrix[1] = *matrix_modeltolight;
1776 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1777 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1781 // this final code is shared
1783 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1784 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1785 VectorScale(lightcolor, colorscale, color2);
1786 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1788 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1789 GL_LockArrays(0, numverts);
1790 R_Mesh_Draw(numverts, numtriangles, elements);
1791 GL_LockArrays(0, 0);
1793 c_rt_lighttris += numtriangles;
1796 if (specularscale && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1798 // FIXME: detect blendsquare!
1799 //if (gl_support_blendsquare)
1801 colorscale = r_shadow_lightintensityscale.value * r_shadow_glossintensity.value * specularscale;
1802 if (glosstexture == r_shadow_blankglosstexture)
1803 colorscale *= r_shadow_gloss2intensity.value;
1805 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1807 // 2/0/0/1/2 3D combine blendsquare path
1808 memset(&m, 0, sizeof(m));
1809 m.pointer_vertex = vertex3f;
1810 m.tex[0] = R_GetTexture(bumptexture);
1811 m.pointer_texcoord[0] = texcoord2f;
1812 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1813 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1814 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1815 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1817 GL_ColorMask(0,0,0,1);
1818 // this squares the result
1819 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1820 GL_LockArrays(0, numverts);
1821 R_Mesh_Draw(numverts, numtriangles, elements);
1822 GL_LockArrays(0, 0);
1824 c_rt_lighttris += numtriangles;
1826 memset(&m, 0, sizeof(m));
1827 m.pointer_vertex = vertex3f;
1829 GL_LockArrays(0, numverts);
1830 // square alpha in framebuffer a few times to make it shiny
1831 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1832 // these comments are a test run through this math for intensity 0.5
1833 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1834 // 0.25 * 0.25 = 0.0625 (this is another pass)
1835 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1836 R_Mesh_Draw(numverts, numtriangles, elements);
1838 c_rt_lighttris += numtriangles;
1839 R_Mesh_Draw(numverts, numtriangles, elements);
1841 c_rt_lighttris += numtriangles;
1842 GL_LockArrays(0, 0);
1844 memset(&m, 0, sizeof(m));
1845 m.pointer_vertex = vertex3f;
1846 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1848 m.pointer_texcoord3f[0] = vertex3f;
1849 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1851 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1852 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1855 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1856 GL_LockArrays(0, numverts);
1857 R_Mesh_Draw(numverts, numtriangles, elements);
1858 GL_LockArrays(0, 0);
1860 c_rt_lighttris += numtriangles;
1862 memset(&m, 0, sizeof(m));
1863 m.pointer_vertex = vertex3f;
1864 m.tex[0] = R_GetTexture(glosstexture);
1865 m.pointer_texcoord[0] = texcoord2f;
1868 m.texcubemap[1] = R_GetTexture(lightcubemap);
1870 m.pointer_texcoord3f[1] = vertex3f;
1871 m.texmatrix[1] = *matrix_modeltolight;
1873 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1874 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1878 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1880 // 2/0/0/2 3D combine blendsquare path
1881 memset(&m, 0, sizeof(m));
1882 m.pointer_vertex = vertex3f;
1883 m.tex[0] = R_GetTexture(bumptexture);
1884 m.pointer_texcoord[0] = texcoord2f;
1885 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1886 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1887 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1888 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1890 GL_ColorMask(0,0,0,1);
1891 // this squares the result
1892 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1893 GL_LockArrays(0, numverts);
1894 R_Mesh_Draw(numverts, numtriangles, elements);
1895 GL_LockArrays(0, 0);
1897 c_rt_lighttris += numtriangles;
1899 memset(&m, 0, sizeof(m));
1900 m.pointer_vertex = vertex3f;
1902 GL_LockArrays(0, numverts);
1903 // square alpha in framebuffer a few times to make it shiny
1904 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1905 // these comments are a test run through this math for intensity 0.5
1906 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1907 // 0.25 * 0.25 = 0.0625 (this is another pass)
1908 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1909 R_Mesh_Draw(numverts, numtriangles, elements);
1911 c_rt_lighttris += numtriangles;
1912 R_Mesh_Draw(numverts, numtriangles, elements);
1914 c_rt_lighttris += numtriangles;
1915 GL_LockArrays(0, 0);
1917 memset(&m, 0, sizeof(m));
1918 m.pointer_vertex = vertex3f;
1919 m.tex[0] = R_GetTexture(glosstexture);
1920 m.pointer_texcoord[0] = texcoord2f;
1921 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1923 m.pointer_texcoord3f[1] = vertex3f;
1924 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1926 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1927 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1932 // 2/0/0/2/2 2D combine blendsquare path
1933 memset(&m, 0, sizeof(m));
1934 m.pointer_vertex = vertex3f;
1935 m.tex[0] = R_GetTexture(bumptexture);
1936 m.pointer_texcoord[0] = texcoord2f;
1937 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1938 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1939 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1940 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1942 GL_ColorMask(0,0,0,1);
1943 // this squares the result
1944 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1945 GL_LockArrays(0, numverts);
1946 R_Mesh_Draw(numverts, numtriangles, elements);
1947 GL_LockArrays(0, 0);
1949 c_rt_lighttris += numtriangles;
1951 memset(&m, 0, sizeof(m));
1952 m.pointer_vertex = vertex3f;
1954 GL_LockArrays(0, numverts);
1955 // square alpha in framebuffer a few times to make it shiny
1956 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1957 // these comments are a test run through this math for intensity 0.5
1958 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1959 // 0.25 * 0.25 = 0.0625 (this is another pass)
1960 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1961 R_Mesh_Draw(numverts, numtriangles, elements);
1963 c_rt_lighttris += numtriangles;
1964 R_Mesh_Draw(numverts, numtriangles, elements);
1966 c_rt_lighttris += numtriangles;
1967 GL_LockArrays(0, 0);
1969 memset(&m, 0, sizeof(m));
1970 m.pointer_vertex = vertex3f;
1971 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1973 m.pointer_texcoord3f[0] = vertex3f;
1974 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1976 m.pointer_texcoord[0] = varray_texcoord2f[0];
1977 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1979 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1981 m.pointer_texcoord3f[1] = vertex3f;
1982 m.texmatrix[1] = *matrix_modeltoattenuationz;
1984 m.pointer_texcoord[1] = varray_texcoord2f[1];
1985 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1988 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1989 GL_LockArrays(0, numverts);
1990 R_Mesh_Draw(numverts, numtriangles, elements);
1991 GL_LockArrays(0, 0);
1993 c_rt_lighttris += numtriangles;
1995 memset(&m, 0, sizeof(m));
1996 m.pointer_vertex = vertex3f;
1997 m.tex[0] = R_GetTexture(glosstexture);
1998 m.pointer_texcoord[0] = texcoord2f;
2001 m.texcubemap[1] = R_GetTexture(lightcubemap);
2003 m.pointer_texcoord3f[1] = vertex3f;
2004 m.texmatrix[1] = *matrix_modeltolight;
2006 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2007 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
2013 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
2014 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2015 VectorScale(lightcolor, colorscale, color2);
2016 GL_LockArrays(0, numverts);
2017 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2019 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
2020 R_Mesh_Draw(numverts, numtriangles, elements);
2022 c_rt_lighttris += numtriangles;
2024 GL_LockArrays(0, 0);
2031 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2032 VectorScale(lightcolor, r_shadow_lightintensityscale.value * ambientscale, color2);
2033 memset(&m, 0, sizeof(m));
2034 m.pointer_vertex = vertex3f;
2035 m.tex[0] = R_GetTexture(basetexture);
2036 m.pointer_texcoord[0] = texcoord2f;
2037 if (r_textureunits.integer >= 2)
2040 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2042 m.pointer_texcoord3f[1] = vertex3f;
2043 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
2045 m.pointer_texcoord[1] = varray_texcoord2f[1];
2046 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
2048 if (r_textureunits.integer >= 3)
2050 // Geforce3/Radeon class but not using dot3
2051 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2053 m.pointer_texcoord3f[2] = vertex3f;
2054 m.texmatrix[2] = *matrix_modeltoattenuationz;
2056 m.pointer_texcoord[2] = varray_texcoord2f[2];
2057 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
2062 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2064 color[0] = bound(0, color2[0], 1);
2065 color[1] = bound(0, color2[1], 1);
2066 color[2] = bound(0, color2[2], 1);
2067 if (r_textureunits.integer >= 3)
2069 GL_Color(color[0], color[1], color[2], 1);
2070 m.pointer_color = NULL;
2072 else if (r_textureunits.integer >= 2)
2074 R_Shadow_VertexNoShadingWithZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
2075 m.pointer_color = varray_color4f;
2079 R_Shadow_VertexNoShadingWithXYZAttenuation(numverts, vertex3f, color, matrix_modeltolight);
2080 m.pointer_color = varray_color4f;
2082 GL_LockArrays(0, numverts);
2083 R_Mesh_Draw(numverts, numtriangles, elements);
2084 GL_LockArrays(0, 0);
2086 c_rt_lighttris += numtriangles;
2091 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2092 VectorScale(lightcolor, r_shadow_lightintensityscale.value * diffusescale, color2);
2093 memset(&m, 0, sizeof(m));
2094 m.pointer_vertex = vertex3f;
2095 m.pointer_color = varray_color4f;
2096 m.tex[0] = R_GetTexture(basetexture);
2097 m.pointer_texcoord[0] = texcoord2f;
2098 if (r_textureunits.integer >= 2)
2101 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2103 m.pointer_texcoord3f[1] = vertex3f;
2104 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
2106 m.pointer_texcoord[1] = varray_texcoord2f[1];
2107 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
2109 if (r_textureunits.integer >= 3)
2111 // Geforce3/Radeon class but not using dot3
2112 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2114 m.pointer_texcoord3f[2] = vertex3f;
2115 m.texmatrix[2] = *matrix_modeltoattenuationz;
2117 m.pointer_texcoord[2] = varray_texcoord2f[2];
2118 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationz);
2123 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2125 color[0] = bound(0, color2[0], 1);
2126 color[1] = bound(0, color2[1], 1);
2127 color[2] = bound(0, color2[2], 1);
2128 if (r_textureunits.integer >= 3)
2129 R_Shadow_VertexShading(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2130 else if (r_textureunits.integer >= 2)
2131 R_Shadow_VertexShadingWithZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2133 R_Shadow_VertexShadingWithXYZAttenuation(numverts, vertex3f, normal3f, color, matrix_modeltolight);
2134 GL_LockArrays(0, numverts);
2135 R_Mesh_Draw(numverts, numtriangles, elements);
2136 GL_LockArrays(0, 0);
2138 c_rt_lighttris += numtriangles;
2144 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
2148 R_RTLight_Uncompile(rtlight);
2149 memset(rtlight, 0, sizeof(*rtlight));
2151 VectorCopy(light->origin, rtlight->shadoworigin);
2152 VectorCopy(light->color, rtlight->color);
2153 rtlight->radius = light->radius;
2154 //rtlight->cullradius = rtlight->radius;
2155 //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2156 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2157 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2158 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2159 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2160 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2161 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2162 rtlight->cubemapname[0] = 0;
2163 if (light->cubemapname[0])
2164 strcpy(rtlight->cubemapname, light->cubemapname);
2165 else if (light->cubemapnum > 0)
2166 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2167 rtlight->shadow = light->shadow;
2168 rtlight->corona = light->corona;
2169 rtlight->style = light->style;
2170 rtlight->isstatic = isstatic;
2171 rtlight->coronasizescale = light->coronasizescale;
2172 rtlight->ambientscale = light->ambientscale;
2173 rtlight->diffusescale = light->diffusescale;
2174 rtlight->specularscale = light->specularscale;
2175 rtlight->flags = light->flags;
2176 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2177 // ConcatScale won't work here because this needs to scale rotate and
2178 // translate, not just rotate
2179 scale = 1.0f / rtlight->radius;
2180 for (k = 0;k < 3;k++)
2181 for (j = 0;j < 4;j++)
2182 rtlight->matrix_worldtolight.m[k][j] *= scale;
2183 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
2184 Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
2186 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
2187 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
2188 VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.125f, rtlight->lightmap_light);
2189 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
2192 // compiles rtlight geometry
2193 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2194 void R_RTLight_Compile(rtlight_t *rtlight)
2196 int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
2197 entity_render_t *ent = &cl_entities[0].render;
2198 model_t *model = ent->model;
2200 // compile the light
2201 rtlight->compiled = true;
2202 rtlight->static_numclusters = 0;
2203 rtlight->static_numclusterpvsbytes = 0;
2204 rtlight->static_clusterlist = NULL;
2205 rtlight->static_clusterpvs = NULL;
2206 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2207 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2208 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2209 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2210 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2211 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2213 if (model && model->GetLightInfo)
2215 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
2216 r_shadow_compilingrtlight = rtlight;
2217 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
2218 R_Shadow_EnlargeSurfaceBuffer(model->nummodelsurfaces);
2219 model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2222 rtlight->static_numclusters = numclusters;
2223 rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
2224 rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
2225 rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
2226 memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
2227 memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
2229 if (model->DrawShadowVolume && rtlight->shadow)
2231 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2232 model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2233 rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
2235 if (model->DrawLight)
2237 rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2238 model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, 0, 0, 0, numsurfaces, r_shadow_buffer_surfacelist);
2239 rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
2241 // switch back to rendering when DrawShadowVolume or DrawLight is called
2242 r_shadow_compilingrtlight = NULL;
2246 // use smallest available cullradius - box radius or light radius
2247 //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2248 //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2252 if (rtlight->static_meshchain_shadow)
2255 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2258 shadowtris += mesh->numtriangles;
2264 if (rtlight->static_meshchain_light)
2267 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2270 lighttris += mesh->numtriangles;
2274 Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
2277 void R_RTLight_Uncompile(rtlight_t *rtlight)
2279 if (rtlight->compiled)
2281 if (rtlight->static_meshchain_shadow)
2282 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2283 rtlight->static_meshchain_shadow = NULL;
2284 if (rtlight->static_meshchain_light)
2285 Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2286 rtlight->static_meshchain_light = NULL;
2287 if (rtlight->static_clusterlist)
2288 Mem_Free(rtlight->static_clusterlist);
2289 rtlight->static_clusterlist = NULL;
2290 if (rtlight->static_clusterpvs)
2291 Mem_Free(rtlight->static_clusterpvs);
2292 rtlight->static_clusterpvs = NULL;
2293 rtlight->static_numclusters = 0;
2294 rtlight->static_numclusterpvsbytes = 0;
2295 rtlight->compiled = false;
2299 void R_Shadow_UncompileWorldLights(void)
2302 for (light = r_shadow_worldlightchain;light;light = light->next)
2303 R_RTLight_Uncompile(&light->rtlight);
2306 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2309 entity_render_t *ent;
2311 vec3_t relativelightorigin, relativeeyeorigin, lightcolor, lightcolor2;
2312 rtexture_t *cubemaptexture;
2313 matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2314 int numclusters, numsurfaces;
2315 int *clusterlist, *surfacelist;
2317 vec3_t cullmins, cullmaxs;
2321 // loading is done before visibility checks because loading should happen
2322 // all at once at the start of a level, not when it stalls gameplay.
2323 // (especially important to benchmarks)
2324 if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2325 R_RTLight_Compile(rtlight);
2326 if (rtlight->cubemapname[0])
2327 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2329 cubemaptexture = NULL;
2331 cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2332 cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2333 cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2334 cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2335 cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2336 cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2337 if (d_lightstylevalue[rtlight->style] <= 0)
2344 if (rtlight->compiled && r_shadow_staticworldlights.integer)
2346 // compiled light, world available and can receive realtime lighting
2347 // retrieve cluster information
2348 numclusters = rtlight->static_numclusters;
2349 clusterlist = rtlight->static_clusterlist;
2350 clusterpvs = rtlight->static_clusterpvs;
2351 VectorCopy(rtlight->cullmins, cullmins);
2352 VectorCopy(rtlight->cullmaxs, cullmaxs);
2354 else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
2356 // dynamic light, world available and can receive realtime lighting
2357 // if the light box is offscreen, skip it right away
2358 if (R_CullBox(cullmins, cullmaxs))
2360 // calculate lit surfaces and clusters
2361 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
2362 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->nummodelsurfaces);
2363 cl.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2364 clusterlist = r_shadow_buffer_clusterlist;
2365 clusterpvs = r_shadow_buffer_clusterpvs;
2366 surfacelist = r_shadow_buffer_surfacelist;
2368 // if the reduced cluster bounds are offscreen, skip it
2369 if (R_CullBox(cullmins, cullmaxs))
2371 // check if light is illuminating any visible clusters
2374 for (i = 0;i < numclusters;i++)
2375 if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
2377 if (i == numclusters)
2380 // set up a scissor rectangle for this light
2381 if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
2384 f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2385 VectorScale(rtlight->color, f, lightcolor);
2387 if (rtlight->selected)
2389 f = 2 + sin(realtime * M_PI * 4.0);
2390 VectorScale(lightcolor, f, lightcolor);
2394 shadow = rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows);
2396 if (shadow && (gl_stencil || visiblevolumes))
2398 if (!visiblevolumes)
2399 R_Shadow_Stage_ShadowVolumes();
2400 ent = &cl_entities[0].render;
2401 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2403 memset(&m, 0, sizeof(m));
2404 R_Mesh_Matrix(&ent->matrix);
2405 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2407 m.pointer_vertex = mesh->vertex3f;
2409 GL_LockArrays(0, mesh->numverts);
2410 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2412 // decrement stencil if frontface is behind depthbuffer
2413 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2414 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2415 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2416 c_rtcached_shadowmeshes++;
2417 c_rtcached_shadowtris += mesh->numtriangles;
2418 // increment stencil if backface is behind depthbuffer
2419 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2420 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2422 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2423 c_rtcached_shadowmeshes++;
2424 c_rtcached_shadowtris += mesh->numtriangles;
2425 GL_LockArrays(0, 0);
2428 else if (numsurfaces)
2430 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2431 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2433 if (r_drawentities.integer)
2435 for (i = 0;i < r_refdef.numentities;i++)
2437 ent = r_refdef.entities[i];
2439 if (r_shadow_cull.integer)
2441 if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2443 if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2446 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2448 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2449 // light emitting entities should not cast their own shadow
2450 if (VectorLength2(relativelightorigin) < 0.1)
2452 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->nummodelsurfaces, ent->model->surfacelist);
2457 if (!visiblevolumes)
2459 R_Shadow_Stage_Light(shadow && gl_stencil);
2461 ent = &cl_entities[0].render;
2462 if (ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2464 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2465 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2466 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2467 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2468 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2469 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2471 R_Mesh_Matrix(&ent->matrix);
2472 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2473 R_Shadow_RenderLighting(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_diffuse, mesh->map_normal, mesh->map_specular, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale);
2476 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, numsurfaces, surfacelist);
2478 if (r_drawentities.integer)
2480 for (i = 0;i < r_refdef.numentities;i++)
2482 ent = r_refdef.entities[i];
2483 // can't draw transparent entity lighting here because
2484 // transparent meshes are deferred for later
2485 if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & (RENDER_LIGHT | RENDER_TRANSPARENT)) == RENDER_LIGHT)
2487 VectorScale(lightcolor, ent->alpha, lightcolor2);
2488 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2489 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2490 Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2491 Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2492 Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2493 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor2, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, rtlight->ambientscale, rtlight->diffusescale, rtlight->specularscale, ent->model->nummodelsurfaces, ent->model->surfacelist);
2500 void R_ShadowVolumeLighting(int visiblevolumes)
2506 if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2507 R_Shadow_EditLights_Reload_f();
2511 memset(&m, 0, sizeof(m));
2514 GL_BlendFunc(GL_ONE, GL_ONE);
2515 GL_DepthMask(false);
2516 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2517 qglDisable(GL_CULL_FACE);
2518 GL_Color(0.0, 0.0125, 0.1, 1);
2521 R_Shadow_Stage_Begin();
2522 flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2523 if (r_shadow_debuglight.integer >= 0)
2525 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2526 if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2527 R_DrawRTLight(&light->rtlight, visiblevolumes);
2530 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2531 if (light->flags & flag)
2532 R_DrawRTLight(&light->rtlight, visiblevolumes);
2534 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2535 R_DrawRTLight(&light->rtlight, visiblevolumes);
2539 qglEnable(GL_CULL_FACE);
2540 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2543 R_Shadow_Stage_End();
2546 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2547 typedef struct suffixinfo_s
2550 qboolean flipx, flipy, flipdiagonal;
2553 static suffixinfo_t suffix[3][6] =
2556 {"px", false, false, false},
2557 {"nx", false, false, false},
2558 {"py", false, false, false},
2559 {"ny", false, false, false},
2560 {"pz", false, false, false},
2561 {"nz", false, false, false}
2564 {"posx", false, false, false},
2565 {"negx", false, false, false},
2566 {"posy", false, false, false},
2567 {"negy", false, false, false},
2568 {"posz", false, false, false},
2569 {"negz", false, false, false}
2572 {"rt", true, false, true},
2573 {"lf", false, true, true},
2574 {"ft", true, true, false},
2575 {"bk", false, false, false},
2576 {"up", true, false, true},
2577 {"dn", true, false, true}
2581 static int componentorder[4] = {0, 1, 2, 3};
2583 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2585 int i, j, cubemapsize;
2586 qbyte *cubemappixels, *image_rgba;
2587 rtexture_t *cubemaptexture;
2589 // must start 0 so the first loadimagepixels has no requested width/height
2591 cubemappixels = NULL;
2592 cubemaptexture = NULL;
2593 // keep trying different suffix groups (posx, px, rt) until one loads
2594 for (j = 0;j < 3 && !cubemappixels;j++)
2596 // load the 6 images in the suffix group
2597 for (i = 0;i < 6;i++)
2599 // generate an image name based on the base and and suffix
2600 snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2602 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2604 // an image loaded, make sure width and height are equal
2605 if (image_width == image_height)
2607 // if this is the first image to load successfully, allocate the cubemap memory
2608 if (!cubemappixels && image_width >= 1)
2610 cubemapsize = image_width;
2611 // note this clears to black, so unavailable sides are black
2612 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2614 // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2616 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);
2619 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2621 Mem_Free(image_rgba);
2625 // if a cubemap loaded, upload it
2628 if (!r_shadow_filters_texturepool)
2629 r_shadow_filters_texturepool = R_AllocTexturePool();
2630 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2631 Mem_Free(cubemappixels);
2635 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2636 for (j = 0;j < 3;j++)
2637 for (i = 0;i < 6;i++)
2638 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2639 Con_Print(" and was unable to find any of them.\n");
2641 return cubemaptexture;
2644 rtexture_t *R_Shadow_Cubemap(const char *basename)
2647 for (i = 0;i < numcubemaps;i++)
2648 if (!strcasecmp(cubemaps[i].basename, basename))
2649 return cubemaps[i].texture;
2650 if (i >= MAX_CUBEMAPS)
2653 strcpy(cubemaps[i].basename, basename);
2654 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2655 return cubemaps[i].texture;
2658 void R_Shadow_FreeCubemaps(void)
2661 R_FreeTexturePool(&r_shadow_filters_texturepool);
2664 dlight_t *R_Shadow_NewWorldLight(void)
2667 light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2668 light->next = r_shadow_worldlightchain;
2669 r_shadow_worldlightchain = light;
2673 void R_Shadow_UpdateWorldLight(dlight_t *light, vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
2675 VectorCopy(origin, light->origin);
2676 light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2677 light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2678 light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2679 light->color[0] = max(color[0], 0);
2680 light->color[1] = max(color[1], 0);
2681 light->color[2] = max(color[2], 0);
2682 light->radius = max(radius, 0);
2683 light->style = style;
2684 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2686 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2689 light->shadow = shadowenable;
2690 light->corona = corona;
2693 strlcpy(light->cubemapname, cubemapname, strlen(light->cubemapname));
2694 light->coronasizescale = coronasizescale;
2695 light->ambientscale = ambientscale;
2696 light->diffusescale = diffusescale;
2697 light->specularscale = specularscale;
2698 light->flags = flags;
2699 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2701 R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2704 void R_Shadow_FreeWorldLight(dlight_t *light)
2706 dlight_t **lightpointer;
2707 R_RTLight_Uncompile(&light->rtlight);
2708 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2709 if (*lightpointer != light)
2710 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2711 *lightpointer = light->next;
2715 void R_Shadow_ClearWorldLights(void)
2717 while (r_shadow_worldlightchain)
2718 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2719 r_shadow_selectedlight = NULL;
2720 R_Shadow_FreeCubemaps();
2723 void R_Shadow_SelectLight(dlight_t *light)
2725 if (r_shadow_selectedlight)
2726 r_shadow_selectedlight->selected = false;
2727 r_shadow_selectedlight = light;
2728 if (r_shadow_selectedlight)
2729 r_shadow_selectedlight->selected = true;
2732 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2734 float scale = r_editlights_cursorgrid.value * 0.5f;
2735 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);
2738 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2741 const dlight_t *light;
2744 if (light->selected)
2745 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2748 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);
2751 void R_Shadow_DrawLightSprites(void)
2757 for (i = 0;i < 5;i++)
2759 lighttextures[i] = NULL;
2760 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2761 lighttextures[i] = pic->tex;
2764 for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
2765 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5);
2766 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2769 void R_Shadow_SelectLightInView(void)
2771 float bestrating, rating, temp[3];
2772 dlight_t *best, *light;
2775 for (light = r_shadow_worldlightchain;light;light = light->next)
2777 VectorSubtract(light->origin, r_vieworigin, temp);
2778 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2781 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2782 if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2784 bestrating = rating;
2789 R_Shadow_SelectLight(best);
2792 void R_Shadow_LoadWorldLights(void)
2794 int n, a, style, shadow, flags;
2795 char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2796 float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
2797 if (cl.worldmodel == NULL)
2799 Con_Print("No map loaded.\n");
2802 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2803 strlcat (name, ".rtlights", sizeof (name));
2804 lightsstring = FS_LoadFile(name, tempmempool, false);
2814 for (;COM_Parse(t, true) && strcmp(
2815 if (COM_Parse(t, true))
2817 if (com_token[0] == '!')
2820 origin[0] = atof(com_token+1);
2823 origin[0] = atof(com_token);
2828 while (*s && *s != '\n')
2834 // check for modifier flags
2840 a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f %f %f %f %f %i", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2], &coronasizescale, &ambientscale, &diffusescale, &specularscale, &flags);
2842 flags = LIGHTFLAG_REALTIMEMODE;
2850 coronasizescale = 0.25f;
2852 VectorClear(angles);
2855 if (a < 9 || !strcmp(cubemapname, "\"\""))
2860 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] coronasizescale ambientscale diffusescale specularscale flags)\n", a, n + 1);
2863 VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2864 radius *= r_editlights_rtlightssizescale.value;
2865 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
2870 Con_Printf("invalid rtlights file \"%s\"\n", name);
2871 Mem_Free(lightsstring);
2875 void R_Shadow_SaveWorldLights(void)
2878 int bufchars, bufmaxchars;
2880 char name[MAX_QPATH];
2882 if (!r_shadow_worldlightchain)
2884 if (cl.worldmodel == NULL)
2886 Con_Print("No map loaded.\n");
2889 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2890 strlcat (name, ".rtlights", sizeof (name));
2891 bufchars = bufmaxchars = 0;
2893 for (light = r_shadow_worldlightchain;light;light = light->next)
2895 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
2896 sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\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, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
2897 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
2898 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, light->corona, light->angles[0], light->angles[1], light->angles[2]);
2900 sprintf(line, "%s%f %f %f %f %f %f %f %d\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);
2901 if (bufchars + (int) strlen(line) > bufmaxchars)
2903 bufmaxchars = bufchars + strlen(line) + 2048;
2905 buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2909 memcpy(buf, oldbuf, bufchars);
2915 memcpy(buf + bufchars, line, strlen(line));
2916 bufchars += strlen(line);
2920 FS_WriteFile(name, buf, bufchars);
2925 void R_Shadow_LoadLightsFile(void)
2928 char name[MAX_QPATH], *lightsstring, *s, *t;
2929 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2930 if (cl.worldmodel == NULL)
2932 Con_Print("No map loaded.\n");
2935 FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2936 strlcat (name, ".lights", sizeof (name));
2937 lightsstring = FS_LoadFile(name, tempmempool, false);
2945 while (*s && *s != '\n')
2950 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);
2954 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);
2957 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2958 radius = bound(15, radius, 4096);
2959 VectorScale(color, (2.0f / (8388608.0f)), color);
2960 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
2965 Con_Printf("invalid lights file \"%s\"\n", name);
2966 Mem_Free(lightsstring);
2970 // tyrlite/hmap2 light types in the delay field
2971 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
2973 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2975 int entnum, style, islight, skin, pflags, effects, type, n;
2978 float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
2979 char key[256], value[1024];
2981 if (cl.worldmodel == NULL)
2983 Con_Print("No map loaded.\n");
2986 // try to load a .ent file first
2987 FS_StripExtension (cl.worldmodel->name, key, sizeof (key));
2988 strlcat (key, ".ent", sizeof (key));
2989 data = entfiledata = FS_LoadFile(key, tempmempool, true);
2990 // and if that is not found, fall back to the bsp file entity string
2992 data = cl.worldmodel->brush.entities;
2995 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2997 type = LIGHTTYPE_MINUSX;
2998 origin[0] = origin[1] = origin[2] = 0;
2999 originhack[0] = originhack[1] = originhack[2] = 0;
3000 angles[0] = angles[1] = angles[2] = 0;
3001 color[0] = color[1] = color[2] = 1;
3002 light[0] = light[1] = light[2] = 1;light[3] = 300;
3003 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3013 if (!COM_ParseToken(&data, false))
3015 if (com_token[0] == '}')
3016 break; // end of entity
3017 if (com_token[0] == '_')
3018 strcpy(key, com_token + 1);
3020 strcpy(key, com_token);
3021 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3022 key[strlen(key)-1] = 0;
3023 if (!COM_ParseToken(&data, false))
3025 strcpy(value, com_token);
3027 // now that we have the key pair worked out...
3028 if (!strcmp("light", key))
3030 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3034 light[0] = vec[0] * (1.0f / 256.0f);
3035 light[1] = vec[0] * (1.0f / 256.0f);
3036 light[2] = vec[0] * (1.0f / 256.0f);
3042 light[0] = vec[0] * (1.0f / 255.0f);
3043 light[1] = vec[1] * (1.0f / 255.0f);
3044 light[2] = vec[2] * (1.0f / 255.0f);
3048 else if (!strcmp("delay", key))
3050 else if (!strcmp("origin", key))
3051 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3052 else if (!strcmp("angle", key))
3053 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3054 else if (!strcmp("angles", key))
3055 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3056 else if (!strcmp("color", key))
3057 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3058 else if (!strcmp("wait", key))
3059 fadescale = atof(value);
3060 else if (!strcmp("classname", key))
3062 if (!strncmp(value, "light", 5))
3065 if (!strcmp(value, "light_fluoro"))
3070 overridecolor[0] = 1;
3071 overridecolor[1] = 1;
3072 overridecolor[2] = 1;
3074 if (!strcmp(value, "light_fluorospark"))
3079 overridecolor[0] = 1;
3080 overridecolor[1] = 1;
3081 overridecolor[2] = 1;
3083 if (!strcmp(value, "light_globe"))
3088 overridecolor[0] = 1;
3089 overridecolor[1] = 0.8;
3090 overridecolor[2] = 0.4;
3092 if (!strcmp(value, "light_flame_large_yellow"))
3097 overridecolor[0] = 1;
3098 overridecolor[1] = 0.5;
3099 overridecolor[2] = 0.1;
3101 if (!strcmp(value, "light_flame_small_yellow"))
3106 overridecolor[0] = 1;
3107 overridecolor[1] = 0.5;
3108 overridecolor[2] = 0.1;
3110 if (!strcmp(value, "light_torch_small_white"))
3115 overridecolor[0] = 1;
3116 overridecolor[1] = 0.5;
3117 overridecolor[2] = 0.1;
3119 if (!strcmp(value, "light_torch_small_walltorch"))
3124 overridecolor[0] = 1;
3125 overridecolor[1] = 0.5;
3126 overridecolor[2] = 0.1;
3130 else if (!strcmp("style", key))
3131 style = atoi(value);
3132 else if (cl.worldmodel->type == mod_brushq3)
3134 if (!strcmp("scale", key))
3135 lightscale = atof(value);
3136 if (!strcmp("fade", key))
3137 fadescale = atof(value);
3139 else if (!strcmp("skin", key))
3140 skin = (int)atof(value);
3141 else if (!strcmp("pflags", key))
3142 pflags = (int)atof(value);
3143 else if (!strcmp("effects", key))
3144 effects = (int)atof(value);
3148 if (lightscale <= 0)
3152 if (color[0] == color[1] && color[0] == color[2])
3154 color[0] *= overridecolor[0];
3155 color[1] *= overridecolor[1];
3156 color[2] *= overridecolor[2];
3158 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3159 color[0] = color[0] * light[0];
3160 color[1] = color[1] * light[1];
3161 color[2] = color[2] * light[2];
3164 case LIGHTTYPE_MINUSX:
3166 case LIGHTTYPE_RECIPX:
3168 VectorScale(color, (1.0f / 16.0f), color);
3170 case LIGHTTYPE_RECIPXX:
3172 VectorScale(color, (1.0f / 16.0f), color);
3175 case LIGHTTYPE_NONE:
3179 case LIGHTTYPE_MINUSXX:
3182 VectorAdd(origin, originhack, origin);
3184 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3187 Mem_Free(entfiledata);
3191 void R_Shadow_SetCursorLocationForView(void)
3193 vec_t dist, push, frac;
3194 vec3_t dest, endpos, normal;
3195 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
3196 frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
3199 dist = frac * r_editlights_cursordistance.value;
3200 push = r_editlights_cursorpushback.value;
3204 VectorMA(endpos, push, r_viewforward, endpos);
3205 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
3207 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3208 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3209 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3212 void R_Shadow_UpdateWorldLightSelection(void)
3214 if (r_editlights.integer)
3216 R_Shadow_SetCursorLocationForView();
3217 R_Shadow_SelectLightInView();
3218 R_Shadow_DrawLightSprites();
3221 R_Shadow_SelectLight(NULL);
3224 void R_Shadow_EditLights_Clear_f(void)
3226 R_Shadow_ClearWorldLights();
3229 void R_Shadow_EditLights_Reload_f(void)
3233 strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
3234 R_Shadow_ClearWorldLights();
3235 R_Shadow_LoadWorldLights();
3236 if (r_shadow_worldlightchain == NULL)
3238 R_Shadow_LoadLightsFile();
3239 if (r_shadow_worldlightchain == NULL)
3240 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3244 void R_Shadow_EditLights_Save_f(void)
3248 R_Shadow_SaveWorldLights();
3251 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3253 R_Shadow_ClearWorldLights();
3254 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3257 void R_Shadow_EditLights_ImportLightsFile_f(void)
3259 R_Shadow_ClearWorldLights();
3260 R_Shadow_LoadLightsFile();
3263 void R_Shadow_EditLights_Spawn_f(void)
3266 if (!r_editlights.integer)
3268 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3271 if (Cmd_Argc() != 1)
3273 Con_Print("r_editlights_spawn does not take parameters\n");
3276 color[0] = color[1] = color[2] = 1;
3277 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3280 void R_Shadow_EditLights_Edit_f(void)
3282 vec3_t origin, angles, color;
3283 vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3284 int style, shadows, flags, normalmode, realtimemode;
3285 char cubemapname[1024];
3286 if (!r_editlights.integer)
3288 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3291 if (!r_shadow_selectedlight)
3293 Con_Print("No selected light.\n");
3296 VectorCopy(r_shadow_selectedlight->origin, origin);
3297 VectorCopy(r_shadow_selectedlight->angles, angles);
3298 VectorCopy(r_shadow_selectedlight->color, color);
3299 radius = r_shadow_selectedlight->radius;
3300 style = r_shadow_selectedlight->style;
3301 if (r_shadow_selectedlight->cubemapname)
3302 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
3305 shadows = r_shadow_selectedlight->shadow;
3306 corona = r_shadow_selectedlight->corona;
3307 coronasizescale = r_shadow_selectedlight->coronasizescale;
3308 ambientscale = r_shadow_selectedlight->ambientscale;
3309 diffusescale = r_shadow_selectedlight->diffusescale;
3310 specularscale = r_shadow_selectedlight->specularscale;
3311 flags = r_shadow_selectedlight->flags;
3312 normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3313 realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3314 if (!strcmp(Cmd_Argv(1), "origin"))
3316 if (Cmd_Argc() != 5)
3318 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3321 origin[0] = atof(Cmd_Argv(2));
3322 origin[1] = atof(Cmd_Argv(3));
3323 origin[2] = atof(Cmd_Argv(4));
3325 else if (!strcmp(Cmd_Argv(1), "originx"))
3327 if (Cmd_Argc() != 3)
3329 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3332 origin[0] = atof(Cmd_Argv(2));
3334 else if (!strcmp(Cmd_Argv(1), "originy"))
3336 if (Cmd_Argc() != 3)
3338 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3341 origin[1] = atof(Cmd_Argv(2));
3343 else if (!strcmp(Cmd_Argv(1), "originz"))
3345 if (Cmd_Argc() != 3)
3347 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3350 origin[2] = atof(Cmd_Argv(2));
3352 else if (!strcmp(Cmd_Argv(1), "move"))
3354 if (Cmd_Argc() != 5)
3356 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3359 origin[0] += atof(Cmd_Argv(2));
3360 origin[1] += atof(Cmd_Argv(3));
3361 origin[2] += atof(Cmd_Argv(4));
3363 else if (!strcmp(Cmd_Argv(1), "movex"))
3365 if (Cmd_Argc() != 3)
3367 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3370 origin[0] += atof(Cmd_Argv(2));
3372 else if (!strcmp(Cmd_Argv(1), "movey"))
3374 if (Cmd_Argc() != 3)
3376 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3379 origin[1] += atof(Cmd_Argv(2));
3381 else if (!strcmp(Cmd_Argv(1), "movez"))
3383 if (Cmd_Argc() != 3)
3385 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3388 origin[2] += atof(Cmd_Argv(2));
3390 else if (!strcmp(Cmd_Argv(1), "angles"))
3392 if (Cmd_Argc() != 5)
3394 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3397 angles[0] = atof(Cmd_Argv(2));
3398 angles[1] = atof(Cmd_Argv(3));
3399 angles[2] = atof(Cmd_Argv(4));
3401 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3403 if (Cmd_Argc() != 3)
3405 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3408 angles[0] = atof(Cmd_Argv(2));
3410 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3412 if (Cmd_Argc() != 3)
3414 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3417 angles[1] = atof(Cmd_Argv(2));
3419 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3421 if (Cmd_Argc() != 3)
3423 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3426 angles[2] = atof(Cmd_Argv(2));
3428 else if (!strcmp(Cmd_Argv(1), "color"))
3430 if (Cmd_Argc() != 5)
3432 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3435 color[0] = atof(Cmd_Argv(2));
3436 color[1] = atof(Cmd_Argv(3));
3437 color[2] = atof(Cmd_Argv(4));
3439 else if (!strcmp(Cmd_Argv(1), "radius"))
3441 if (Cmd_Argc() != 3)
3443 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3446 radius = atof(Cmd_Argv(2));
3448 else if (!strcmp(Cmd_Argv(1), "style"))
3450 if (Cmd_Argc() != 3)
3452 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3455 style = atoi(Cmd_Argv(2));
3457 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3461 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3464 if (Cmd_Argc() == 3)
3465 strcpy(cubemapname, Cmd_Argv(2));
3469 else if (!strcmp(Cmd_Argv(1), "shadows"))
3471 if (Cmd_Argc() != 3)
3473 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3476 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3478 else if (!strcmp(Cmd_Argv(1), "corona"))
3480 if (Cmd_Argc() != 3)
3482 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3485 corona = atof(Cmd_Argv(2));
3487 else if (!strcmp(Cmd_Argv(1), "coronasize"))
3489 if (Cmd_Argc() != 3)
3491 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3494 coronasizescale = atof(Cmd_Argv(2));
3496 else if (!strcmp(Cmd_Argv(1), "ambient"))
3498 if (Cmd_Argc() != 3)
3500 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3503 ambientscale = atof(Cmd_Argv(2));
3505 else if (!strcmp(Cmd_Argv(1), "diffuse"))
3507 if (Cmd_Argc() != 3)
3509 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3512 diffusescale = atof(Cmd_Argv(2));
3514 else if (!strcmp(Cmd_Argv(1), "specular"))
3516 if (Cmd_Argc() != 3)
3518 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3521 specularscale = atof(Cmd_Argv(2));
3523 else if (!strcmp(Cmd_Argv(1), "normalmode"))
3525 if (Cmd_Argc() != 3)
3527 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3530 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3532 else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3534 if (Cmd_Argc() != 3)
3536 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3539 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3543 Con_Print("usage: r_editlights_edit [property] [value]\n");
3544 Con_Print("Selected light's properties:\n");
3545 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3546 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3547 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3548 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3549 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3550 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3551 Con_Printf("Shadows : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3552 Con_Printf("Cubemap : %s\n", r_shadow_selectedlight->cubemapname);
3553 Con_Printf("CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);
3554 Con_Printf("Ambient : %f\n", r_shadow_selectedlight->ambientscale);
3555 Con_Printf("Diffuse : %f\n", r_shadow_selectedlight->diffusescale);
3556 Con_Printf("Specular : %f\n", r_shadow_selectedlight->specularscale);
3557 Con_Printf("NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
3558 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
3561 flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
3562 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3565 void R_Shadow_EditLights_EditAll_f(void)
3569 if (!r_editlights.integer)
3571 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
3575 for (light = r_shadow_worldlightchain;light;light = light->next)
3577 R_Shadow_SelectLight(light);
3578 R_Shadow_EditLights_Edit_f();
3582 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3584 int lightnumber, lightcount;
3588 if (!r_editlights.integer)
3594 for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
3595 if (light == r_shadow_selectedlight)
3596 lightnumber = lightcount;
3597 sprintf(temp, "Cursor %f %f %f Total Lights %i", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2], lightcount);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3598 if (r_shadow_selectedlight == NULL)
3600 sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3601 sprintf(temp, "Origin : %f %f %f\n", 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;
3602 sprintf(temp, "Angles : %f %f %f\n", 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;
3603 sprintf(temp, "Color : %f %f %f\n", 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;
3604 sprintf(temp, "Radius : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3605 sprintf(temp, "Corona : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3606 sprintf(temp, "Style : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3607 sprintf(temp, "Shadows : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3608 sprintf(temp, "Cubemap : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3609 sprintf(temp, "CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3610 sprintf(temp, "Ambient : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3611 sprintf(temp, "Diffuse : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3612 sprintf(temp, "Specular : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3613 sprintf(temp, "NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3614 sprintf(temp, "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3617 void R_Shadow_EditLights_ToggleShadow_f(void)
3619 if (!r_editlights.integer)
3621 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3624 if (!r_shadow_selectedlight)
3626 Con_Print("No selected light.\n");
3629 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, 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, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
3632 void R_Shadow_EditLights_ToggleCorona_f(void)
3634 if (!r_editlights.integer)
3636 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3639 if (!r_shadow_selectedlight)
3641 Con_Print("No selected light.\n");
3644 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, 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, r_shadow_selectedlight->coronasizescale, r_shadow_selectedlight->ambientscale, r_shadow_selectedlight->diffusescale, r_shadow_selectedlight->specularscale, r_shadow_selectedlight->flags);
3647 void R_Shadow_EditLights_Remove_f(void)
3649 if (!r_editlights.integer)
3651 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3654 if (!r_shadow_selectedlight)
3656 Con_Print("No selected light.\n");
3659 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3660 r_shadow_selectedlight = NULL;
3663 void R_Shadow_EditLights_Help_f(void)
3666 "Documentation on r_editlights system:\n"
3668 "r_editlights : enable/disable editing mode\n"
3669 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3670 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3671 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3672 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3673 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3674 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3675 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3677 "r_editlights_help : this help\n"
3678 "r_editlights_clear : remove all lights\n"
3679 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3680 "r_editlights_save : save to .rtlights file\n"
3681 "r_editlights_spawn : create a light with default settings\n"
3682 "r_editlights_edit command : edit selected light - more documentation below\n"
3683 "r_editlights_remove : remove selected light\n"
3684 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3685 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3686 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3688 "origin x y z : set light location\n"
3689 "originx x: set x component of light location\n"
3690 "originy y: set y component of light location\n"
3691 "originz z: set z component of light location\n"
3692 "move x y z : adjust light location\n"
3693 "movex x: adjust x component of light location\n"
3694 "movey y: adjust y component of light location\n"
3695 "movez z: adjust z component of light location\n"
3696 "angles x y z : set light angles\n"
3697 "anglesx x: set x component of light angles\n"
3698 "anglesy y: set y component of light angles\n"
3699 "anglesz z: set z component of light angles\n"
3700 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3701 "radius radius : set radius (size) of light\n"
3702 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3703 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3704 "shadows 1/0 : turn on/off shadows\n"
3705 "corona n : set corona intensity\n"
3706 "coronasize n : set corona size (0-1)\n"
3707 "ambient n : set ambient intensity (0-1)\n"
3708 "diffuse n : set diffuse intensity (0-1)\n"
3709 "specular n : set specular intensity (0-1)\n"
3710 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
3711 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
3712 "<nothing> : print light properties to console\n"
3716 void R_Shadow_EditLights_CopyInfo_f(void)
3718 if (!r_editlights.integer)
3720 Con_Print("Cannot copy light info when not in editing mode. Set r_editlights to 1.\n");
3723 if (!r_shadow_selectedlight)
3725 Con_Print("No selected light.\n");
3728 VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
3729 VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
3730 r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
3731 r_shadow_bufferlight.style = r_shadow_selectedlight->style;
3732 if (r_shadow_selectedlight->cubemapname)
3733 strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
3735 r_shadow_bufferlight.cubemapname[0] = 0;
3736 r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
3737 r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
3738 r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
3739 r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
3740 r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
3741 r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
3742 r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
3745 void R_Shadow_EditLights_PasteInfo_f(void)
3747 if (!r_editlights.integer)
3749 Con_Print("Cannot paste light info when not in editing mode. Set r_editlights to 1.\n");
3752 if (!r_shadow_selectedlight)
3754 Con_Print("No selected light.\n");
3757 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, r_shadow_selectedlight->origin, r_shadow_bufferlight.angles, r_shadow_bufferlight.color, r_shadow_bufferlight.radius, r_shadow_bufferlight.corona, r_shadow_bufferlight.style, r_shadow_bufferlight.shadow, r_shadow_bufferlight.cubemapname, r_shadow_bufferlight.coronasizescale, r_shadow_bufferlight.ambientscale, r_shadow_bufferlight.diffusescale, r_shadow_bufferlight.specularscale, r_shadow_bufferlight.flags);
3760 void R_Shadow_EditLights_Init(void)
3762 Cvar_RegisterVariable(&r_editlights);
3763 Cvar_RegisterVariable(&r_editlights_cursordistance);
3764 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3765 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3766 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3767 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3768 Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3769 Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3770 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3771 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3772 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3773 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3774 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3775 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3776 Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
3777 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3778 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3779 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3780 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3781 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3782 Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f);
3783 Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f);