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 normally rendered using Carmack's Reverse technique, in which
11 backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
12 zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
13 where shadows did not intersect the visible geometry, suitable as a stencil
14 mask for rendering lighting everywhere but shadow.
16 In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
17 as decrement and the frontfaces as increment, and we redefine the DepthFunc to
18 GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
19 and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
20 additionally we clear stencil to 128 to avoid the need for the unclamped
21 incr/decr extension (not related to patent).
24 This algorithm may be covered by Creative's patent (US Patent #6384822),
25 however that patent is quite specific about increment on backfaces and
26 decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
27 opposite this implementation and partially opposite Carmack's Reverse paper
28 (which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
32 Terminology: Stencil Light Volume (sometimes called Light Volumes)
33 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
34 areas in shadow it contains the areas in light, this can only be built
35 quickly for certain limited cases (such as portal visibility from a point),
36 but is quite useful for some effects (sunlight coming from sky polygons is
37 one possible example, translucent occluders is another example).
41 Terminology: Optimized Stencil Shadow Volume
42 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
43 no duplicate coverage of areas (no need to shadow an area twice), often this
44 greatly improves performance but is an operation too costly to use on moving
45 lights (however completely optimal Stencil Light Volumes can be constructed
50 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
51 Per pixel evaluation of lighting equations, at a bare minimum this involves
52 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
53 vector and surface normal, using a texture of the surface bumps, called a
54 NormalMap) if supported by hardware; in our case there is support for cards
55 which are incapable of DOT3, the quality is quite poor however. Additionally
56 it is desirable to have specular evaluation per pixel, per vertex
57 normalization of specular halfangle vectors causes noticable distortion but
58 is unavoidable on hardware without GL_ARB_fragment_program or
59 GL_ARB_fragment_shader.
63 Terminology: Normalization CubeMap
64 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
65 encoded as RGB colors) for any possible direction, this technique allows per
66 pixel calculation of incidence vector for per pixel lighting purposes, which
67 would not otherwise be possible per pixel without GL_ARB_fragment_program or
68 GL_ARB_fragment_shader.
72 Terminology: 2D+1D Attenuation Texturing
73 A very crude approximation of light attenuation with distance which results
74 in cylindrical light shapes which fade vertically as a streak (some games
75 such as Doom3 allow this to be rotated to be less noticable in specific
76 cases), the technique is simply modulating lighting by two 2D textures (which
77 can be the same) on different axes of projection (XY and Z, typically), this
78 is the second best technique available without 3D Attenuation Texturing,
79 GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
83 Terminology: 2D+1D Inverse Attenuation Texturing
84 A clever method described in papers on the Abducted engine, this has a squared
85 distance texture (bright on the outside, black in the middle), which is used
86 twice using GL_ADD blending, the result of this is used in an inverse modulate
87 (GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
88 lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
93 Terminology: 3D Attenuation Texturing
94 A slightly crude approximation of light attenuation with distance, its flaws
95 are limited radius and resolution (performance tradeoffs).
99 Terminology: 3D Attenuation-Normalization Texturing
100 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
101 vectors shorter the lighting becomes darker, a very effective optimization of
102 diffuse lighting if 3D Attenuation Textures are already used.
106 Terminology: Light Cubemap Filtering
107 A technique for modeling non-uniform light distribution according to
108 direction, for example a lantern may use a cubemap to describe the light
109 emission pattern of the cage around the lantern (as well as soot buildup
110 discoloring the light in certain areas), often also used for softened grate
111 shadows and light shining through a stained glass window (done crudely by
112 texturing the lighting with a cubemap), another good example would be a disco
113 light. This technique is used heavily in many games (Doom3 does not support
118 Terminology: Light Projection Filtering
119 A technique for modeling shadowing of light passing through translucent
120 surfaces, allowing stained glass windows and other effects to be done more
121 elegantly than possible with Light Cubemap Filtering by applying an occluder
122 texture to the lighting combined with a stencil light volume to limit the lit
123 area, this technique is used by Doom3 for spotlights and flashlights, among
124 other things, this can also be used more generally to render light passing
125 through multiple translucent occluders in a scene (using a light volume to
126 describe the area beyond the occluder, and thus mask off rendering of all
131 Terminology: Doom3 Lighting
132 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
133 CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
134 demonstrated by the game Doom3.
137 #include "quakedef.h"
138 #include "r_shadow.h"
139 #include "cl_collision.h"
143 extern void R_Shadow_EditLights_Init(void);
145 typedef enum r_shadow_rendermode_e
147 R_SHADOW_RENDERMODE_NONE,
148 R_SHADOW_RENDERMODE_STENCIL,
149 R_SHADOW_RENDERMODE_STENCILTWOSIDE,
150 R_SHADOW_RENDERMODE_LIGHT_VERTEX,
151 R_SHADOW_RENDERMODE_LIGHT_DOT3,
152 R_SHADOW_RENDERMODE_LIGHT_GLSL,
153 R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
154 R_SHADOW_RENDERMODE_VISIBLELIGHTING,
156 r_shadow_rendermode_t;
158 r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
159 r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
160 r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
162 mempool_t *r_shadow_mempool;
164 int maxshadowelements;
178 int r_shadow_buffer_numleafpvsbytes;
179 unsigned char *r_shadow_buffer_leafpvs;
180 int *r_shadow_buffer_leaflist;
182 int r_shadow_buffer_numsurfacepvsbytes;
183 unsigned char *r_shadow_buffer_surfacepvs;
184 int *r_shadow_buffer_surfacelist;
186 rtexturepool_t *r_shadow_texturepool;
187 rtexture_t *r_shadow_attenuation2dtexture;
188 rtexture_t *r_shadow_attenuation3dtexture;
190 // lights are reloaded when this changes
191 char r_shadow_mapname[MAX_QPATH];
193 // used only for light filters (cubemaps)
194 rtexturepool_t *r_shadow_filters_texturepool;
196 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
197 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
198 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
199 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
200 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
201 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
202 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
203 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
204 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
205 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
206 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
207 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
208 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
209 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal culling optimizations on dynamic lights (slow! you probably don't want this!)"};
210 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"};
211 cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1", "enables shadows from dynamic lights when using full world lighting"};
212 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"};
213 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
214 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
215 cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1", "enables compilation of shadows from world lights for higher performance rendering"};
216 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
217 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
218 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
219 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
220 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
221 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
222 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
223 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
224 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
225 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
226 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
228 float r_shadow_attenpower, r_shadow_attenscale;
230 rtlight_t *r_shadow_compilingrtlight;
231 dlight_t *r_shadow_worldlightchain;
232 dlight_t *r_shadow_selectedlight;
233 dlight_t r_shadow_bufferlight;
234 vec3_t r_editlights_cursorlocation;
236 extern int con_vislines;
238 typedef struct cubemapinfo_s
245 #define MAX_CUBEMAPS 256
246 static int numcubemaps;
247 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
249 void R_Shadow_UncompileWorldLights(void);
250 void R_Shadow_ClearWorldLights(void);
251 void R_Shadow_SaveWorldLights(void);
252 void R_Shadow_LoadWorldLights(void);
253 void R_Shadow_LoadLightsFile(void);
254 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
255 void R_Shadow_EditLights_Reload_f(void);
256 void R_Shadow_ValidateCvars(void);
257 static void R_Shadow_MakeTextures(void);
258 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
260 void r_shadow_start(void)
262 // allocate vertex processing arrays
264 r_shadow_attenuation2dtexture = NULL;
265 r_shadow_attenuation3dtexture = NULL;
266 r_shadow_texturepool = NULL;
267 r_shadow_filters_texturepool = NULL;
268 R_Shadow_ValidateCvars();
269 R_Shadow_MakeTextures();
270 maxshadowelements = 0;
271 shadowelements = NULL;
279 shadowmarklist = NULL;
281 r_shadow_buffer_numleafpvsbytes = 0;
282 r_shadow_buffer_leafpvs = NULL;
283 r_shadow_buffer_leaflist = NULL;
284 r_shadow_buffer_numsurfacepvsbytes = 0;
285 r_shadow_buffer_surfacepvs = NULL;
286 r_shadow_buffer_surfacelist = NULL;
289 void r_shadow_shutdown(void)
291 R_Shadow_UncompileWorldLights();
293 r_shadow_attenuation2dtexture = NULL;
294 r_shadow_attenuation3dtexture = NULL;
295 R_FreeTexturePool(&r_shadow_texturepool);
296 R_FreeTexturePool(&r_shadow_filters_texturepool);
297 maxshadowelements = 0;
299 Mem_Free(shadowelements);
300 shadowelements = NULL;
303 Mem_Free(vertexupdate);
306 Mem_Free(vertexremap);
312 Mem_Free(shadowmark);
315 Mem_Free(shadowmarklist);
316 shadowmarklist = NULL;
318 r_shadow_buffer_numleafpvsbytes = 0;
319 if (r_shadow_buffer_leafpvs)
320 Mem_Free(r_shadow_buffer_leafpvs);
321 r_shadow_buffer_leafpvs = NULL;
322 if (r_shadow_buffer_leaflist)
323 Mem_Free(r_shadow_buffer_leaflist);
324 r_shadow_buffer_leaflist = NULL;
325 r_shadow_buffer_numsurfacepvsbytes = 0;
326 if (r_shadow_buffer_surfacepvs)
327 Mem_Free(r_shadow_buffer_surfacepvs);
328 r_shadow_buffer_surfacepvs = NULL;
329 if (r_shadow_buffer_surfacelist)
330 Mem_Free(r_shadow_buffer_surfacelist);
331 r_shadow_buffer_surfacelist = NULL;
334 void r_shadow_newmap(void)
338 void R_Shadow_Help_f(void)
341 "Documentation on r_shadow system:\n"
343 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
344 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
345 "r_shadow_debuglight : render only this light number (-1 = all)\n"
346 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
347 "r_shadow_gloss2intensity : brightness of forced gloss\n"
348 "r_shadow_glossintensity : brightness of textured gloss\n"
349 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
350 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
351 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
352 "r_shadow_portallight : use portal visibility for static light precomputation\n"
353 "r_shadow_projectdistance : shadow volume projection distance\n"
354 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
355 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
356 "r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
357 "r_shadow_realtime_world : use high quality world lighting mode\n"
358 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
359 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
360 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
361 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
362 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
363 "r_shadow_scissor : use scissor optimization\n"
364 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
365 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
366 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
367 "r_showlighting : useful for performance testing; bright = slow!\n"
368 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
370 "r_shadow_help : this help\n"
374 void R_Shadow_Init(void)
376 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
377 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
378 Cvar_RegisterVariable(&r_shadow_debuglight);
379 Cvar_RegisterVariable(&r_shadow_gloss);
380 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
381 Cvar_RegisterVariable(&r_shadow_glossintensity);
382 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
383 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
384 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
385 Cvar_RegisterVariable(&r_shadow_portallight);
386 Cvar_RegisterVariable(&r_shadow_projectdistance);
387 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
388 Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
389 Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
390 Cvar_RegisterVariable(&r_shadow_realtime_world);
391 Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
392 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
393 Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
394 Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
395 Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
396 Cvar_RegisterVariable(&r_shadow_scissor);
397 Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
398 Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
399 Cvar_RegisterVariable(&r_shadow_texture3d);
400 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
401 if (gamemode == GAME_TENEBRAE)
403 Cvar_SetValue("r_shadow_gloss", 2);
404 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
406 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
407 R_Shadow_EditLights_Init();
408 r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
409 r_shadow_worldlightchain = NULL;
410 maxshadowelements = 0;
411 shadowelements = NULL;
419 shadowmarklist = NULL;
421 r_shadow_buffer_numleafpvsbytes = 0;
422 r_shadow_buffer_leafpvs = NULL;
423 r_shadow_buffer_leaflist = NULL;
424 r_shadow_buffer_numsurfacepvsbytes = 0;
425 r_shadow_buffer_surfacepvs = NULL;
426 r_shadow_buffer_surfacelist = NULL;
427 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
430 matrix4x4_t matrix_attenuationxyz =
433 {0.5, 0.0, 0.0, 0.5},
434 {0.0, 0.5, 0.0, 0.5},
435 {0.0, 0.0, 0.5, 0.5},
440 matrix4x4_t matrix_attenuationz =
443 {0.0, 0.0, 0.5, 0.5},
444 {0.0, 0.0, 0.0, 0.5},
445 {0.0, 0.0, 0.0, 0.5},
450 int *R_Shadow_ResizeShadowElements(int numtris)
452 // make sure shadowelements is big enough for this volume
453 if (maxshadowelements < numtris * 24)
455 maxshadowelements = numtris * 24;
457 Mem_Free(shadowelements);
458 shadowelements = (int *)Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
460 return shadowelements;
463 static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
465 int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
466 int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
467 if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
469 if (r_shadow_buffer_leafpvs)
470 Mem_Free(r_shadow_buffer_leafpvs);
471 if (r_shadow_buffer_leaflist)
472 Mem_Free(r_shadow_buffer_leaflist);
473 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
474 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
475 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
477 if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
479 if (r_shadow_buffer_surfacepvs)
480 Mem_Free(r_shadow_buffer_surfacepvs);
481 if (r_shadow_buffer_surfacelist)
482 Mem_Free(r_shadow_buffer_surfacelist);
483 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
484 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
485 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
489 void R_Shadow_PrepareShadowMark(int numtris)
491 // make sure shadowmark is big enough for this volume
492 if (maxshadowmark < numtris)
494 maxshadowmark = numtris;
496 Mem_Free(shadowmark);
498 Mem_Free(shadowmarklist);
499 shadowmark = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
500 shadowmarklist = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
504 // if shadowmarkcount wrapped we clear the array and adjust accordingly
505 if (shadowmarkcount == 0)
508 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
513 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)
516 int outtriangles = 0, outvertices = 0;
520 if (maxvertexupdate < innumvertices)
522 maxvertexupdate = innumvertices;
524 Mem_Free(vertexupdate);
526 Mem_Free(vertexremap);
527 vertexupdate = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
528 vertexremap = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
532 if (vertexupdatenum == 0)
535 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
536 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
539 for (i = 0;i < numshadowmarktris;i++)
540 shadowmark[shadowmarktris[i]] = shadowmarkcount;
542 for (i = 0;i < numshadowmarktris;i++)
544 element = inelement3i + shadowmarktris[i] * 3;
545 // make sure the vertices are created
546 for (j = 0;j < 3;j++)
548 if (vertexupdate[element[j]] != vertexupdatenum)
550 float ratio, direction[3];
551 vertexupdate[element[j]] = vertexupdatenum;
552 vertexremap[element[j]] = outvertices;
553 vertex = invertex3f + element[j] * 3;
554 // project one copy of the vertex to the sphere radius of the light
555 // (FIXME: would projecting it to the light box be better?)
556 VectorSubtract(vertex, projectorigin, direction);
557 ratio = projectdistance / VectorLength(direction);
558 VectorCopy(vertex, outvertex3f);
559 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
566 for (i = 0;i < numshadowmarktris;i++)
568 int remappedelement[3];
570 const int *neighbortriangle;
572 markindex = shadowmarktris[i] * 3;
573 element = inelement3i + markindex;
574 neighbortriangle = inneighbor3i + markindex;
575 // output the front and back triangles
576 outelement3i[0] = vertexremap[element[0]];
577 outelement3i[1] = vertexremap[element[1]];
578 outelement3i[2] = vertexremap[element[2]];
579 outelement3i[3] = vertexremap[element[2]] + 1;
580 outelement3i[4] = vertexremap[element[1]] + 1;
581 outelement3i[5] = vertexremap[element[0]] + 1;
585 // output the sides (facing outward from this triangle)
586 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
588 remappedelement[0] = vertexremap[element[0]];
589 remappedelement[1] = vertexremap[element[1]];
590 outelement3i[0] = remappedelement[1];
591 outelement3i[1] = remappedelement[0];
592 outelement3i[2] = remappedelement[0] + 1;
593 outelement3i[3] = remappedelement[1];
594 outelement3i[4] = remappedelement[0] + 1;
595 outelement3i[5] = remappedelement[1] + 1;
600 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
602 remappedelement[1] = vertexremap[element[1]];
603 remappedelement[2] = vertexremap[element[2]];
604 outelement3i[0] = remappedelement[2];
605 outelement3i[1] = remappedelement[1];
606 outelement3i[2] = remappedelement[1] + 1;
607 outelement3i[3] = remappedelement[2];
608 outelement3i[4] = remappedelement[1] + 1;
609 outelement3i[5] = remappedelement[2] + 1;
614 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
616 remappedelement[0] = vertexremap[element[0]];
617 remappedelement[2] = vertexremap[element[2]];
618 outelement3i[0] = remappedelement[0];
619 outelement3i[1] = remappedelement[2];
620 outelement3i[2] = remappedelement[2] + 1;
621 outelement3i[3] = remappedelement[0];
622 outelement3i[4] = remappedelement[2] + 1;
623 outelement3i[5] = remappedelement[0] + 1;
630 *outnumvertices = outvertices;
634 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)
637 if (projectdistance < 0.1)
639 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
642 if (!numverts || !nummarktris)
644 // make sure shadowelements is big enough for this volume
645 if (maxshadowelements < nummarktris * 24)
646 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
647 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
648 renderstats.lights_dynamicshadowtriangles += tris;
649 R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
652 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
657 if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
659 tend = firsttriangle + numtris;
660 if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
661 && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
662 && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
664 // surface box entirely inside light box, no box cull
665 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
666 if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
667 shadowmarklist[numshadowmark++] = t;
671 // surface box not entirely inside light box, cull each triangle
672 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
674 v[0] = invertex3f + e[0] * 3;
675 v[1] = invertex3f + e[1] * 3;
676 v[2] = invertex3f + e[2] * 3;
677 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
678 && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
679 && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
680 && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
681 && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
682 && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
683 && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
684 shadowmarklist[numshadowmark++] = t;
689 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
692 if (r_shadow_compilingrtlight)
694 // if we're compiling an rtlight, capture the mesh
695 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
698 renderstats.lights_shadowtriangles += numtriangles;
699 memset(&m, 0, sizeof(m));
700 m.pointer_vertex = vertex3f;
702 GL_LockArrays(0, numvertices);
703 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
705 // decrement stencil if backface is behind depthbuffer
706 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
707 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
708 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
709 // increment stencil if frontface is behind depthbuffer
710 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
711 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
713 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
717 static void R_Shadow_MakeTextures(void)
720 float v[3], intensity;
722 R_FreeTexturePool(&r_shadow_texturepool);
723 r_shadow_texturepool = R_AllocTexturePool();
724 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
725 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
726 #define ATTEN2DSIZE 64
727 #define ATTEN3DSIZE 32
728 data = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
729 for (y = 0;y < ATTEN2DSIZE;y++)
731 for (x = 0;x < ATTEN2DSIZE;x++)
733 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
734 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
736 intensity = 1.0f - sqrt(DotProduct(v, v));
738 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
739 d = bound(0, intensity, 255);
740 data[(y*ATTEN2DSIZE+x)*4+0] = d;
741 data[(y*ATTEN2DSIZE+x)*4+1] = d;
742 data[(y*ATTEN2DSIZE+x)*4+2] = d;
743 data[(y*ATTEN2DSIZE+x)*4+3] = d;
746 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
747 if (r_shadow_texture3d.integer)
749 for (z = 0;z < ATTEN3DSIZE;z++)
751 for (y = 0;y < ATTEN3DSIZE;y++)
753 for (x = 0;x < ATTEN3DSIZE;x++)
755 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
756 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
757 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
758 intensity = 1.0f - sqrt(DotProduct(v, v));
760 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
761 d = bound(0, intensity, 255);
762 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
763 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
764 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
765 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
769 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
774 void R_Shadow_ValidateCvars(void)
776 if (r_shadow_texture3d.integer && !gl_texture3d)
777 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
778 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
779 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
782 // light currently being rendered
783 rtlight_t *r_shadow_rtlight;
785 // this is the location of the eye in entity space
786 vec3_t r_shadow_entityeyeorigin;
787 // this is the location of the light in entity space
788 vec3_t r_shadow_entitylightorigin;
789 // this transforms entity coordinates to light filter cubemap coordinates
790 // (also often used for other purposes)
791 matrix4x4_t r_shadow_entitytolight;
792 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
793 // of attenuation texturing in full 3D (Z result often ignored)
794 matrix4x4_t r_shadow_entitytoattenuationxyz;
795 // this transforms only the Z to S, and T is always 0.5
796 matrix4x4_t r_shadow_entitytoattenuationz;
798 void R_Shadow_RenderMode_Begin(void)
802 R_Shadow_ValidateCvars();
804 if (!r_shadow_attenuation2dtexture
805 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
806 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
807 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
808 R_Shadow_MakeTextures();
810 memset(&m, 0, sizeof(m));
812 GL_BlendFunc(GL_ONE, GL_ZERO);
815 GL_Color(0, 0, 0, 1);
816 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
817 qglEnable(GL_CULL_FACE);
818 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
820 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
822 if (gl_ext_stenciltwoside.integer)
823 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
825 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
827 if (r_glsl.integer && gl_support_fragment_shader)
828 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
829 else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
830 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
832 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
835 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
837 r_shadow_rtlight = rtlight;
840 void R_Shadow_RenderMode_Reset(void)
843 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
845 qglUseProgramObjectARB(0);
846 // HACK HACK HACK: work around for bug in NVIDIAI 6xxx drivers that causes GL_OUT_OF_MEMORY and/or software rendering
847 qglBegin(GL_TRIANGLES);
851 else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
852 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
853 memset(&m, 0, sizeof(m));
857 void R_Shadow_RenderMode_StencilShadowVolumes(void)
859 R_Shadow_RenderMode_Reset();
860 GL_Color(1, 1, 1, 1);
861 GL_ColorMask(0, 0, 0, 0);
862 GL_BlendFunc(GL_ONE, GL_ZERO);
866 qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
867 //if (r_shadow_shadow_polygonoffset.value != 0)
869 // qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
870 // qglEnable(GL_POLYGON_OFFSET_FILL);
873 // qglDisable(GL_POLYGON_OFFSET_FILL);
874 qglDepthFunc(GL_LESS);
875 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
876 qglEnable(GL_STENCIL_TEST);
877 qglStencilFunc(GL_ALWAYS, 128, ~0);
878 r_shadow_rendermode = r_shadow_shadowingrendermode;
879 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
881 qglDisable(GL_CULL_FACE);
882 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
883 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
885 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
886 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
888 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
892 qglEnable(GL_CULL_FACE);
894 // this is changed by every shadow render so its value here is unimportant
895 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
897 GL_Clear(GL_STENCIL_BUFFER_BIT);
898 renderstats.lights_clears++;
901 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
903 R_Shadow_RenderMode_Reset();
904 GL_BlendFunc(GL_ONE, GL_ONE);
908 qglPolygonOffset(0, 0);
909 //qglDisable(GL_POLYGON_OFFSET_FILL);
910 GL_Color(1, 1, 1, 1);
911 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
913 qglDepthFunc(GL_LEQUAL);
915 qglDepthFunc(GL_EQUAL);
916 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
917 qglEnable(GL_CULL_FACE);
919 qglEnable(GL_STENCIL_TEST);
921 qglDisable(GL_STENCIL_TEST);
923 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
924 // only draw light where this geometry was already rendered AND the
925 // stencil is 128 (values other than this mean shadow)
926 qglStencilFunc(GL_EQUAL, 128, ~0);
927 r_shadow_rendermode = r_shadow_lightingrendermode;
928 // do global setup needed for the chosen lighting mode
929 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
931 R_Mesh_VertexPointer(varray_vertex3f);
932 R_Mesh_TexCoordPointer(0, 2, varray_texcoord2f[0]);
933 R_Mesh_TexCoordPointer(1, 3, varray_svector3f);
934 R_Mesh_TexCoordPointer(2, 3, varray_tvector3f);
935 R_Mesh_TexCoordPointer(3, 3, varray_normal3f);
936 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
937 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
938 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
939 R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap)); // light filter
940 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
941 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
942 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
943 //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
944 GL_BlendFunc(GL_ONE, GL_ONE);
945 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
950 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
952 R_Shadow_RenderMode_Reset();
953 GL_BlendFunc(GL_ONE, GL_ONE);
955 GL_DepthTest(!r_showdisabledepthtest.integer);
957 qglPolygonOffset(0, 0);
958 GL_Color(0.0, 0.0125, 0.1, 1);
959 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
960 qglDepthFunc(GL_GEQUAL);
961 qglCullFace(GL_FRONT); // this culls back
962 qglDisable(GL_CULL_FACE);
963 qglDisable(GL_STENCIL_TEST);
964 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
967 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
969 R_Shadow_RenderMode_Reset();
970 GL_BlendFunc(GL_ONE, GL_ONE);
972 GL_DepthTest(!r_showdisabledepthtest.integer);
974 qglPolygonOffset(0, 0);
975 GL_Color(0.1, 0.0125, 0, 1);
976 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
978 qglDepthFunc(GL_LEQUAL);
980 qglDepthFunc(GL_EQUAL);
981 qglCullFace(GL_FRONT); // this culls back
982 qglEnable(GL_CULL_FACE);
984 qglEnable(GL_STENCIL_TEST);
986 qglDisable(GL_STENCIL_TEST);
987 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
990 void R_Shadow_RenderMode_End(void)
992 R_Shadow_RenderMode_Reset();
993 R_Shadow_RenderMode_ActiveLight(NULL);
994 GL_BlendFunc(GL_ONE, GL_ZERO);
998 qglPolygonOffset(0, 0);
999 //qglDisable(GL_POLYGON_OFFSET_FILL);
1000 GL_Color(1, 1, 1, 1);
1001 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1002 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1003 qglDepthFunc(GL_LEQUAL);
1004 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1005 qglEnable(GL_CULL_FACE);
1006 qglDisable(GL_STENCIL_TEST);
1007 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1008 if (gl_support_stenciltwoside)
1009 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1011 qglStencilFunc(GL_ALWAYS, 128, ~0);
1012 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1015 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1017 int i, ix1, iy1, ix2, iy2;
1018 float x1, y1, x2, y2;
1021 mplane_t planes[11];
1022 float vertex3f[256*3];
1024 // if view is inside the light box, just say yes it's visible
1025 if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1027 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1031 // create a temporary brush describing the area the light can affect in worldspace
1032 VectorNegate(frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -frustum[0].dist;
1033 VectorNegate(frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -frustum[1].dist;
1034 VectorNegate(frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -frustum[2].dist;
1035 VectorNegate(frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -frustum[3].dist;
1036 VectorNegate(frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -frustum[4].dist;
1037 VectorSet (planes[ 5].normal, 1, 0, 0); planes[ 5].dist = maxs[0];
1038 VectorSet (planes[ 6].normal, -1, 0, 0); planes[ 6].dist = -mins[0];
1039 VectorSet (planes[ 7].normal, 0, 1, 0); planes[ 7].dist = maxs[1];
1040 VectorSet (planes[ 8].normal, 0, -1, 0); planes[ 8].dist = -mins[1];
1041 VectorSet (planes[ 9].normal, 0, 0, 1); planes[ 9].dist = maxs[2];
1042 VectorSet (planes[10].normal, 0, 0, -1); planes[10].dist = -mins[2];
1044 // turn the brush into a mesh
1045 memset(&mesh, 0, sizeof(rmesh_t));
1046 mesh.maxvertices = 256;
1047 mesh.vertex3f = vertex3f;
1048 mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
1049 R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
1051 // if that mesh is empty, the light is not visible at all
1052 if (!mesh.numvertices)
1055 if (!r_shadow_scissor.integer)
1058 // if that mesh is not empty, check what area of the screen it covers
1059 x1 = y1 = x2 = y2 = 0;
1061 for (i = 0;i < mesh.numvertices;i++)
1063 VectorCopy(mesh.vertex3f + i * 3, v);
1064 GL_TransformToScreen(v, v2);
1065 //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]);
1068 if (x1 > v2[0]) x1 = v2[0];
1069 if (x2 < v2[0]) x2 = v2[0];
1070 if (y1 > v2[1]) y1 = v2[1];
1071 if (y2 < v2[1]) y2 = v2[1];
1080 // now convert the scissor rectangle to integer screen coordinates
1085 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1087 // clamp it to the screen
1088 if (ix1 < r_view_x) ix1 = r_view_x;
1089 if (iy1 < r_view_y) iy1 = r_view_y;
1090 if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1091 if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1093 // if it is inside out, it's not visible
1094 if (ix2 <= ix1 || iy2 <= iy1)
1097 // the light area is visible, set up the scissor rectangle
1098 GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
1099 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1100 //qglEnable(GL_SCISSOR_TEST);
1101 renderstats.lights_scissored++;
1105 extern float *rsurface_vertex3f;
1106 extern float *rsurface_svector3f;
1107 extern float *rsurface_tvector3f;
1108 extern float *rsurface_normal3f;
1109 extern void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg, qboolean generatenormals, qboolean generatetangents);
1111 static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_t *surface, const float *diffusecolor, const float *ambientcolor)
1113 int numverts = surface->num_vertices;
1114 float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
1115 float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
1116 float *color4f = varray_color4f + 4 * surface->num_firstvertex;
1117 float dist, dot, distintensity, shadeintensity, v[3], n[3];
1118 if (r_textureunits.integer >= 3)
1120 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1122 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1123 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1124 if ((dot = DotProduct(n, v)) < 0)
1126 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1127 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
1128 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
1129 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
1132 float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1133 VectorScale(color4f, f, color4f);
1137 VectorClear(color4f);
1141 else if (r_textureunits.integer >= 2)
1143 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1145 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1146 if ((dist = fabs(v[2])) < 1)
1148 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1149 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1150 if ((dot = DotProduct(n, v)) < 0)
1152 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1153 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1154 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1155 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1159 color4f[0] = ambientcolor[0] * distintensity;
1160 color4f[1] = ambientcolor[1] * distintensity;
1161 color4f[2] = ambientcolor[2] * distintensity;
1165 float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1166 VectorScale(color4f, f, color4f);
1170 VectorClear(color4f);
1176 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1178 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1179 if ((dist = DotProduct(v, v)) < 1)
1182 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1183 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1184 if ((dot = DotProduct(n, v)) < 0)
1186 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1187 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1188 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1189 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1193 color4f[0] = ambientcolor[0] * distintensity;
1194 color4f[1] = ambientcolor[1] * distintensity;
1195 color4f[2] = ambientcolor[2] * distintensity;
1199 float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1200 VectorScale(color4f, f, color4f);
1204 VectorClear(color4f);
1210 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1211 #define USETEXMATRIX
1213 #ifndef USETEXMATRIX
1214 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1215 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1216 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1220 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1221 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1222 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1229 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1233 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1234 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1242 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)
1246 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1248 VectorSubtract(relativelightorigin, vertex3f, lightdir);
1249 // the cubemap normalizes this for us
1250 out3f[0] = DotProduct(svector3f, lightdir);
1251 out3f[1] = DotProduct(tvector3f, lightdir);
1252 out3f[2] = DotProduct(normal3f, lightdir);
1256 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)
1259 float lightdir[3], eyedir[3], halfdir[3];
1260 for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1262 VectorSubtract(relativelightorigin, vertex3f, lightdir);
1263 VectorNormalize(lightdir);
1264 VectorSubtract(relativeeyeorigin, vertex3f, eyedir);
1265 VectorNormalize(eyedir);
1266 VectorAdd(lightdir, eyedir, halfdir);
1267 // the cubemap normalizes this for us
1268 out3f[0] = DotProduct(svector3f, halfdir);
1269 out3f[1] = DotProduct(tvector3f, halfdir);
1270 out3f[2] = DotProduct(normal3f, halfdir);
1274 static void R_Shadow_RenderSurfacesLighting_VisibleLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
1276 // used to display how many times a surface is lit for level design purposes
1277 int surfacelistindex;
1279 GL_Color(0.1, 0.025, 0, 1);
1280 memset(&m, 0, sizeof(m));
1282 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1284 const msurface_t *surface = surfacelist[surfacelistindex];
1285 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, false);
1286 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1287 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle);
1288 GL_LockArrays(0, 0);
1292 static void R_Shadow_RenderSurfacesLighting_Light_GLSL(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
1294 // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1295 int surfacelistindex;
1296 R_SetupSurfaceShader(ent, texture, lightcolorbase, lightcolorpants, lightcolorshirt, basetexture, pantstexture, shirttexture, normalmaptexture, glosstexture, specularscale, dopants, doshirt);
1297 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1299 const msurface_t *surface = surfacelist[surfacelistindex];
1300 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1301 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, true);
1302 R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
1303 R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
1304 R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
1305 R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
1306 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1307 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1308 GL_LockArrays(0, 0);
1312 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1317 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1319 // colorscale accounts for how much we multiply the brightness
1322 // mult is how many times the final pass of the lighting will be
1323 // performed to get more brightness than otherwise possible.
1325 // Limit mult to 64 for sanity sake.
1326 if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1328 // 3 3D combine path (Geforce3, Radeon 8500)
1329 memset(&m, 0, sizeof(m));
1330 m.pointer_vertex = rsurface_vertex3f;
1331 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1333 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1334 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1336 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1337 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1339 m.tex[1] = R_GetTexture(basetexture);
1340 m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
1341 m.texmatrix[1] = texture->currenttexmatrix;
1342 m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1344 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1345 m.texmatrix[2] = r_shadow_entitytolight;
1347 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1348 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1350 GL_BlendFunc(GL_ONE, GL_ONE);
1352 else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1354 // 2 3D combine path (Geforce3, original Radeon)
1355 memset(&m, 0, sizeof(m));
1356 m.pointer_vertex = rsurface_vertex3f;
1357 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1359 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1360 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1362 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1363 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1365 m.tex[1] = R_GetTexture(basetexture);
1366 m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
1367 m.texmatrix[1] = texture->currenttexmatrix;
1368 GL_BlendFunc(GL_ONE, GL_ONE);
1370 else if (r_textureunits.integer >= 4 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1372 // 4 2D combine path (Geforce3, Radeon 8500)
1373 memset(&m, 0, sizeof(m));
1374 m.pointer_vertex = rsurface_vertex3f;
1375 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1377 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1378 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1380 m.pointer_texcoord[0] = varray_texcoord2f[0];
1381 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1383 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1385 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1386 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1388 m.pointer_texcoord[1] = varray_texcoord2f[1];
1389 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1391 m.tex[2] = R_GetTexture(basetexture);
1392 m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
1393 m.texmatrix[2] = texture->currenttexmatrix;
1394 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1396 m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1398 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1399 m.texmatrix[3] = r_shadow_entitytolight;
1401 m.pointer_texcoord3f[3] = varray_texcoord3f[3];
1402 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1405 GL_BlendFunc(GL_ONE, GL_ONE);
1407 else if (r_textureunits.integer >= 3 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1409 // 3 2D combine path (Geforce3, original Radeon)
1410 memset(&m, 0, sizeof(m));
1411 m.pointer_vertex = rsurface_vertex3f;
1412 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1414 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1415 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1417 m.pointer_texcoord[0] = varray_texcoord2f[0];
1418 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1420 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1422 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1423 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1425 m.pointer_texcoord[1] = varray_texcoord2f[1];
1426 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1428 m.tex[2] = R_GetTexture(basetexture);
1429 m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
1430 m.texmatrix[2] = texture->currenttexmatrix;
1431 GL_BlendFunc(GL_ONE, GL_ONE);
1435 // 2/2/2 2D combine path (any dot3 card)
1436 memset(&m, 0, sizeof(m));
1437 m.pointer_vertex = rsurface_vertex3f;
1438 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1440 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1441 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1443 m.pointer_texcoord[0] = varray_texcoord2f[0];
1444 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1446 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1448 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1449 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1451 m.pointer_texcoord[1] = varray_texcoord2f[1];
1452 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1455 GL_ColorMask(0,0,0,1);
1456 GL_BlendFunc(GL_ONE, GL_ZERO);
1457 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1458 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1459 GL_LockArrays(0, 0);
1461 memset(&m, 0, sizeof(m));
1462 m.pointer_vertex = rsurface_vertex3f;
1463 m.tex[0] = R_GetTexture(basetexture);
1464 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1465 m.texmatrix[0] = texture->currenttexmatrix;
1466 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1468 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1470 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1471 m.texmatrix[1] = r_shadow_entitytolight;
1473 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1474 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1477 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1479 // this final code is shared
1481 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1482 VectorScale(lightcolorbase, colorscale, color2);
1483 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1484 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1486 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1487 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1489 GL_LockArrays(0, 0);
1492 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1497 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1499 // colorscale accounts for how much we multiply the brightness
1502 // mult is how many times the final pass of the lighting will be
1503 // performed to get more brightness than otherwise possible.
1505 // Limit mult to 64 for sanity sake.
1506 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1508 // 3/2 3D combine path (Geforce3, Radeon 8500)
1509 memset(&m, 0, sizeof(m));
1510 m.pointer_vertex = rsurface_vertex3f;
1511 m.tex[0] = R_GetTexture(normalmaptexture);
1512 m.texcombinergb[0] = GL_REPLACE;
1513 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1514 m.texmatrix[0] = texture->currenttexmatrix;
1515 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1516 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1517 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1518 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
1519 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1521 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1522 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1524 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1525 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1528 GL_ColorMask(0,0,0,1);
1529 GL_BlendFunc(GL_ONE, GL_ZERO);
1530 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1531 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1532 GL_LockArrays(0, 0);
1534 memset(&m, 0, sizeof(m));
1535 m.pointer_vertex = rsurface_vertex3f;
1536 m.tex[0] = R_GetTexture(basetexture);
1537 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1538 m.texmatrix[0] = texture->currenttexmatrix;
1539 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1541 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1543 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1544 m.texmatrix[1] = r_shadow_entitytolight;
1546 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1547 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1550 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1552 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1554 // 1/2/2 3D combine path (original Radeon)
1555 memset(&m, 0, sizeof(m));
1556 m.pointer_vertex = rsurface_vertex3f;
1557 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1559 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1560 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1562 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1563 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1566 GL_ColorMask(0,0,0,1);
1567 GL_BlendFunc(GL_ONE, GL_ZERO);
1568 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1569 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1570 GL_LockArrays(0, 0);
1572 memset(&m, 0, sizeof(m));
1573 m.pointer_vertex = rsurface_vertex3f;
1574 m.tex[0] = R_GetTexture(normalmaptexture);
1575 m.texcombinergb[0] = GL_REPLACE;
1576 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1577 m.texmatrix[0] = texture->currenttexmatrix;
1578 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1579 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1580 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1581 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
1583 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1584 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1585 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1586 GL_LockArrays(0, 0);
1588 memset(&m, 0, sizeof(m));
1589 m.pointer_vertex = rsurface_vertex3f;
1590 m.tex[0] = R_GetTexture(basetexture);
1591 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1592 m.texmatrix[0] = texture->currenttexmatrix;
1593 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1595 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1597 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1598 m.texmatrix[1] = r_shadow_entitytolight;
1600 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1601 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1604 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1606 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1608 // 2/2 3D combine path (original Radeon)
1609 memset(&m, 0, sizeof(m));
1610 m.pointer_vertex = rsurface_vertex3f;
1611 m.tex[0] = R_GetTexture(normalmaptexture);
1612 m.texcombinergb[0] = GL_REPLACE;
1613 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1614 m.texmatrix[0] = texture->currenttexmatrix;
1615 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1616 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1617 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1618 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
1620 GL_ColorMask(0,0,0,1);
1621 GL_BlendFunc(GL_ONE, GL_ZERO);
1622 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1623 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1624 GL_LockArrays(0, 0);
1626 memset(&m, 0, sizeof(m));
1627 m.pointer_vertex = rsurface_vertex3f;
1628 m.tex[0] = R_GetTexture(basetexture);
1629 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1630 m.texmatrix[0] = texture->currenttexmatrix;
1631 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1633 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1634 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1636 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1637 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1639 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1641 else if (r_textureunits.integer >= 4)
1643 // 4/2 2D combine path (Geforce3, Radeon 8500)
1644 memset(&m, 0, sizeof(m));
1645 m.pointer_vertex = rsurface_vertex3f;
1646 m.tex[0] = R_GetTexture(normalmaptexture);
1647 m.texcombinergb[0] = GL_REPLACE;
1648 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1649 m.texmatrix[0] = texture->currenttexmatrix;
1650 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1651 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1652 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1653 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
1654 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1656 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1657 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1659 m.pointer_texcoord[2] = varray_texcoord2f[2];
1660 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1662 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1664 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1665 m.texmatrix[3] = r_shadow_entitytoattenuationz;
1667 m.pointer_texcoord[3] = varray_texcoord2f[3];
1668 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[3] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1671 GL_ColorMask(0,0,0,1);
1672 GL_BlendFunc(GL_ONE, GL_ZERO);
1673 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1674 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1675 GL_LockArrays(0, 0);
1677 memset(&m, 0, sizeof(m));
1678 m.pointer_vertex = rsurface_vertex3f;
1679 m.tex[0] = R_GetTexture(basetexture);
1680 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1681 m.texmatrix[0] = texture->currenttexmatrix;
1682 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1684 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1686 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1687 m.texmatrix[1] = r_shadow_entitytolight;
1689 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1690 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1693 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1697 // 2/2/2 2D combine path (any dot3 card)
1698 memset(&m, 0, sizeof(m));
1699 m.pointer_vertex = rsurface_vertex3f;
1700 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1702 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1703 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1705 m.pointer_texcoord[0] = varray_texcoord2f[0];
1706 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1708 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1710 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1711 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1713 m.pointer_texcoord[1] = varray_texcoord2f[1];
1714 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1717 GL_ColorMask(0,0,0,1);
1718 GL_BlendFunc(GL_ONE, GL_ZERO);
1719 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1720 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1721 GL_LockArrays(0, 0);
1723 memset(&m, 0, sizeof(m));
1724 m.pointer_vertex = rsurface_vertex3f;
1725 m.tex[0] = R_GetTexture(normalmaptexture);
1726 m.texcombinergb[0] = GL_REPLACE;
1727 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1728 m.texmatrix[0] = texture->currenttexmatrix;
1729 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1730 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1731 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1732 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin);
1734 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1735 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1736 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1737 GL_LockArrays(0, 0);
1739 memset(&m, 0, sizeof(m));
1740 m.pointer_vertex = rsurface_vertex3f;
1741 m.tex[0] = R_GetTexture(basetexture);
1742 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1743 m.texmatrix[0] = texture->currenttexmatrix;
1744 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1746 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1748 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1749 m.texmatrix[1] = r_shadow_entitytolight;
1751 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1752 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1755 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1757 // this final code is shared
1759 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1760 VectorScale(lightcolorbase, colorscale, color2);
1761 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1762 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1764 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1765 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1767 GL_LockArrays(0, 0);
1770 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1775 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1776 // FIXME: detect blendsquare!
1777 //if (!gl_support_blendsquare)
1780 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1782 // 2/0/0/1/2 3D combine blendsquare path
1783 memset(&m, 0, sizeof(m));
1784 m.pointer_vertex = rsurface_vertex3f;
1785 m.tex[0] = R_GetTexture(normalmaptexture);
1786 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1787 m.texmatrix[0] = texture->currenttexmatrix;
1788 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1789 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1790 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1791 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
1793 GL_ColorMask(0,0,0,1);
1794 // this squares the result
1795 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1796 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1797 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1798 GL_LockArrays(0, 0);
1800 memset(&m, 0, sizeof(m));
1801 m.pointer_vertex = rsurface_vertex3f;
1803 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1804 // square alpha in framebuffer a few times to make it shiny
1805 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1806 // these comments are a test run through this math for intensity 0.5
1807 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1808 // 0.25 * 0.25 = 0.0625 (this is another pass)
1809 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1810 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1811 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1812 GL_LockArrays(0, 0);
1814 memset(&m, 0, sizeof(m));
1815 m.pointer_vertex = rsurface_vertex3f;
1816 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1818 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1819 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1821 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1822 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1825 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1826 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1827 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1828 GL_LockArrays(0, 0);
1830 memset(&m, 0, sizeof(m));
1831 m.pointer_vertex = rsurface_vertex3f;
1832 m.tex[0] = R_GetTexture(glosstexture);
1833 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1834 m.texmatrix[0] = texture->currenttexmatrix;
1835 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1837 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1839 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1840 m.texmatrix[1] = r_shadow_entitytolight;
1842 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1843 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1846 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1848 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1850 // 2/0/0/2 3D combine blendsquare path
1851 memset(&m, 0, sizeof(m));
1852 m.pointer_vertex = rsurface_vertex3f;
1853 m.tex[0] = R_GetTexture(normalmaptexture);
1854 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1855 m.texmatrix[0] = texture->currenttexmatrix;
1856 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1857 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1858 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1859 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
1861 GL_ColorMask(0,0,0,1);
1862 // this squares the result
1863 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1864 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1865 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1866 GL_LockArrays(0, 0);
1868 memset(&m, 0, sizeof(m));
1869 m.pointer_vertex = rsurface_vertex3f;
1871 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1872 // square alpha in framebuffer a few times to make it shiny
1873 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1874 // these comments are a test run through this math for intensity 0.5
1875 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1876 // 0.25 * 0.25 = 0.0625 (this is another pass)
1877 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1878 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1879 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1880 GL_LockArrays(0, 0);
1882 memset(&m, 0, sizeof(m));
1883 m.pointer_vertex = rsurface_vertex3f;
1884 m.tex[0] = R_GetTexture(glosstexture);
1885 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1886 m.texmatrix[0] = texture->currenttexmatrix;
1887 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1889 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1890 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1892 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1893 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1895 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1899 // 2/0/0/2/2 2D combine blendsquare path
1900 memset(&m, 0, sizeof(m));
1901 m.pointer_vertex = rsurface_vertex3f;
1902 m.tex[0] = R_GetTexture(normalmaptexture);
1903 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1904 m.texmatrix[0] = texture->currenttexmatrix;
1905 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1906 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1907 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1908 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, rsurface_svector3f + 3 * surface->num_firstvertex, rsurface_tvector3f + 3 * surface->num_firstvertex, rsurface_normal3f + 3 * surface->num_firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
1910 GL_ColorMask(0,0,0,1);
1911 // this squares the result
1912 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1913 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1914 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1915 GL_LockArrays(0, 0);
1917 memset(&m, 0, sizeof(m));
1918 m.pointer_vertex = rsurface_vertex3f;
1920 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1921 // square alpha in framebuffer a few times to make it shiny
1922 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1923 // these comments are a test run through this math for intensity 0.5
1924 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1925 // 0.25 * 0.25 = 0.0625 (this is another pass)
1926 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1927 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1928 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1929 GL_LockArrays(0, 0);
1931 memset(&m, 0, sizeof(m));
1932 m.pointer_vertex = rsurface_vertex3f;
1933 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1935 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1936 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1938 m.pointer_texcoord[0] = varray_texcoord2f[0];
1939 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[0] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
1941 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1943 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1944 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1946 m.pointer_texcoord[1] = varray_texcoord2f[1];
1947 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
1950 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1951 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1952 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1953 GL_LockArrays(0, 0);
1955 memset(&m, 0, sizeof(m));
1956 m.pointer_vertex = rsurface_vertex3f;
1957 m.tex[0] = R_GetTexture(glosstexture);
1958 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1959 m.texmatrix[0] = texture->currenttexmatrix;
1960 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1962 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1964 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1965 m.texmatrix[1] = r_shadow_entitytolight;
1967 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1968 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytolight);
1971 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1974 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1975 VectorScale(lightcolorbase, colorscale, color2);
1976 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1977 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1979 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1980 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1982 GL_LockArrays(0, 0);
1985 static void R_Shadow_RenderSurfacesLighting_Light_Dot3(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
1987 // ARB path (any Geforce, any Radeon)
1988 int surfacelistindex;
1989 qboolean doambient = r_shadow_rtlight->ambientscale > 0;
1990 qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
1991 qboolean dospecular = specularscale > 0;
1992 if (!doambient && !dodiffuse && !dospecular)
1994 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1996 const msurface_t *surface = surfacelist[surfacelistindex];
1997 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, true);
1999 R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
2001 R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
2005 R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
2007 R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
2012 R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
2014 R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
2017 R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(ent, texture, surface, lightcolorbase, glosstexture, normalmaptexture, specularscale);
2021 void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const msurface_t *surface, vec3_t diffusecolor2, vec3_t ambientcolor2)
2024 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
2025 R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(surface, diffusecolor2, ambientcolor2);
2026 for (renders = 0;renders < 64 && (ambientcolor2[0] > renders || ambientcolor2[1] > renders || ambientcolor2[2] > renders || diffusecolor2[0] > renders || diffusecolor2[1] > renders || diffusecolor2[2] > renders);renders++)
2031 // due to low fillrate on the cards this vertex lighting path is
2032 // designed for, we manually cull all triangles that do not
2033 // contain a lit vertex
2036 int newnumtriangles;
2038 int newelements[3072];
2040 newnumtriangles = 0;
2042 for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
2044 if (newnumtriangles >= 1024)
2046 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
2047 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
2048 GL_LockArrays(0, 0);
2049 newnumtriangles = 0;
2052 if (VectorLength2(varray_color4f + e[0] * 4) + VectorLength2(varray_color4f + e[1] * 4) + VectorLength2(varray_color4f + e[2] * 4) >= 0.01)
2062 if (newnumtriangles >= 1)
2064 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
2065 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
2066 GL_LockArrays(0, 0);
2072 for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
2073 if (VectorLength2(c))
2077 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
2078 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
2079 GL_LockArrays(0, 0);
2081 // now reduce the intensity for the next overbright pass
2082 for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
2084 c[0] = max(0, c[0] - 1);
2085 c[1] = max(0, c[1] - 1);
2086 c[2] = max(0, c[2] - 1);
2091 static void R_Shadow_RenderSurfacesLighting_Light_Vertex(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float specularscale, qboolean dopants, qboolean doshirt)
2093 int surfacelistindex;
2094 float ambientcolorbase[3], diffusecolorbase[3];
2095 float ambientcolorpants[3], diffusecolorpants[3];
2096 float ambientcolorshirt[3], diffusecolorshirt[3];
2098 VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2, ambientcolorbase);
2099 VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2, diffusecolorbase);
2100 VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2, ambientcolorpants);
2101 VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2, diffusecolorpants);
2102 VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
2103 VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, diffusecolorshirt);
2104 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2105 memset(&m, 0, sizeof(m));
2106 m.tex[0] = R_GetTexture(basetexture);
2107 if (r_textureunits.integer >= 2)
2110 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2112 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2114 m.pointer_texcoord[1] = varray_texcoord2f[1];
2115 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
2117 if (r_textureunits.integer >= 3)
2119 // Geforce3/Radeon class but not using dot3
2120 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2122 m.texmatrix[2] = r_shadow_entitytoattenuationz;
2124 m.pointer_texcoord[2] = varray_texcoord2f[2];
2125 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
2129 m.pointer_color = varray_color4f;
2131 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2133 const msurface_t *surface = surfacelist[surfacelistindex];
2134 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, true, false);
2135 // OpenGL 1.1 path (anything)
2136 R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
2137 R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
2138 if (r_textureunits.integer >= 2)
2142 R_Mesh_TexCoordPointer(1, 3, rsurface_vertex3f);
2144 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[1] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationxyz);
2146 if (r_textureunits.integer >= 3)
2148 // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2150 R_Mesh_TexCoordPointer(2, 3, rsurface_vertex3f);
2152 R_Shadow_Transform_Vertex3f_Texcoord2f(varray_texcoord2f[2] + 3 * surface->num_firstvertex, surface->num_vertices, rsurface_vertex3f + 3 * surface->num_firstvertex, &r_shadow_entitytoattenuationz);
2156 R_Mesh_TexBind(0, R_GetTexture(basetexture));
2157 R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorbase, ambientcolorbase);
2160 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2161 R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorpants, ambientcolorpants);
2165 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2166 R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorshirt, ambientcolorshirt);
2171 void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist)
2173 // FIXME: support MATERIALFLAG_NODEPTHTEST
2174 vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2175 rtexture_t *basetexture;
2176 rtexture_t *pantstexture;
2177 rtexture_t *shirttexture;
2178 rtexture_t *glosstexture;
2179 float specularscale;
2180 qboolean dopants, doshirt;
2181 glosstexture = r_texture_black;
2183 if (r_shadow_gloss.integer > 0)
2185 if (texture->skin.gloss)
2187 if (r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
2189 glosstexture = texture->skin.gloss;
2190 specularscale = r_shadow_rtlight->specularscale * r_shadow_glossintensity.value;
2195 if (r_shadow_gloss.integer >= 2 && r_shadow_gloss2intensity.value > 0 && r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
2197 glosstexture = r_texture_white;
2198 specularscale = r_shadow_rtlight->specularscale * r_shadow_gloss2intensity.value;
2202 // calculate colors to render this texture with
2203 lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * ent->colormod[0] * texture->currentalpha;
2204 lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * ent->colormod[1] * texture->currentalpha;
2205 lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * ent->colormod[2] * texture->currentalpha;
2206 if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2208 if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
2209 qglDisable(GL_CULL_FACE);
2211 qglEnable(GL_CULL_FACE);
2212 dopants = texture->skin.pants != NULL && VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f);
2213 doshirt = texture->skin.shirt != NULL && VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
2214 if (dopants + doshirt)
2218 lightcolorpants[0] = lightcolorbase[0] * ent->colormap_pantscolor[0];
2219 lightcolorpants[1] = lightcolorbase[1] * ent->colormap_pantscolor[1];
2220 lightcolorpants[2] = lightcolorbase[2] * ent->colormap_pantscolor[2];
2224 pantstexture = r_texture_black;
2225 VectorClear(lightcolorpants);
2229 shirttexture = texture->skin.shirt;
2230 lightcolorshirt[0] = lightcolorbase[0] * ent->colormap_shirtcolor[0];
2231 lightcolorshirt[1] = lightcolorbase[1] * ent->colormap_shirtcolor[1];
2232 lightcolorshirt[2] = lightcolorbase[2] * ent->colormap_shirtcolor[2];
2236 shirttexture = r_texture_black;
2237 VectorClear(lightcolorshirt);
2239 switch (r_shadow_rendermode)
2241 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2242 R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale, dopants, doshirt);
2244 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2245 R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale, dopants, doshirt);
2247 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2248 R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale, dopants, doshirt);
2250 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2251 R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->skin.base, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, glosstexture, specularscale, dopants, doshirt);
2254 Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2260 basetexture = texture->skin.merged ? texture->skin.merged : texture->skin.base;
2261 switch (r_shadow_rendermode)
2263 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2264 R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale, false, false);
2266 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2267 R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale, false, false);
2269 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2270 R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale, false, false);
2272 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2273 R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, basetexture, r_texture_black, r_texture_black, texture->skin.nmap, glosstexture, specularscale, false, false);
2276 Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2282 void R_RTLight_Update(dlight_t *light, int isstatic)
2286 rtlight_t *rtlight = &light->rtlight;
2287 R_RTLight_Uncompile(rtlight);
2288 memset(rtlight, 0, sizeof(*rtlight));
2290 VectorCopy(light->origin, rtlight->shadoworigin);
2291 VectorCopy(light->color, rtlight->color);
2292 rtlight->radius = light->radius;
2293 //rtlight->cullradius = rtlight->radius;
2294 //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2295 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2296 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2297 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2298 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2299 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2300 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2301 rtlight->cubemapname[0] = 0;
2302 if (light->cubemapname[0])
2303 strcpy(rtlight->cubemapname, light->cubemapname);
2304 else if (light->cubemapnum > 0)
2305 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2306 rtlight->shadow = light->shadow;
2307 rtlight->corona = light->corona;
2308 rtlight->style = light->style;
2309 rtlight->isstatic = isstatic;
2310 rtlight->coronasizescale = light->coronasizescale;
2311 rtlight->ambientscale = light->ambientscale;
2312 rtlight->diffusescale = light->diffusescale;
2313 rtlight->specularscale = light->specularscale;
2314 rtlight->flags = light->flags;
2315 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2316 // ConcatScale won't work here because this needs to scale rotate and
2317 // translate, not just rotate
2318 scale = 1.0f / rtlight->radius;
2319 for (k = 0;k < 3;k++)
2320 for (j = 0;j < 4;j++)
2321 rtlight->matrix_worldtolight.m[k][j] *= scale;
2323 rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
2324 rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
2325 VectorScale(rtlight->color, rtlight->radius * (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * 0.125f, rtlight->lightmap_light);
2326 rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
2329 // compiles rtlight geometry
2330 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2331 void R_RTLight_Compile(rtlight_t *rtlight)
2333 int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
2334 entity_render_t *ent = r_refdef.worldentity;
2335 model_t *model = r_refdef.worldmodel;
2336 unsigned char *data;
2338 // compile the light
2339 rtlight->compiled = true;
2340 rtlight->static_numleafs = 0;
2341 rtlight->static_numleafpvsbytes = 0;
2342 rtlight->static_leaflist = NULL;
2343 rtlight->static_leafpvs = NULL;
2344 rtlight->static_numsurfaces = 0;
2345 rtlight->static_surfacelist = NULL;
2346 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2347 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2348 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2349 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2350 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2351 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2353 if (model && model->GetLightInfo)
2355 // this variable must be set for the CompileShadowVolume code
2356 r_shadow_compilingrtlight = rtlight;
2357 R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
2358 model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2359 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2360 data = (unsigned char *)Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
2361 rtlight->static_numleafs = numleafs;
2362 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2363 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2364 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2365 rtlight->static_numsurfaces = numsurfaces;
2366 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2368 memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2369 if (numleafpvsbytes)
2370 memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2372 memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2373 if (model->CompileShadowVolume && rtlight->shadow)
2374 model->CompileShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2375 // now we're done compiling the rtlight
2376 r_shadow_compilingrtlight = NULL;
2380 // use smallest available cullradius - box radius or light radius
2381 //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2382 //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2386 if (rtlight->static_meshchain_shadow)
2389 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2392 shadowtris += mesh->numtriangles;
2396 Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume 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);
2399 void R_RTLight_Uncompile(rtlight_t *rtlight)
2401 if (rtlight->compiled)
2403 if (rtlight->static_meshchain_shadow)
2404 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2405 rtlight->static_meshchain_shadow = NULL;
2406 // these allocations are grouped
2407 if (rtlight->static_leaflist)
2408 Mem_Free(rtlight->static_leaflist);
2409 rtlight->static_numleafs = 0;
2410 rtlight->static_numleafpvsbytes = 0;
2411 rtlight->static_leaflist = NULL;
2412 rtlight->static_leafpvs = NULL;
2413 rtlight->static_numsurfaces = 0;
2414 rtlight->static_surfacelist = NULL;
2415 rtlight->compiled = false;
2419 void R_Shadow_UncompileWorldLights(void)
2422 for (light = r_shadow_worldlightchain;light;light = light->next)
2423 R_RTLight_Uncompile(&light->rtlight);
2426 void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfacelist)
2428 vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2429 vec_t relativeshadowradius;
2430 if (ent == r_refdef.worldentity)
2432 if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2435 R_Mesh_Matrix(&ent->matrix);
2436 for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2438 renderstats.lights_shadowtriangles += mesh->numtriangles;
2439 R_Mesh_VertexPointer(mesh->vertex3f);
2440 GL_LockArrays(0, mesh->numverts);
2441 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2443 // decrement stencil if backface is behind depthbuffer
2444 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2445 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2446 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2447 // increment stencil if frontface is behind depthbuffer
2448 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2449 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2451 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2452 GL_LockArrays(0, 0);
2455 else if (numsurfaces)
2457 R_Mesh_Matrix(&ent->matrix);
2458 ent->model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
2463 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
2464 relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
2465 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2466 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2467 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2468 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2469 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2470 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2471 R_Mesh_Matrix(&ent->matrix);
2472 ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2476 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2478 // set up properties for rendering light onto this entity
2479 Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
2480 Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2481 Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2482 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2483 Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
2484 R_Mesh_Matrix(&ent->matrix);
2487 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2489 R_Shadow_SetupEntityLight(ent);
2490 if (ent == r_refdef.worldentity)
2491 ent->model->DrawLight(ent, numsurfaces, surfacelist);
2493 ent->model->DrawLight(ent, ent->model->nummodelsurfaces, ent->model->surfacelist);
2496 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2500 int numleafs, numsurfaces;
2501 int *leaflist, *surfacelist;
2502 unsigned char *leafpvs;
2503 int numlightentities;
2504 int numshadowentities;
2505 entity_render_t *lightentities[MAX_EDICTS];
2506 entity_render_t *shadowentities[MAX_EDICTS];
2508 // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2509 // skip lights that are basically invisible (color 0 0 0)
2510 if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2513 // loading is done before visibility checks because loading should happen
2514 // all at once at the start of a level, not when it stalls gameplay.
2515 // (especially important to benchmarks)
2517 if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2518 R_RTLight_Compile(rtlight);
2520 rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2522 // look up the light style value at this time
2523 f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2524 VectorScale(rtlight->color, f, rtlight->currentcolor);
2526 if (rtlight->selected)
2528 f = 2 + sin(realtime * M_PI * 4.0);
2529 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2533 // if lightstyle is currently off, don't draw the light
2534 if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2537 // if the light box is offscreen, skip it
2538 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2541 if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2543 // compiled light, world available and can receive realtime lighting
2544 // retrieve leaf information
2545 numleafs = rtlight->static_numleafs;
2546 leaflist = rtlight->static_leaflist;
2547 leafpvs = rtlight->static_leafpvs;
2548 numsurfaces = rtlight->static_numsurfaces;
2549 surfacelist = rtlight->static_surfacelist;
2551 else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2553 // dynamic light, world available and can receive realtime lighting
2554 // calculate lit surfaces and leafs
2555 R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
2556 r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2557 leaflist = r_shadow_buffer_leaflist;
2558 leafpvs = r_shadow_buffer_leafpvs;
2559 surfacelist = r_shadow_buffer_surfacelist;
2560 // if the reduced leaf bounds are offscreen, skip it
2561 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2573 // check if light is illuminating any visible leafs
2576 for (i = 0;i < numleafs;i++)
2577 if (r_worldleafvisible[leaflist[i]])
2582 // set up a scissor rectangle for this light
2583 if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2586 // make a list of lit entities and shadow casting entities
2587 numlightentities = 0;
2588 numshadowentities = 0;
2589 // don't count the world unless some surfaces are actually lit
2592 lightentities[numlightentities++] = r_refdef.worldentity;
2593 shadowentities[numshadowentities++] = r_refdef.worldentity;
2595 // add dynamic entities that are lit by the light
2596 if (r_drawentities.integer)
2598 for (i = 0;i < r_refdef.numentities;i++)
2600 entity_render_t *ent = r_refdef.entities[i];
2601 if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2603 && !(ent->flags & RENDER_TRANSPARENT)
2604 && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
2606 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2607 if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
2608 shadowentities[numshadowentities++] = ent;
2609 if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
2610 lightentities[numlightentities++] = ent;
2615 // return if there's nothing at all to light
2616 if (!numlightentities)
2619 // don't let sound skip if going slow
2620 if (r_refdef.extraupdate)
2623 // make this the active rtlight for rendering purposes
2624 R_Shadow_RenderMode_ActiveLight(rtlight);
2625 // count this light in the r_speeds
2626 renderstats.lights++;
2629 if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
2631 // draw stencil shadow volumes to mask off pixels that are in shadow
2632 // so that they won't receive lighting
2636 R_Shadow_RenderMode_StencilShadowVolumes();
2637 for (i = 0;i < numshadowentities;i++)
2638 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2641 // optionally draw visible shape of the shadow volumes
2642 // for performance analysis by level designers
2643 if (r_showshadowvolumes.integer)
2645 R_Shadow_RenderMode_VisibleShadowVolumes();
2646 for (i = 0;i < numshadowentities;i++)
2647 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2651 if (numlightentities)
2653 // draw lighting in the unmasked areas
2654 R_Shadow_RenderMode_Lighting(usestencil, false);
2655 for (i = 0;i < numlightentities;i++)
2656 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2658 // optionally draw the illuminated areas
2659 // for performance analysis by level designers
2660 if (r_showlighting.integer)
2662 R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
2663 for (i = 0;i < numlightentities;i++)
2664 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2669 void R_ShadowVolumeLighting(qboolean visible)
2674 if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2675 R_Shadow_EditLights_Reload_f();
2677 R_Shadow_RenderMode_Begin();
2679 flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2680 if (r_shadow_debuglight.integer >= 0)
2682 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2683 if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2684 R_DrawRTLight(&light->rtlight, visible);
2687 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2688 if (light->flags & flag)
2689 R_DrawRTLight(&light->rtlight, visible);
2691 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
2692 R_DrawRTLight(&r_refdef.lights[lnum]->rtlight, visible);
2694 R_Shadow_RenderMode_End();
2697 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2698 typedef struct suffixinfo_s
2701 qboolean flipx, flipy, flipdiagonal;
2704 static suffixinfo_t suffix[3][6] =
2707 {"px", false, false, false},
2708 {"nx", false, false, false},
2709 {"py", false, false, false},
2710 {"ny", false, false, false},
2711 {"pz", false, false, false},
2712 {"nz", false, false, false}
2715 {"posx", false, false, false},
2716 {"negx", false, false, false},
2717 {"posy", false, false, false},
2718 {"negy", false, false, false},
2719 {"posz", false, false, false},
2720 {"negz", false, false, false}
2723 {"rt", true, false, true},
2724 {"lf", false, true, true},
2725 {"ft", true, true, false},
2726 {"bk", false, false, false},
2727 {"up", true, false, true},
2728 {"dn", true, false, true}
2732 static int componentorder[4] = {0, 1, 2, 3};
2734 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2736 int i, j, cubemapsize;
2737 unsigned char *cubemappixels, *image_rgba;
2738 rtexture_t *cubemaptexture;
2740 // must start 0 so the first loadimagepixels has no requested width/height
2742 cubemappixels = NULL;
2743 cubemaptexture = NULL;
2744 // keep trying different suffix groups (posx, px, rt) until one loads
2745 for (j = 0;j < 3 && !cubemappixels;j++)
2747 // load the 6 images in the suffix group
2748 for (i = 0;i < 6;i++)
2750 // generate an image name based on the base and and suffix
2751 dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2753 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2755 // an image loaded, make sure width and height are equal
2756 if (image_width == image_height)
2758 // if this is the first image to load successfully, allocate the cubemap memory
2759 if (!cubemappixels && image_width >= 1)
2761 cubemapsize = image_width;
2762 // note this clears to black, so unavailable sides are black
2763 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2765 // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2767 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);
2770 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2772 Mem_Free(image_rgba);
2776 // if a cubemap loaded, upload it
2779 if (!r_shadow_filters_texturepool)
2780 r_shadow_filters_texturepool = R_AllocTexturePool();
2781 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2782 Mem_Free(cubemappixels);
2786 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2787 for (j = 0;j < 3;j++)
2788 for (i = 0;i < 6;i++)
2789 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2790 Con_Print(" and was unable to find any of them.\n");
2792 return cubemaptexture;
2795 rtexture_t *R_Shadow_Cubemap(const char *basename)
2798 for (i = 0;i < numcubemaps;i++)
2799 if (!strcasecmp(cubemaps[i].basename, basename))
2800 return cubemaps[i].texture;
2801 if (i >= MAX_CUBEMAPS)
2802 return r_texture_whitecube;
2804 strcpy(cubemaps[i].basename, basename);
2805 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2806 if (!cubemaps[i].texture)
2807 cubemaps[i].texture = r_texture_whitecube;
2808 return cubemaps[i].texture;
2811 void R_Shadow_FreeCubemaps(void)
2814 R_FreeTexturePool(&r_shadow_filters_texturepool);
2817 dlight_t *R_Shadow_NewWorldLight(void)
2820 light = (dlight_t *)Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2821 light->next = r_shadow_worldlightchain;
2822 r_shadow_worldlightchain = light;
2826 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)
2828 VectorCopy(origin, light->origin);
2829 light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2830 light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2831 light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2832 light->color[0] = max(color[0], 0);
2833 light->color[1] = max(color[1], 0);
2834 light->color[2] = max(color[2], 0);
2835 light->radius = max(radius, 0);
2836 light->style = style;
2837 if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2839 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2842 light->shadow = shadowenable;
2843 light->corona = corona;
2846 strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
2847 light->coronasizescale = coronasizescale;
2848 light->ambientscale = ambientscale;
2849 light->diffusescale = diffusescale;
2850 light->specularscale = specularscale;
2851 light->flags = flags;
2852 Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2854 R_RTLight_Update(light, true);
2857 void R_Shadow_FreeWorldLight(dlight_t *light)
2859 dlight_t **lightpointer;
2860 R_RTLight_Uncompile(&light->rtlight);
2861 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2862 if (*lightpointer != light)
2863 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
2864 *lightpointer = light->next;
2868 void R_Shadow_ClearWorldLights(void)
2870 while (r_shadow_worldlightchain)
2871 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2872 r_shadow_selectedlight = NULL;
2873 R_Shadow_FreeCubemaps();
2876 void R_Shadow_SelectLight(dlight_t *light)
2878 if (r_shadow_selectedlight)
2879 r_shadow_selectedlight->selected = false;
2880 r_shadow_selectedlight = light;
2881 if (r_shadow_selectedlight)
2882 r_shadow_selectedlight->selected = true;
2885 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2887 float scale = r_editlights_cursorgrid.value * 0.5f;
2888 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[0]->tex, NULL, false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2891 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2894 const dlight_t *light = (dlight_t *)ent;
2896 if (light->selected)
2897 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2900 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[surfacenumber]->tex, NULL, false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2903 void R_Shadow_DrawLightSprites(void)
2908 for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
2909 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
2910 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
2913 void R_Shadow_SelectLightInView(void)
2915 float bestrating, rating, temp[3];
2916 dlight_t *best, *light;
2919 for (light = r_shadow_worldlightchain;light;light = light->next)
2921 VectorSubtract(light->origin, r_vieworigin, temp);
2922 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2925 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2926 if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_vieworigin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
2928 bestrating = rating;
2933 R_Shadow_SelectLight(best);
2936 void R_Shadow_LoadWorldLights(void)
2938 int n, a, style, shadow, flags;
2939 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
2940 float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
2941 if (r_refdef.worldmodel == NULL)
2943 Con_Print("No map loaded.\n");
2946 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
2947 strlcat (name, ".rtlights", sizeof (name));
2948 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
2958 for (;COM_Parse(t, true) && strcmp(
2959 if (COM_Parse(t, true))
2961 if (com_token[0] == '!')
2964 origin[0] = atof(com_token+1);
2967 origin[0] = atof(com_token);
2972 while (*s && *s != '\n' && *s != '\r')
2978 // check for modifier flags
2985 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);
2988 flags = LIGHTFLAG_REALTIMEMODE;
2996 coronasizescale = 0.25f;
2998 VectorClear(angles);
3001 if (a < 9 || !strcmp(cubemapname, "\"\""))
3003 // remove quotes on cubemapname
3004 if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3006 cubemapname[strlen(cubemapname)-1] = 0;
3007 strcpy(cubemapname, cubemapname + 1);
3011 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);
3014 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3022 Con_Printf("invalid rtlights file \"%s\"\n", name);
3023 Mem_Free(lightsstring);
3027 void R_Shadow_SaveWorldLights(void)
3030 size_t bufchars, bufmaxchars;
3032 char name[MAX_QPATH];
3033 char line[MAX_INPUTLINE];
3034 if (!r_shadow_worldlightchain)
3036 if (r_refdef.worldmodel == NULL)
3038 Con_Print("No map loaded.\n");
3041 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3042 strlcat (name, ".rtlights", sizeof (name));
3043 bufchars = bufmaxchars = 0;
3045 for (light = r_shadow_worldlightchain;light;light = light->next)
3047 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3048 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, light->color[0], light->color[1], light->color[2], 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);
3049 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3050 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, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
3052 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, light->color[0], light->color[1], light->color[2], light->style);
3053 if (bufchars + strlen(line) > bufmaxchars)
3055 bufmaxchars = bufchars + strlen(line) + 2048;
3057 buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3061 memcpy(buf, oldbuf, bufchars);
3067 memcpy(buf + bufchars, line, strlen(line));
3068 bufchars += strlen(line);
3072 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3077 void R_Shadow_LoadLightsFile(void)
3080 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3081 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3082 if (r_refdef.worldmodel == NULL)
3084 Con_Print("No map loaded.\n");
3087 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3088 strlcat (name, ".lights", sizeof (name));
3089 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3097 while (*s && *s != '\n' && *s != '\r')
3103 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);
3107 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);
3110 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3111 radius = bound(15, radius, 4096);
3112 VectorScale(color, (2.0f / (8388608.0f)), color);
3113 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3121 Con_Printf("invalid lights file \"%s\"\n", name);
3122 Mem_Free(lightsstring);
3126 // tyrlite/hmap2 light types in the delay field
3127 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3129 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3131 int entnum, style, islight, skin, pflags, effects, type, n;
3134 float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3135 char key[256], value[MAX_INPUTLINE];
3137 if (r_refdef.worldmodel == NULL)
3139 Con_Print("No map loaded.\n");
3142 // try to load a .ent file first
3143 FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3144 strlcat (key, ".ent", sizeof (key));
3145 data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3146 // and if that is not found, fall back to the bsp file entity string
3148 data = r_refdef.worldmodel->brush.entities;
3151 for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
3153 type = LIGHTTYPE_MINUSX;
3154 origin[0] = origin[1] = origin[2] = 0;
3155 originhack[0] = originhack[1] = originhack[2] = 0;
3156 angles[0] = angles[1] = angles[2] = 0;
3157 color[0] = color[1] = color[2] = 1;
3158 light[0] = light[1] = light[2] = 1;light[3] = 300;
3159 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3169 if (!COM_ParseToken(&data, false))
3171 if (com_token[0] == '}')
3172 break; // end of entity
3173 if (com_token[0] == '_')
3174 strcpy(key, com_token + 1);
3176 strcpy(key, com_token);
3177 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3178 key[strlen(key)-1] = 0;
3179 if (!COM_ParseToken(&data, false))
3181 strcpy(value, com_token);
3183 // now that we have the key pair worked out...
3184 if (!strcmp("light", key))
3186 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3190 light[0] = vec[0] * (1.0f / 256.0f);
3191 light[1] = vec[0] * (1.0f / 256.0f);
3192 light[2] = vec[0] * (1.0f / 256.0f);
3198 light[0] = vec[0] * (1.0f / 255.0f);
3199 light[1] = vec[1] * (1.0f / 255.0f);
3200 light[2] = vec[2] * (1.0f / 255.0f);
3204 else if (!strcmp("delay", key))
3206 else if (!strcmp("origin", key))
3207 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3208 else if (!strcmp("angle", key))
3209 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3210 else if (!strcmp("angles", key))
3211 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3212 else if (!strcmp("color", key))
3213 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3214 else if (!strcmp("wait", key))
3215 fadescale = atof(value);
3216 else if (!strcmp("classname", key))
3218 if (!strncmp(value, "light", 5))
3221 if (!strcmp(value, "light_fluoro"))
3226 overridecolor[0] = 1;
3227 overridecolor[1] = 1;
3228 overridecolor[2] = 1;
3230 if (!strcmp(value, "light_fluorospark"))
3235 overridecolor[0] = 1;
3236 overridecolor[1] = 1;
3237 overridecolor[2] = 1;
3239 if (!strcmp(value, "light_globe"))
3244 overridecolor[0] = 1;
3245 overridecolor[1] = 0.8;
3246 overridecolor[2] = 0.4;
3248 if (!strcmp(value, "light_flame_large_yellow"))
3253 overridecolor[0] = 1;
3254 overridecolor[1] = 0.5;
3255 overridecolor[2] = 0.1;
3257 if (!strcmp(value, "light_flame_small_yellow"))
3262 overridecolor[0] = 1;
3263 overridecolor[1] = 0.5;
3264 overridecolor[2] = 0.1;
3266 if (!strcmp(value, "light_torch_small_white"))
3271 overridecolor[0] = 1;
3272 overridecolor[1] = 0.5;
3273 overridecolor[2] = 0.1;
3275 if (!strcmp(value, "light_torch_small_walltorch"))
3280 overridecolor[0] = 1;
3281 overridecolor[1] = 0.5;
3282 overridecolor[2] = 0.1;
3286 else if (!strcmp("style", key))
3287 style = atoi(value);
3288 else if (!strcmp("skin", key))
3289 skin = (int)atof(value);
3290 else if (!strcmp("pflags", key))
3291 pflags = (int)atof(value);
3292 else if (!strcmp("effects", key))
3293 effects = (int)atof(value);
3294 else if (r_refdef.worldmodel->type == mod_brushq3)
3296 if (!strcmp("scale", key))
3297 lightscale = atof(value);
3298 if (!strcmp("fade", key))
3299 fadescale = atof(value);
3304 if (lightscale <= 0)
3308 if (color[0] == color[1] && color[0] == color[2])
3310 color[0] *= overridecolor[0];
3311 color[1] *= overridecolor[1];
3312 color[2] *= overridecolor[2];
3314 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3315 color[0] = color[0] * light[0];
3316 color[1] = color[1] * light[1];
3317 color[2] = color[2] * light[2];
3320 case LIGHTTYPE_MINUSX:
3322 case LIGHTTYPE_RECIPX:
3324 VectorScale(color, (1.0f / 16.0f), color);
3326 case LIGHTTYPE_RECIPXX:
3328 VectorScale(color, (1.0f / 16.0f), color);
3331 case LIGHTTYPE_NONE:
3335 case LIGHTTYPE_MINUSXX:
3338 VectorAdd(origin, originhack, origin);
3340 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);
3343 Mem_Free(entfiledata);
3347 void R_Shadow_SetCursorLocationForView(void)
3350 vec3_t dest, endpos;
3352 VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
3353 trace = CL_TraceBox(r_vieworigin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
3354 if (trace.fraction < 1)
3356 dist = trace.fraction * r_editlights_cursordistance.value;
3357 push = r_editlights_cursorpushback.value;
3361 VectorMA(trace.endpos, push, r_viewforward, endpos);
3362 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
3366 VectorClear( endpos );
3368 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3369 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3370 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3373 void R_Shadow_UpdateWorldLightSelection(void)
3375 if (r_editlights.integer)
3377 R_Shadow_SetCursorLocationForView();
3378 R_Shadow_SelectLightInView();
3379 R_Shadow_DrawLightSprites();
3382 R_Shadow_SelectLight(NULL);
3385 void R_Shadow_EditLights_Clear_f(void)
3387 R_Shadow_ClearWorldLights();
3390 void R_Shadow_EditLights_Reload_f(void)
3392 if (!r_refdef.worldmodel)
3394 strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
3395 R_Shadow_ClearWorldLights();
3396 R_Shadow_LoadWorldLights();
3397 if (r_shadow_worldlightchain == NULL)
3399 R_Shadow_LoadLightsFile();
3400 if (r_shadow_worldlightchain == NULL)
3401 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3405 void R_Shadow_EditLights_Save_f(void)
3407 if (!r_refdef.worldmodel)
3409 R_Shadow_SaveWorldLights();
3412 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3414 R_Shadow_ClearWorldLights();
3415 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3418 void R_Shadow_EditLights_ImportLightsFile_f(void)
3420 R_Shadow_ClearWorldLights();
3421 R_Shadow_LoadLightsFile();
3424 void R_Shadow_EditLights_Spawn_f(void)
3427 if (!r_editlights.integer)
3429 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3432 if (Cmd_Argc() != 1)
3434 Con_Print("r_editlights_spawn does not take parameters\n");
3437 color[0] = color[1] = color[2] = 1;
3438 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3441 void R_Shadow_EditLights_Edit_f(void)
3443 vec3_t origin, angles, color;
3444 vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3445 int style, shadows, flags, normalmode, realtimemode;
3446 char cubemapname[MAX_INPUTLINE];
3447 if (!r_editlights.integer)
3449 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3452 if (!r_shadow_selectedlight)
3454 Con_Print("No selected light.\n");
3457 VectorCopy(r_shadow_selectedlight->origin, origin);
3458 VectorCopy(r_shadow_selectedlight->angles, angles);
3459 VectorCopy(r_shadow_selectedlight->color, color);
3460 radius = r_shadow_selectedlight->radius;
3461 style = r_shadow_selectedlight->style;
3462 if (r_shadow_selectedlight->cubemapname)
3463 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
3466 shadows = r_shadow_selectedlight->shadow;
3467 corona = r_shadow_selectedlight->corona;
3468 coronasizescale = r_shadow_selectedlight->coronasizescale;
3469 ambientscale = r_shadow_selectedlight->ambientscale;
3470 diffusescale = r_shadow_selectedlight->diffusescale;
3471 specularscale = r_shadow_selectedlight->specularscale;
3472 flags = r_shadow_selectedlight->flags;
3473 normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3474 realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3475 if (!strcmp(Cmd_Argv(1), "origin"))
3477 if (Cmd_Argc() != 5)
3479 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3482 origin[0] = atof(Cmd_Argv(2));
3483 origin[1] = atof(Cmd_Argv(3));
3484 origin[2] = atof(Cmd_Argv(4));
3486 else if (!strcmp(Cmd_Argv(1), "originx"))
3488 if (Cmd_Argc() != 3)
3490 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3493 origin[0] = atof(Cmd_Argv(2));
3495 else if (!strcmp(Cmd_Argv(1), "originy"))
3497 if (Cmd_Argc() != 3)
3499 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3502 origin[1] = atof(Cmd_Argv(2));
3504 else if (!strcmp(Cmd_Argv(1), "originz"))
3506 if (Cmd_Argc() != 3)
3508 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3511 origin[2] = atof(Cmd_Argv(2));
3513 else if (!strcmp(Cmd_Argv(1), "move"))
3515 if (Cmd_Argc() != 5)
3517 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3520 origin[0] += atof(Cmd_Argv(2));
3521 origin[1] += atof(Cmd_Argv(3));
3522 origin[2] += atof(Cmd_Argv(4));
3524 else if (!strcmp(Cmd_Argv(1), "movex"))
3526 if (Cmd_Argc() != 3)
3528 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3531 origin[0] += atof(Cmd_Argv(2));
3533 else if (!strcmp(Cmd_Argv(1), "movey"))
3535 if (Cmd_Argc() != 3)
3537 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3540 origin[1] += atof(Cmd_Argv(2));
3542 else if (!strcmp(Cmd_Argv(1), "movez"))
3544 if (Cmd_Argc() != 3)
3546 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3549 origin[2] += atof(Cmd_Argv(2));
3551 else if (!strcmp(Cmd_Argv(1), "angles"))
3553 if (Cmd_Argc() != 5)
3555 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3558 angles[0] = atof(Cmd_Argv(2));
3559 angles[1] = atof(Cmd_Argv(3));
3560 angles[2] = atof(Cmd_Argv(4));
3562 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3564 if (Cmd_Argc() != 3)
3566 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3569 angles[0] = atof(Cmd_Argv(2));
3571 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3573 if (Cmd_Argc() != 3)
3575 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3578 angles[1] = atof(Cmd_Argv(2));
3580 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3582 if (Cmd_Argc() != 3)
3584 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3587 angles[2] = atof(Cmd_Argv(2));
3589 else if (!strcmp(Cmd_Argv(1), "color"))
3591 if (Cmd_Argc() != 5)
3593 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3596 color[0] = atof(Cmd_Argv(2));
3597 color[1] = atof(Cmd_Argv(3));
3598 color[2] = atof(Cmd_Argv(4));
3600 else if (!strcmp(Cmd_Argv(1), "radius"))
3602 if (Cmd_Argc() != 3)
3604 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3607 radius = atof(Cmd_Argv(2));
3609 else if (!strcmp(Cmd_Argv(1), "colorscale"))
3611 if (Cmd_Argc() == 3)
3613 double scale = atof(Cmd_Argv(2));
3620 if (Cmd_Argc() != 5)
3622 Con_Printf("usage: r_editlights_edit %s red green blue (OR grey instead of red green blue)\n", Cmd_Argv(1));
3625 color[0] *= atof(Cmd_Argv(2));
3626 color[1] *= atof(Cmd_Argv(3));
3627 color[2] *= atof(Cmd_Argv(4));
3630 else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
3632 if (Cmd_Argc() != 3)
3634 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3637 radius *= atof(Cmd_Argv(2));
3639 else if (!strcmp(Cmd_Argv(1), "style"))
3641 if (Cmd_Argc() != 3)
3643 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3646 style = atoi(Cmd_Argv(2));
3648 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3652 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3655 if (Cmd_Argc() == 3)
3656 strcpy(cubemapname, Cmd_Argv(2));
3660 else if (!strcmp(Cmd_Argv(1), "shadows"))
3662 if (Cmd_Argc() != 3)
3664 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3667 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3669 else if (!strcmp(Cmd_Argv(1), "corona"))
3671 if (Cmd_Argc() != 3)
3673 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3676 corona = atof(Cmd_Argv(2));
3678 else if (!strcmp(Cmd_Argv(1), "coronasize"))
3680 if (Cmd_Argc() != 3)
3682 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3685 coronasizescale = atof(Cmd_Argv(2));
3687 else if (!strcmp(Cmd_Argv(1), "ambient"))
3689 if (Cmd_Argc() != 3)
3691 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3694 ambientscale = atof(Cmd_Argv(2));
3696 else if (!strcmp(Cmd_Argv(1), "diffuse"))
3698 if (Cmd_Argc() != 3)
3700 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3703 diffusescale = atof(Cmd_Argv(2));
3705 else if (!strcmp(Cmd_Argv(1), "specular"))
3707 if (Cmd_Argc() != 3)
3709 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3712 specularscale = atof(Cmd_Argv(2));
3714 else if (!strcmp(Cmd_Argv(1), "normalmode"))
3716 if (Cmd_Argc() != 3)
3718 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3721 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3723 else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3725 if (Cmd_Argc() != 3)
3727 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3730 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3734 Con_Print("usage: r_editlights_edit [property] [value]\n");
3735 Con_Print("Selected light's properties:\n");
3736 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3737 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3738 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3739 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3740 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3741 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3742 Con_Printf("Shadows : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3743 Con_Printf("Cubemap : %s\n", r_shadow_selectedlight->cubemapname);
3744 Con_Printf("CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);
3745 Con_Printf("Ambient : %f\n", r_shadow_selectedlight->ambientscale);
3746 Con_Printf("Diffuse : %f\n", r_shadow_selectedlight->diffusescale);
3747 Con_Printf("Specular : %f\n", r_shadow_selectedlight->specularscale);
3748 Con_Printf("NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
3749 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
3752 flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
3753 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3756 void R_Shadow_EditLights_EditAll_f(void)
3760 if (!r_editlights.integer)
3762 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
3766 for (light = r_shadow_worldlightchain;light;light = light->next)
3768 R_Shadow_SelectLight(light);
3769 R_Shadow_EditLights_Edit_f();
3773 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3775 int lightnumber, lightcount;
3779 if (!r_editlights.integer)
3785 for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
3786 if (light == r_shadow_selectedlight)
3787 lightnumber = lightcount;
3788 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;
3789 if (r_shadow_selectedlight == NULL)
3791 sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3792 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;
3793 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;
3794 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;
3795 sprintf(temp, "Radius : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3796 sprintf(temp, "Corona : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3797 sprintf(temp, "Style : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3798 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;
3799 sprintf(temp, "Cubemap : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3800 sprintf(temp, "CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3801 sprintf(temp, "Ambient : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3802 sprintf(temp, "Diffuse : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3803 sprintf(temp, "Specular : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3804 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;
3805 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;
3808 void R_Shadow_EditLights_ToggleShadow_f(void)
3810 if (!r_editlights.integer)
3812 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3815 if (!r_shadow_selectedlight)
3817 Con_Print("No selected light.\n");
3820 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);
3823 void R_Shadow_EditLights_ToggleCorona_f(void)
3825 if (!r_editlights.integer)
3827 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3830 if (!r_shadow_selectedlight)
3832 Con_Print("No selected light.\n");
3835 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);
3838 void R_Shadow_EditLights_Remove_f(void)
3840 if (!r_editlights.integer)
3842 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3845 if (!r_shadow_selectedlight)
3847 Con_Print("No selected light.\n");
3850 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3851 r_shadow_selectedlight = NULL;
3854 void R_Shadow_EditLights_Help_f(void)
3857 "Documentation on r_editlights system:\n"
3859 "r_editlights : enable/disable editing mode\n"
3860 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3861 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3862 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3863 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3864 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3866 "r_editlights_help : this help\n"
3867 "r_editlights_clear : remove all lights\n"
3868 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3869 "r_editlights_save : save to .rtlights file\n"
3870 "r_editlights_spawn : create a light with default settings\n"
3871 "r_editlights_edit command : edit selected light - more documentation below\n"
3872 "r_editlights_remove : remove selected light\n"
3873 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3874 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3875 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3877 "origin x y z : set light location\n"
3878 "originx x: set x component of light location\n"
3879 "originy y: set y component of light location\n"
3880 "originz z: set z component of light location\n"
3881 "move x y z : adjust light location\n"
3882 "movex x: adjust x component of light location\n"
3883 "movey y: adjust y component of light location\n"
3884 "movez z: adjust z component of light location\n"
3885 "angles x y z : set light angles\n"
3886 "anglesx x: set x component of light angles\n"
3887 "anglesy y: set y component of light angles\n"
3888 "anglesz z: set z component of light angles\n"
3889 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3890 "radius radius : set radius (size) of light\n"
3891 "colorscale grey : multiply color of light (1 does nothing)\n"
3892 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
3893 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
3894 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
3895 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3896 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3897 "shadows 1/0 : turn on/off shadows\n"
3898 "corona n : set corona intensity\n"
3899 "coronasize n : set corona size (0-1)\n"
3900 "ambient n : set ambient intensity (0-1)\n"
3901 "diffuse n : set diffuse intensity (0-1)\n"
3902 "specular n : set specular intensity (0-1)\n"
3903 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
3904 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
3905 "<nothing> : print light properties to console\n"
3909 void R_Shadow_EditLights_CopyInfo_f(void)
3911 if (!r_editlights.integer)
3913 Con_Print("Cannot copy light info when not in editing mode. Set r_editlights to 1.\n");
3916 if (!r_shadow_selectedlight)
3918 Con_Print("No selected light.\n");
3921 VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
3922 VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
3923 r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
3924 r_shadow_bufferlight.style = r_shadow_selectedlight->style;
3925 if (r_shadow_selectedlight->cubemapname)
3926 strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
3928 r_shadow_bufferlight.cubemapname[0] = 0;
3929 r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
3930 r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
3931 r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
3932 r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
3933 r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
3934 r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
3935 r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
3938 void R_Shadow_EditLights_PasteInfo_f(void)
3940 if (!r_editlights.integer)
3942 Con_Print("Cannot paste light info when not in editing mode. Set r_editlights to 1.\n");
3945 if (!r_shadow_selectedlight)
3947 Con_Print("No selected light.\n");
3950 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);
3953 void R_Shadow_EditLights_Init(void)
3955 Cvar_RegisterVariable(&r_editlights);
3956 Cvar_RegisterVariable(&r_editlights_cursordistance);
3957 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3958 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3959 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3960 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3961 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
3962 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
3963 Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f, "reloads rtlights file (or imports from .lights file or .ent file or the map itself)");
3964 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
3965 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
3966 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
3967 Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f, "changes a property on ALL lights at once (tip: use radiusscale and colorscale to alter these properties)");
3968 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
3969 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
3970 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
3971 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
3972 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
3973 Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
3974 Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f, "apply the stored properties onto the selected light (making it exactly identical except for origin)");