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_SEPARATESTENCIL,
150 R_SHADOW_RENDERMODE_STENCILTWOSIDE,
151 R_SHADOW_RENDERMODE_LIGHT_VERTEX,
152 R_SHADOW_RENDERMODE_LIGHT_DOT3,
153 R_SHADOW_RENDERMODE_LIGHT_GLSL,
154 R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
155 R_SHADOW_RENDERMODE_VISIBLELIGHTING,
157 r_shadow_rendermode_t;
159 r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
160 r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
161 r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
163 int maxshadowtriangles;
166 int maxshadowvertices;
167 float *shadowvertex3f;
180 int r_shadow_buffer_numleafpvsbytes;
181 unsigned char *r_shadow_buffer_leafpvs;
182 int *r_shadow_buffer_leaflist;
184 int r_shadow_buffer_numsurfacepvsbytes;
185 unsigned char *r_shadow_buffer_surfacepvs;
186 int *r_shadow_buffer_surfacelist;
188 int r_shadow_buffer_numshadowtrispvsbytes;
189 unsigned char *r_shadow_buffer_shadowtrispvs;
190 int r_shadow_buffer_numlighttrispvsbytes;
191 unsigned char *r_shadow_buffer_lighttrispvs;
193 // current light's cull box (copied out of an rtlight or calculated by GetLightInfo)
194 vec3_t r_shadow_rtlight_cullmins;
195 vec3_t r_shadow_rtlight_cullmaxs;
197 rtexturepool_t *r_shadow_texturepool;
198 rtexture_t *r_shadow_attenuation2dtexture;
199 rtexture_t *r_shadow_attenuation3dtexture;
201 // lights are reloaded when this changes
202 char r_shadow_mapname[MAX_QPATH];
204 // used only for light filters (cubemaps)
205 rtexturepool_t *r_shadow_filters_texturepool;
207 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"};
208 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"};
209 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
210 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)"};
211 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.125", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
212 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
213 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
214 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
215 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
216 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
217 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
218 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
219 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
220 cvar_t r_shadow_frontsidecasting = {0, "r_shadow_frontsidecasting", "1", "whether to cast shadows from illuminated triangles (front side of model) or unlit triangles (back side of model)"};
221 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
222 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
223 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
224 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
225 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)"};
226 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"};
227 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"};
228 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
229 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
230 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"};
231 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
232 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
233 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)"};
234 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
235 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
236 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)"};
237 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)"};
238 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatetencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
239 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
240 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
241 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
242 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
243 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
244 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
245 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
247 float r_shadow_attenpower, r_shadow_attenscale;
249 rtlight_t *r_shadow_compilingrtlight;
250 dlight_t *r_shadow_worldlightchain;
251 dlight_t *r_shadow_selectedlight;
252 dlight_t r_shadow_bufferlight;
253 vec3_t r_editlights_cursorlocation;
255 extern int con_vislines;
257 typedef struct cubemapinfo_s
264 #define MAX_CUBEMAPS 256
265 static int numcubemaps;
266 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
268 void R_Shadow_UncompileWorldLights(void);
269 void R_Shadow_ClearWorldLights(void);
270 void R_Shadow_SaveWorldLights(void);
271 void R_Shadow_LoadWorldLights(void);
272 void R_Shadow_LoadLightsFile(void);
273 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
274 void R_Shadow_EditLights_Reload_f(void);
275 void R_Shadow_ValidateCvars(void);
276 static void R_Shadow_MakeTextures(void);
278 void r_shadow_start(void)
280 // allocate vertex processing arrays
282 r_shadow_attenuation2dtexture = NULL;
283 r_shadow_attenuation3dtexture = NULL;
284 r_shadow_texturepool = NULL;
285 r_shadow_filters_texturepool = NULL;
286 R_Shadow_ValidateCvars();
287 R_Shadow_MakeTextures();
288 maxshadowtriangles = 0;
289 shadowelements = NULL;
290 maxshadowvertices = 0;
291 shadowvertex3f = NULL;
299 shadowmarklist = NULL;
301 r_shadow_buffer_numleafpvsbytes = 0;
302 r_shadow_buffer_leafpvs = NULL;
303 r_shadow_buffer_leaflist = NULL;
304 r_shadow_buffer_numsurfacepvsbytes = 0;
305 r_shadow_buffer_surfacepvs = NULL;
306 r_shadow_buffer_surfacelist = NULL;
307 r_shadow_buffer_numshadowtrispvsbytes = 0;
308 r_shadow_buffer_shadowtrispvs = NULL;
309 r_shadow_buffer_numlighttrispvsbytes = 0;
310 r_shadow_buffer_lighttrispvs = NULL;
313 void r_shadow_shutdown(void)
315 R_Shadow_UncompileWorldLights();
317 r_shadow_attenuation2dtexture = NULL;
318 r_shadow_attenuation3dtexture = NULL;
319 R_FreeTexturePool(&r_shadow_texturepool);
320 R_FreeTexturePool(&r_shadow_filters_texturepool);
321 maxshadowtriangles = 0;
323 Mem_Free(shadowelements);
324 shadowelements = NULL;
326 Mem_Free(shadowvertex3f);
327 shadowvertex3f = NULL;
330 Mem_Free(vertexupdate);
333 Mem_Free(vertexremap);
339 Mem_Free(shadowmark);
342 Mem_Free(shadowmarklist);
343 shadowmarklist = NULL;
345 r_shadow_buffer_numleafpvsbytes = 0;
346 if (r_shadow_buffer_leafpvs)
347 Mem_Free(r_shadow_buffer_leafpvs);
348 r_shadow_buffer_leafpvs = NULL;
349 if (r_shadow_buffer_leaflist)
350 Mem_Free(r_shadow_buffer_leaflist);
351 r_shadow_buffer_leaflist = NULL;
352 r_shadow_buffer_numsurfacepvsbytes = 0;
353 if (r_shadow_buffer_surfacepvs)
354 Mem_Free(r_shadow_buffer_surfacepvs);
355 r_shadow_buffer_surfacepvs = NULL;
356 if (r_shadow_buffer_surfacelist)
357 Mem_Free(r_shadow_buffer_surfacelist);
358 r_shadow_buffer_surfacelist = NULL;
359 r_shadow_buffer_numshadowtrispvsbytes = 0;
360 if (r_shadow_buffer_shadowtrispvs)
361 Mem_Free(r_shadow_buffer_shadowtrispvs);
362 r_shadow_buffer_numlighttrispvsbytes = 0;
363 if (r_shadow_buffer_lighttrispvs)
364 Mem_Free(r_shadow_buffer_lighttrispvs);
367 void r_shadow_newmap(void)
371 void R_Shadow_Help_f(void)
374 "Documentation on r_shadow system:\n"
376 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
377 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
378 "r_shadow_debuglight : render only this light number (-1 = all)\n"
379 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
380 "r_shadow_gloss2intensity : brightness of forced gloss\n"
381 "r_shadow_glossintensity : brightness of textured gloss\n"
382 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
383 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
384 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
385 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
386 "r_shadow_portallight : use portal visibility for static light precomputation\n"
387 "r_shadow_projectdistance : shadow volume projection distance\n"
388 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
389 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
390 "r_shadow_realtime_world : use high quality world lighting mode\n"
391 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
392 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
393 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
394 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
395 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
396 "r_shadow_scissor : use scissor optimization\n"
397 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
398 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
399 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
400 "r_showlighting : useful for performance testing; bright = slow!\n"
401 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
403 "r_shadow_help : this help\n"
407 void R_Shadow_Init(void)
409 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
410 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
411 Cvar_RegisterVariable(&r_shadow_debuglight);
412 Cvar_RegisterVariable(&r_shadow_gloss);
413 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
414 Cvar_RegisterVariable(&r_shadow_glossintensity);
415 Cvar_RegisterVariable(&r_shadow_glossexponent);
416 Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
417 Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
418 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
419 Cvar_RegisterVariable(&r_shadow_lightradiusscale);
420 Cvar_RegisterVariable(&r_shadow_portallight);
421 Cvar_RegisterVariable(&r_shadow_projectdistance);
422 Cvar_RegisterVariable(&r_shadow_frontsidecasting);
423 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
424 Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
425 Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
426 Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
427 Cvar_RegisterVariable(&r_shadow_realtime_world);
428 Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
429 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
430 Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
431 Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
432 Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
433 Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
434 Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
435 Cvar_RegisterVariable(&r_shadow_scissor);
436 Cvar_RegisterVariable(&r_shadow_culltriangles);
437 Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
438 Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
439 Cvar_RegisterVariable(&r_shadow_texture3d);
440 Cvar_RegisterVariable(&gl_ext_separatestencil);
441 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
442 if (gamemode == GAME_TENEBRAE)
444 Cvar_SetValue("r_shadow_gloss", 2);
445 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
447 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
448 R_Shadow_EditLights_Init();
449 r_shadow_worldlightchain = NULL;
450 maxshadowtriangles = 0;
451 shadowelements = NULL;
452 maxshadowvertices = 0;
453 shadowvertex3f = NULL;
461 shadowmarklist = NULL;
463 r_shadow_buffer_numleafpvsbytes = 0;
464 r_shadow_buffer_leafpvs = NULL;
465 r_shadow_buffer_leaflist = NULL;
466 r_shadow_buffer_numsurfacepvsbytes = 0;
467 r_shadow_buffer_surfacepvs = NULL;
468 r_shadow_buffer_surfacelist = NULL;
469 r_shadow_buffer_shadowtrispvs = NULL;
470 r_shadow_buffer_lighttrispvs = NULL;
471 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
474 matrix4x4_t matrix_attenuationxyz =
477 {0.5, 0.0, 0.0, 0.5},
478 {0.0, 0.5, 0.0, 0.5},
479 {0.0, 0.0, 0.5, 0.5},
484 matrix4x4_t matrix_attenuationz =
487 {0.0, 0.0, 0.5, 0.5},
488 {0.0, 0.0, 0.0, 0.5},
489 {0.0, 0.0, 0.0, 0.5},
494 void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
496 // make sure shadowelements is big enough for this volume
497 if (maxshadowtriangles < numtriangles)
499 maxshadowtriangles = numtriangles;
501 Mem_Free(shadowelements);
502 shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
504 // make sure shadowvertex3f is big enough for this volume
505 if (maxshadowvertices < numvertices)
507 maxshadowvertices = numvertices;
509 Mem_Free(shadowvertex3f);
510 shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
514 static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
516 int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
517 int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
518 int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
519 int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
520 if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
522 if (r_shadow_buffer_leafpvs)
523 Mem_Free(r_shadow_buffer_leafpvs);
524 if (r_shadow_buffer_leaflist)
525 Mem_Free(r_shadow_buffer_leaflist);
526 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
527 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
528 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
530 if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
532 if (r_shadow_buffer_surfacepvs)
533 Mem_Free(r_shadow_buffer_surfacepvs);
534 if (r_shadow_buffer_surfacelist)
535 Mem_Free(r_shadow_buffer_surfacelist);
536 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
537 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
538 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
540 if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
542 if (r_shadow_buffer_shadowtrispvs)
543 Mem_Free(r_shadow_buffer_shadowtrispvs);
544 r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
545 r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
547 if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
549 if (r_shadow_buffer_lighttrispvs)
550 Mem_Free(r_shadow_buffer_lighttrispvs);
551 r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
552 r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
556 void R_Shadow_PrepareShadowMark(int numtris)
558 // make sure shadowmark is big enough for this volume
559 if (maxshadowmark < numtris)
561 maxshadowmark = numtris;
563 Mem_Free(shadowmark);
565 Mem_Free(shadowmarklist);
566 shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
567 shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
571 // if shadowmarkcount wrapped we clear the array and adjust accordingly
572 if (shadowmarkcount == 0)
575 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
580 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, const float *projectdirection, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
583 int outtriangles = 0, outvertices = 0;
586 float ratio, direction[3], projectvector[3];
588 if (projectdirection)
589 VectorScale(projectdirection, projectdistance, projectvector);
591 VectorClear(projectvector);
593 if (maxvertexupdate < innumvertices)
595 maxvertexupdate = innumvertices;
597 Mem_Free(vertexupdate);
599 Mem_Free(vertexremap);
600 vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
601 vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
605 if (vertexupdatenum == 0)
608 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
609 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
612 for (i = 0;i < numshadowmarktris;i++)
613 shadowmark[shadowmarktris[i]] = shadowmarkcount;
615 // create the vertices
616 if (projectdirection)
618 for (i = 0;i < numshadowmarktris;i++)
620 element = inelement3i + shadowmarktris[i] * 3;
621 for (j = 0;j < 3;j++)
623 if (vertexupdate[element[j]] != vertexupdatenum)
625 vertexupdate[element[j]] = vertexupdatenum;
626 vertexremap[element[j]] = outvertices;
627 vertex = invertex3f + element[j] * 3;
628 // project one copy of the vertex according to projectvector
629 VectorCopy(vertex, outvertex3f);
630 VectorAdd(vertex, projectvector, (outvertex3f + 3));
639 for (i = 0;i < numshadowmarktris;i++)
641 element = inelement3i + shadowmarktris[i] * 3;
642 for (j = 0;j < 3;j++)
644 if (vertexupdate[element[j]] != vertexupdatenum)
646 vertexupdate[element[j]] = vertexupdatenum;
647 vertexremap[element[j]] = outvertices;
648 vertex = invertex3f + element[j] * 3;
649 // project one copy of the vertex to the sphere radius of the light
650 // (FIXME: would projecting it to the light box be better?)
651 VectorSubtract(vertex, projectorigin, direction);
652 ratio = projectdistance / VectorLength(direction);
653 VectorCopy(vertex, outvertex3f);
654 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
662 if (r_shadow_frontsidecasting.integer)
664 for (i = 0;i < numshadowmarktris;i++)
666 int remappedelement[3];
668 const int *neighbortriangle;
670 markindex = shadowmarktris[i] * 3;
671 element = inelement3i + markindex;
672 neighbortriangle = inneighbor3i + markindex;
673 // output the front and back triangles
674 outelement3i[0] = vertexremap[element[0]];
675 outelement3i[1] = vertexremap[element[1]];
676 outelement3i[2] = vertexremap[element[2]];
677 outelement3i[3] = vertexremap[element[2]] + 1;
678 outelement3i[4] = vertexremap[element[1]] + 1;
679 outelement3i[5] = vertexremap[element[0]] + 1;
683 // output the sides (facing outward from this triangle)
684 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
686 remappedelement[0] = vertexremap[element[0]];
687 remappedelement[1] = vertexremap[element[1]];
688 outelement3i[0] = remappedelement[1];
689 outelement3i[1] = remappedelement[0];
690 outelement3i[2] = remappedelement[0] + 1;
691 outelement3i[3] = remappedelement[1];
692 outelement3i[4] = remappedelement[0] + 1;
693 outelement3i[5] = remappedelement[1] + 1;
698 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
700 remappedelement[1] = vertexremap[element[1]];
701 remappedelement[2] = vertexremap[element[2]];
702 outelement3i[0] = remappedelement[2];
703 outelement3i[1] = remappedelement[1];
704 outelement3i[2] = remappedelement[1] + 1;
705 outelement3i[3] = remappedelement[2];
706 outelement3i[4] = remappedelement[1] + 1;
707 outelement3i[5] = remappedelement[2] + 1;
712 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
714 remappedelement[0] = vertexremap[element[0]];
715 remappedelement[2] = vertexremap[element[2]];
716 outelement3i[0] = remappedelement[0];
717 outelement3i[1] = remappedelement[2];
718 outelement3i[2] = remappedelement[2] + 1;
719 outelement3i[3] = remappedelement[0];
720 outelement3i[4] = remappedelement[2] + 1;
721 outelement3i[5] = remappedelement[0] + 1;
730 for (i = 0;i < numshadowmarktris;i++)
732 int remappedelement[3];
734 const int *neighbortriangle;
736 markindex = shadowmarktris[i] * 3;
737 element = inelement3i + markindex;
738 neighbortriangle = inneighbor3i + markindex;
739 // output the front and back triangles
740 outelement3i[0] = vertexremap[element[2]];
741 outelement3i[1] = vertexremap[element[1]];
742 outelement3i[2] = vertexremap[element[0]];
743 outelement3i[3] = vertexremap[element[0]] + 1;
744 outelement3i[4] = vertexremap[element[1]] + 1;
745 outelement3i[5] = vertexremap[element[2]] + 1;
749 // output the sides (facing outward from this triangle)
750 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
752 remappedelement[0] = vertexremap[element[0]];
753 remappedelement[1] = vertexremap[element[1]];
754 outelement3i[0] = remappedelement[0];
755 outelement3i[1] = remappedelement[1];
756 outelement3i[2] = remappedelement[1] + 1;
757 outelement3i[3] = remappedelement[0];
758 outelement3i[4] = remappedelement[1] + 1;
759 outelement3i[5] = remappedelement[0] + 1;
764 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
766 remappedelement[1] = vertexremap[element[1]];
767 remappedelement[2] = vertexremap[element[2]];
768 outelement3i[0] = remappedelement[1];
769 outelement3i[1] = remappedelement[2];
770 outelement3i[2] = remappedelement[2] + 1;
771 outelement3i[3] = remappedelement[1];
772 outelement3i[4] = remappedelement[2] + 1;
773 outelement3i[5] = remappedelement[1] + 1;
778 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
780 remappedelement[0] = vertexremap[element[0]];
781 remappedelement[2] = vertexremap[element[2]];
782 outelement3i[0] = remappedelement[2];
783 outelement3i[1] = remappedelement[0];
784 outelement3i[2] = remappedelement[0] + 1;
785 outelement3i[3] = remappedelement[2];
786 outelement3i[4] = remappedelement[0] + 1;
787 outelement3i[5] = remappedelement[2] + 1;
795 *outnumvertices = outvertices;
799 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris)
802 if (projectdistance < 0.1)
804 Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
807 if (!numverts || !nummarktris)
809 // make sure shadowelements is big enough for this volume
810 if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
811 R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
812 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
813 r_refdef.stats.lights_dynamicshadowtriangles += tris;
814 R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
817 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
823 if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
825 tend = firsttriangle + numtris;
826 if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
828 // surface box entirely inside light box, no box cull
829 if (projectdirection)
831 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
833 TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
834 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
835 shadowmarklist[numshadowmark++] = t;
840 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
841 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
842 shadowmarklist[numshadowmark++] = t;
847 // surface box not entirely inside light box, cull each triangle
848 if (projectdirection)
850 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
852 v[0] = invertex3f + e[0] * 3;
853 v[1] = invertex3f + e[1] * 3;
854 v[2] = invertex3f + e[2] * 3;
855 TriangleNormal(v[0], v[1], v[2], normal);
856 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
857 && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
858 shadowmarklist[numshadowmark++] = t;
863 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
865 v[0] = invertex3f + e[0] * 3;
866 v[1] = invertex3f + e[1] * 3;
867 v[2] = invertex3f + e[2] * 3;
868 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
869 && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
870 shadowmarklist[numshadowmark++] = t;
876 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
878 if (r_shadow_compilingrtlight)
880 // if we're compiling an rtlight, capture the mesh
881 Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
884 r_refdef.stats.lights_shadowtriangles += numtriangles;
886 R_Mesh_VertexPointer(vertex3f);
887 GL_LockArrays(0, numvertices);
888 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
890 // decrement stencil if backface is behind depthbuffer
891 GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
892 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
893 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
894 // increment stencil if frontface is behind depthbuffer
895 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
896 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
898 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
903 static void R_Shadow_MakeTextures(void)
906 float v[3], intensity;
908 R_FreeTexturePool(&r_shadow_texturepool);
909 r_shadow_texturepool = R_AllocTexturePool();
910 r_shadow_attenpower = r_shadow_lightattenuationpower.value;
911 r_shadow_attenscale = r_shadow_lightattenuationscale.value;
912 #define ATTEN2DSIZE 64
913 #define ATTEN3DSIZE 32
914 data = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
915 for (y = 0;y < ATTEN2DSIZE;y++)
917 for (x = 0;x < ATTEN2DSIZE;x++)
919 v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
920 v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
922 intensity = 1.0f - sqrt(DotProduct(v, v));
924 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
925 d = (int)bound(0, intensity, 255);
926 data[(y*ATTEN2DSIZE+x)*4+0] = d;
927 data[(y*ATTEN2DSIZE+x)*4+1] = d;
928 data[(y*ATTEN2DSIZE+x)*4+2] = d;
929 data[(y*ATTEN2DSIZE+x)*4+3] = d;
932 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
933 if (r_shadow_texture3d.integer && gl_texture3d)
935 for (z = 0;z < ATTEN3DSIZE;z++)
937 for (y = 0;y < ATTEN3DSIZE;y++)
939 for (x = 0;x < ATTEN3DSIZE;x++)
941 v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
942 v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
943 v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
944 intensity = 1.0f - sqrt(DotProduct(v, v));
946 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
947 d = (int)bound(0, intensity, 255);
948 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
949 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
950 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
951 data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
955 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
960 void R_Shadow_ValidateCvars(void)
962 if (r_shadow_texture3d.integer && !gl_texture3d)
963 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
964 if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
965 Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
966 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
967 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
970 // light currently being rendered
971 rtlight_t *r_shadow_rtlight;
973 // this is the location of the light in entity space
974 vec3_t r_shadow_entitylightorigin;
975 // this transforms entity coordinates to light filter cubemap coordinates
976 // (also often used for other purposes)
977 matrix4x4_t r_shadow_entitytolight;
978 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
979 // of attenuation texturing in full 3D (Z result often ignored)
980 matrix4x4_t r_shadow_entitytoattenuationxyz;
981 // this transforms only the Z to S, and T is always 0.5
982 matrix4x4_t r_shadow_entitytoattenuationz;
984 void R_Shadow_RenderMode_Begin(void)
986 R_Shadow_ValidateCvars();
988 if (!r_shadow_attenuation2dtexture
989 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
990 || r_shadow_lightattenuationpower.value != r_shadow_attenpower
991 || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
992 R_Shadow_MakeTextures();
995 R_Mesh_ColorPointer(NULL);
996 R_Mesh_ResetTextureState();
997 GL_BlendFunc(GL_ONE, GL_ZERO);
1000 GL_Color(0, 0, 0, 1);
1001 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1003 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1005 if (gl_ext_separatestencil.integer)
1006 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
1007 else if (gl_ext_stenciltwoside.integer)
1008 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
1010 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
1012 if (r_glsl.integer && gl_support_fragment_shader)
1013 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
1014 else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1015 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
1017 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
1020 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
1022 r_shadow_rtlight = rtlight;
1025 void R_Shadow_RenderMode_Reset(void)
1028 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1030 qglUseProgramObjectARB(0);CHECKGLERROR
1032 else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1034 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1036 R_Mesh_ColorPointer(NULL);
1037 R_Mesh_ResetTextureState();
1039 GL_DepthMask(false);
1040 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1041 qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
1042 qglDisable(GL_STENCIL_TEST);CHECKGLERROR
1043 qglStencilMask(~0);CHECKGLERROR
1044 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
1045 qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
1046 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
1047 GL_Color(1, 1, 1, 1);
1048 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
1049 GL_BlendFunc(GL_ONE, GL_ZERO);
1052 void R_Shadow_RenderMode_StencilShadowVolumes(void)
1055 R_Shadow_RenderMode_Reset();
1056 GL_ColorMask(0, 0, 0, 0);
1057 qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1058 qglDepthFunc(GL_LESS);CHECKGLERROR
1059 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1060 r_shadow_rendermode = r_shadow_shadowingrendermode;
1061 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
1063 GL_CullFace(GL_NONE);
1064 qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR // quake is backwards, this is front faces
1065 qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR // quake is backwards, this is back faces
1067 else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1069 GL_CullFace(GL_NONE);
1070 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1071 qglActiveStencilFaceEXT(GL_BACK);CHECKGLERROR // quake is backwards, this is front faces
1072 qglStencilMask(~0);CHECKGLERROR
1073 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1074 qglActiveStencilFaceEXT(GL_FRONT);CHECKGLERROR // quake is backwards, this is back faces
1075 qglStencilMask(~0);CHECKGLERROR
1076 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1078 GL_Clear(GL_STENCIL_BUFFER_BIT);
1079 r_refdef.stats.lights_clears++;
1082 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
1085 R_Shadow_RenderMode_Reset();
1086 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1089 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1093 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1094 // only draw light where this geometry was already rendered AND the
1095 // stencil is 128 (values other than this mean shadow)
1096 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1098 r_shadow_rendermode = r_shadow_lightingrendermode;
1099 // do global setup needed for the chosen lighting mode
1100 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1102 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
1103 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
1104 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
1105 R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap)); // light filter
1106 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
1107 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
1108 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
1109 R_Mesh_TexBind(7, R_GetTexture(r_texture_white)); // lightmap
1110 R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap)); // deluxemap
1111 R_Mesh_TexBind(9, R_GetTexture(r_texture_black)); // glow
1112 //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
1113 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1114 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1119 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1122 R_Shadow_RenderMode_Reset();
1123 GL_BlendFunc(GL_ONE, GL_ONE);
1124 GL_DepthTest(r_showshadowvolumes.integer < 2);
1125 GL_Color(0.0, 0.0125 * r_view.colorscale, 0.1 * r_view.colorscale, 1);
1126 qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1127 GL_CullFace(GL_NONE);
1128 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1131 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1134 R_Shadow_RenderMode_Reset();
1135 GL_BlendFunc(GL_ONE, GL_ONE);
1136 GL_DepthTest(r_showlighting.integer < 2);
1137 GL_Color(0.1 * r_view.colorscale, 0.0125 * r_view.colorscale, 0, 1);
1140 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1144 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1145 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1147 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1150 void R_Shadow_RenderMode_End(void)
1153 R_Shadow_RenderMode_Reset();
1154 R_Shadow_RenderMode_ActiveLight(NULL);
1156 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1157 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1160 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1162 int i, ix1, iy1, ix2, iy2;
1163 float x1, y1, x2, y2;
1166 mplane_t planes[11];
1167 float vertex3f[256*3];
1169 // if view is inside the light box, just say yes it's visible
1170 if (BoxesOverlap(r_view.origin, r_view.origin, mins, maxs))
1172 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1176 // create a temporary brush describing the area the light can affect in worldspace
1177 VectorNegate(r_view.frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -r_view.frustum[0].dist;
1178 VectorNegate(r_view.frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -r_view.frustum[1].dist;
1179 VectorNegate(r_view.frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -r_view.frustum[2].dist;
1180 VectorNegate(r_view.frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -r_view.frustum[3].dist;
1181 VectorNegate(r_view.frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -r_view.frustum[4].dist;
1182 VectorSet (planes[ 5].normal, 1, 0, 0); planes[ 5].dist = maxs[0];
1183 VectorSet (planes[ 6].normal, -1, 0, 0); planes[ 6].dist = -mins[0];
1184 VectorSet (planes[ 7].normal, 0, 1, 0); planes[ 7].dist = maxs[1];
1185 VectorSet (planes[ 8].normal, 0, -1, 0); planes[ 8].dist = -mins[1];
1186 VectorSet (planes[ 9].normal, 0, 0, 1); planes[ 9].dist = maxs[2];
1187 VectorSet (planes[10].normal, 0, 0, -1); planes[10].dist = -mins[2];
1189 // turn the brush into a mesh
1190 memset(&mesh, 0, sizeof(rmesh_t));
1191 mesh.maxvertices = 256;
1192 mesh.vertex3f = vertex3f;
1193 mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
1194 R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
1196 // if that mesh is empty, the light is not visible at all
1197 if (!mesh.numvertices)
1200 if (!r_shadow_scissor.integer)
1203 // if that mesh is not empty, check what area of the screen it covers
1204 x1 = y1 = x2 = y2 = 0;
1206 //Con_Printf("%i vertices to transform...\n", mesh.numvertices);
1207 for (i = 0;i < mesh.numvertices;i++)
1209 VectorCopy(mesh.vertex3f + i * 3, v);
1210 GL_TransformToScreen(v, v2);
1211 //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]);
1214 if (x1 > v2[0]) x1 = v2[0];
1215 if (x2 < v2[0]) x2 = v2[0];
1216 if (y1 > v2[1]) y1 = v2[1];
1217 if (y2 < v2[1]) y2 = v2[1];
1226 // now convert the scissor rectangle to integer screen coordinates
1227 ix1 = (int)(x1 - 1.0f);
1228 iy1 = (int)(y1 - 1.0f);
1229 ix2 = (int)(x2 + 1.0f);
1230 iy2 = (int)(y2 + 1.0f);
1231 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1233 // clamp it to the screen
1234 if (ix1 < r_view.x) ix1 = r_view.x;
1235 if (iy1 < r_view.y) iy1 = r_view.y;
1236 if (ix2 > r_view.x + r_view.width) ix2 = r_view.x + r_view.width;
1237 if (iy2 > r_view.y + r_view.height) iy2 = r_view.y + r_view.height;
1239 // if it is inside out, it's not visible
1240 if (ix2 <= ix1 || iy2 <= iy1)
1243 // the light area is visible, set up the scissor rectangle
1244 GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1245 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1246 //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1247 r_refdef.stats.lights_scissored++;
1251 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1253 float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1254 float *normal3f = rsurface_normal3f + 3 * firstvertex;
1255 float *color4f = rsurface_array_color4f + 4 * firstvertex;
1256 float dist, dot, distintensity, shadeintensity, v[3], n[3];
1257 if (r_textureunits.integer >= 3)
1259 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1261 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1262 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1263 if ((dot = DotProduct(n, v)) < 0)
1265 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1266 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
1267 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
1268 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
1269 if (r_refdef.fogenabled)
1271 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1272 VectorScale(color4f, f, color4f);
1276 VectorClear(color4f);
1280 else if (r_textureunits.integer >= 2)
1282 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1284 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1285 if ((dist = fabs(v[2])) < 1)
1287 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1288 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1289 if ((dot = DotProduct(n, v)) < 0)
1291 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1292 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1293 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1294 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1298 color4f[0] = ambientcolor[0] * distintensity;
1299 color4f[1] = ambientcolor[1] * distintensity;
1300 color4f[2] = ambientcolor[2] * distintensity;
1302 if (r_refdef.fogenabled)
1304 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1305 VectorScale(color4f, f, color4f);
1309 VectorClear(color4f);
1315 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1317 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1318 if ((dist = DotProduct(v, v)) < 1)
1321 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1322 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1323 if ((dot = DotProduct(n, v)) < 0)
1325 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1326 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1327 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1328 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1332 color4f[0] = ambientcolor[0] * distintensity;
1333 color4f[1] = ambientcolor[1] * distintensity;
1334 color4f[2] = ambientcolor[2] * distintensity;
1336 if (r_refdef.fogenabled)
1338 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1339 VectorScale(color4f, f, color4f);
1343 VectorClear(color4f);
1349 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1351 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1354 float *out3f = rsurface_array_texcoord3f + 3 * firstvertex;
1355 const float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1356 const float *svector3f = rsurface_svector3f + 3 * firstvertex;
1357 const float *tvector3f = rsurface_tvector3f + 3 * firstvertex;
1358 const float *normal3f = rsurface_normal3f + 3 * firstvertex;
1360 for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1362 VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
1363 // the cubemap normalizes this for us
1364 out3f[0] = DotProduct(svector3f, lightdir);
1365 out3f[1] = DotProduct(tvector3f, lightdir);
1366 out3f[2] = DotProduct(normal3f, lightdir);
1370 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1373 float *out3f = rsurface_array_texcoord3f + 3 * firstvertex;
1374 const float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1375 const float *svector3f = rsurface_svector3f + 3 * firstvertex;
1376 const float *tvector3f = rsurface_tvector3f + 3 * firstvertex;
1377 const float *normal3f = rsurface_normal3f + 3 * firstvertex;
1378 float lightdir[3], eyedir[3], halfdir[3];
1379 for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1381 VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
1382 VectorNormalize(lightdir);
1383 VectorSubtract(rsurface_modelorg, vertex3f, eyedir);
1384 VectorNormalize(eyedir);
1385 VectorAdd(lightdir, eyedir, halfdir);
1386 // the cubemap normalizes this for us
1387 out3f[0] = DotProduct(svector3f, halfdir);
1388 out3f[1] = DotProduct(tvector3f, halfdir);
1389 out3f[2] = DotProduct(normal3f, halfdir);
1393 static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, 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)
1395 // used to display how many times a surface is lit for level design purposes
1396 GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
1397 R_Mesh_ColorPointer(NULL);
1398 R_Mesh_ResetTextureState();
1399 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1402 static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, 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)
1404 // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1405 R_SetupSurfaceShader(lightcolorbase, false);
1406 R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
1407 R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
1408 R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
1409 R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
1410 if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1412 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1414 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1415 if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1417 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1421 static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, float r, float g, float b)
1423 // shared final code for all the dot3 layers
1425 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1426 for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1428 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1429 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1433 static void R_Shadow_RenderLighting_Light_Dot3_AmbientPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1436 // colorscale accounts for how much we multiply the brightness
1439 // mult is how many times the final pass of the lighting will be
1440 // performed to get more brightness than otherwise possible.
1442 // Limit mult to 64 for sanity sake.
1444 if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1446 // 3 3D combine path (Geforce3, Radeon 8500)
1447 memset(&m, 0, sizeof(m));
1448 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1449 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1450 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1451 m.tex[1] = R_GetTexture(basetexture);
1452 m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
1453 m.texmatrix[1] = rsurface_texture->currenttexmatrix;
1454 m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1455 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1456 m.texmatrix[2] = r_shadow_entitytolight;
1457 GL_BlendFunc(GL_ONE, GL_ONE);
1459 else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1461 // 2 3D combine path (Geforce3, original Radeon)
1462 memset(&m, 0, sizeof(m));
1463 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1464 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1465 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1466 m.tex[1] = R_GetTexture(basetexture);
1467 m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
1468 m.texmatrix[1] = rsurface_texture->currenttexmatrix;
1469 GL_BlendFunc(GL_ONE, GL_ONE);
1471 else if (r_textureunits.integer >= 4 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1473 // 4 2D combine path (Geforce3, Radeon 8500)
1474 memset(&m, 0, sizeof(m));
1475 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1476 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1477 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1478 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1479 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1480 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1481 m.tex[2] = R_GetTexture(basetexture);
1482 m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
1483 m.texmatrix[2] = rsurface_texture->currenttexmatrix;
1484 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1486 m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1487 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1488 m.texmatrix[3] = r_shadow_entitytolight;
1490 GL_BlendFunc(GL_ONE, GL_ONE);
1492 else if (r_textureunits.integer >= 3 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1494 // 3 2D combine path (Geforce3, original Radeon)
1495 memset(&m, 0, sizeof(m));
1496 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1497 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1498 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1499 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1500 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1501 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1502 m.tex[2] = R_GetTexture(basetexture);
1503 m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
1504 m.texmatrix[2] = rsurface_texture->currenttexmatrix;
1505 GL_BlendFunc(GL_ONE, GL_ONE);
1509 // 2/2/2 2D combine path (any dot3 card)
1510 memset(&m, 0, sizeof(m));
1511 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1512 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1513 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1514 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1515 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1516 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1517 R_Mesh_TextureState(&m);
1518 GL_ColorMask(0,0,0,1);
1519 GL_BlendFunc(GL_ONE, GL_ZERO);
1520 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1523 memset(&m, 0, sizeof(m));
1524 m.tex[0] = R_GetTexture(basetexture);
1525 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1526 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1527 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1529 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1530 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1531 m.texmatrix[1] = r_shadow_entitytolight;
1533 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1535 // this final code is shared
1536 R_Mesh_TextureState(&m);
1537 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1540 static void R_Shadow_RenderLighting_Light_Dot3_DiffusePass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1543 // colorscale accounts for how much we multiply the brightness
1546 // mult is how many times the final pass of the lighting will be
1547 // performed to get more brightness than otherwise possible.
1549 // Limit mult to 64 for sanity sake.
1551 // generate normalization cubemap texcoords
1552 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1553 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1555 // 3/2 3D combine path (Geforce3, Radeon 8500)
1556 memset(&m, 0, sizeof(m));
1557 m.tex[0] = R_GetTexture(normalmaptexture);
1558 m.texcombinergb[0] = GL_REPLACE;
1559 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1560 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1561 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1562 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1563 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1564 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1565 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1566 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1567 R_Mesh_TextureState(&m);
1568 GL_ColorMask(0,0,0,1);
1569 GL_BlendFunc(GL_ONE, GL_ZERO);
1570 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1573 memset(&m, 0, sizeof(m));
1574 m.tex[0] = R_GetTexture(basetexture);
1575 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1576 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1577 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1579 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1580 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1581 m.texmatrix[1] = r_shadow_entitytolight;
1583 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1585 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1587 // 1/2/2 3D combine path (original Radeon)
1588 memset(&m, 0, sizeof(m));
1589 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1590 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1591 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1592 R_Mesh_TextureState(&m);
1593 GL_ColorMask(0,0,0,1);
1594 GL_BlendFunc(GL_ONE, GL_ZERO);
1595 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1598 memset(&m, 0, sizeof(m));
1599 m.tex[0] = R_GetTexture(normalmaptexture);
1600 m.texcombinergb[0] = GL_REPLACE;
1601 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1602 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1603 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1604 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1605 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1606 R_Mesh_TextureState(&m);
1607 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1608 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1611 memset(&m, 0, sizeof(m));
1612 m.tex[0] = R_GetTexture(basetexture);
1613 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1614 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1615 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1617 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1618 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1619 m.texmatrix[1] = r_shadow_entitytolight;
1621 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1623 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1625 // 2/2 3D combine path (original Radeon)
1626 memset(&m, 0, sizeof(m));
1627 m.tex[0] = R_GetTexture(normalmaptexture);
1628 m.texcombinergb[0] = GL_REPLACE;
1629 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1630 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1631 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1632 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1633 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1634 R_Mesh_TextureState(&m);
1635 GL_ColorMask(0,0,0,1);
1636 GL_BlendFunc(GL_ONE, GL_ZERO);
1637 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1640 memset(&m, 0, sizeof(m));
1641 m.tex[0] = R_GetTexture(basetexture);
1642 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1643 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1644 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1645 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1646 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1647 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1649 else if (r_textureunits.integer >= 4)
1651 // 4/2 2D combine path (Geforce3, Radeon 8500)
1652 memset(&m, 0, sizeof(m));
1653 m.tex[0] = R_GetTexture(normalmaptexture);
1654 m.texcombinergb[0] = GL_REPLACE;
1655 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1656 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1657 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1658 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1659 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1660 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1661 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1662 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1663 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1664 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1665 m.texmatrix[3] = r_shadow_entitytoattenuationz;
1666 R_Mesh_TextureState(&m);
1667 GL_ColorMask(0,0,0,1);
1668 GL_BlendFunc(GL_ONE, GL_ZERO);
1669 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1672 memset(&m, 0, sizeof(m));
1673 m.tex[0] = R_GetTexture(basetexture);
1674 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1675 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1676 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1678 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1679 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1680 m.texmatrix[1] = r_shadow_entitytolight;
1682 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1686 // 2/2/2 2D combine path (any dot3 card)
1687 memset(&m, 0, sizeof(m));
1688 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1689 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1690 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1691 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1692 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1693 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1694 R_Mesh_TextureState(&m);
1695 GL_ColorMask(0,0,0,1);
1696 GL_BlendFunc(GL_ONE, GL_ZERO);
1697 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1700 memset(&m, 0, sizeof(m));
1701 m.tex[0] = R_GetTexture(normalmaptexture);
1702 m.texcombinergb[0] = GL_REPLACE;
1703 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1704 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1705 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1706 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1707 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1708 R_Mesh_TextureState(&m);
1709 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1710 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1713 memset(&m, 0, sizeof(m));
1714 m.tex[0] = R_GetTexture(basetexture);
1715 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1716 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1717 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1719 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1720 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1721 m.texmatrix[1] = r_shadow_entitytolight;
1723 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1725 // this final code is shared
1726 R_Mesh_TextureState(&m);
1727 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1730 static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1732 float glossexponent;
1734 // FIXME: detect blendsquare!
1735 //if (!gl_support_blendsquare)
1738 // generate normalization cubemap texcoords
1739 R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1740 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1742 // 2/0/0/1/2 3D combine blendsquare path
1743 memset(&m, 0, sizeof(m));
1744 m.tex[0] = R_GetTexture(normalmaptexture);
1745 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1746 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1747 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1748 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1749 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1750 R_Mesh_TextureState(&m);
1751 GL_ColorMask(0,0,0,1);
1752 // this squares the result
1753 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1754 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1756 // second and third pass
1757 R_Mesh_ResetTextureState();
1758 // square alpha in framebuffer a few times to make it shiny
1759 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1760 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1761 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1764 memset(&m, 0, sizeof(m));
1765 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1766 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1767 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1768 R_Mesh_TextureState(&m);
1769 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1770 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1773 memset(&m, 0, sizeof(m));
1774 m.tex[0] = R_GetTexture(glosstexture);
1775 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1776 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1777 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1779 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1780 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1781 m.texmatrix[1] = r_shadow_entitytolight;
1783 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1785 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1787 // 2/0/0/2 3D combine blendsquare path
1788 memset(&m, 0, sizeof(m));
1789 m.tex[0] = R_GetTexture(normalmaptexture);
1790 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1791 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1792 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1793 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1794 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1795 R_Mesh_TextureState(&m);
1796 GL_ColorMask(0,0,0,1);
1797 // this squares the result
1798 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1799 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1801 // second and third pass
1802 R_Mesh_ResetTextureState();
1803 // square alpha in framebuffer a few times to make it shiny
1804 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1805 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1806 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1809 memset(&m, 0, sizeof(m));
1810 m.tex[0] = R_GetTexture(glosstexture);
1811 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1812 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1813 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1814 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1815 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1816 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1820 // 2/0/0/2/2 2D combine blendsquare path
1821 memset(&m, 0, sizeof(m));
1822 m.tex[0] = R_GetTexture(normalmaptexture);
1823 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1824 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1825 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1826 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1827 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1828 R_Mesh_TextureState(&m);
1829 GL_ColorMask(0,0,0,1);
1830 // this squares the result
1831 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1832 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1834 // second and third pass
1835 R_Mesh_ResetTextureState();
1836 // square alpha in framebuffer a few times to make it shiny
1837 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1838 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1839 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1842 memset(&m, 0, sizeof(m));
1843 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1844 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1845 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1846 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1847 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1848 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1849 R_Mesh_TextureState(&m);
1850 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1851 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1854 memset(&m, 0, sizeof(m));
1855 m.tex[0] = R_GetTexture(glosstexture);
1856 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1857 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1858 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1860 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1861 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1862 m.texmatrix[1] = r_shadow_entitytolight;
1864 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1866 // this final code is shared
1867 R_Mesh_TextureState(&m);
1868 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1871 static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, 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)
1873 // ARB path (any Geforce, any Radeon)
1874 qboolean doambient = r_shadow_rtlight->ambientscale > 0;
1875 qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
1876 qboolean dospecular = specularscale > 0;
1877 if (!doambient && !dodiffuse && !dospecular)
1879 R_Mesh_ColorPointer(NULL);
1881 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
1883 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
1887 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
1889 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
1894 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale * r_view.colorscale);
1896 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale * r_view.colorscale);
1899 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
1902 void R_Shadow_RenderLighting_Light_Vertex_Pass(const model_t *model, int firstvertex, int numvertices, int numtriangles, const int *element3i, vec3_t diffusecolor2, vec3_t ambientcolor2)
1909 int newnumtriangles;
1913 int newelements[3072];
1914 R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
1915 for (renders = 0;renders < 64;renders++)
1920 newnumtriangles = 0;
1922 // due to low fillrate on the cards this vertex lighting path is
1923 // designed for, we manually cull all triangles that do not
1924 // contain a lit vertex
1925 // this builds batches of triangles from multiple surfaces and
1926 // renders them at once
1927 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
1929 if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
1931 if (newnumtriangles)
1933 newfirstvertex = min(newfirstvertex, e[0]);
1934 newlastvertex = max(newlastvertex, e[0]);
1938 newfirstvertex = e[0];
1939 newlastvertex = e[0];
1941 newfirstvertex = min(newfirstvertex, e[1]);
1942 newlastvertex = max(newlastvertex, e[1]);
1943 newfirstvertex = min(newfirstvertex, e[2]);
1944 newlastvertex = max(newlastvertex, e[2]);
1950 if (newnumtriangles >= 1024)
1952 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
1953 newnumtriangles = 0;
1959 if (newnumtriangles >= 1)
1961 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
1964 // if we couldn't find any lit triangles, exit early
1967 // now reduce the intensity for the next overbright pass
1968 // we have to clamp to 0 here incase the drivers have improper
1969 // handling of negative colors
1970 // (some old drivers even have improper handling of >1 color)
1972 for (i = 0, c = rsurface_array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
1974 if (c[0] > 1 || c[1] > 1 || c[2] > 1)
1976 c[0] = max(0, c[0] - 1);
1977 c[1] = max(0, c[1] - 1);
1978 c[2] = max(0, c[2] - 1);
1990 static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, 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)
1992 // OpenGL 1.1 path (anything)
1993 model_t *model = rsurface_entity->model;
1994 float ambientcolorbase[3], diffusecolorbase[3];
1995 float ambientcolorpants[3], diffusecolorpants[3];
1996 float ambientcolorshirt[3], diffusecolorshirt[3];
1998 VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorbase);
1999 VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorbase);
2000 VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorpants);
2001 VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorpants);
2002 VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
2003 VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
2004 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2005 R_Mesh_ColorPointer(rsurface_array_color4f);
2006 memset(&m, 0, sizeof(m));
2007 m.tex[0] = R_GetTexture(basetexture);
2008 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
2009 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
2010 if (r_textureunits.integer >= 2)
2013 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2014 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2015 m.pointer_texcoord3f[1] = rsurface_vertex3f;
2016 if (r_textureunits.integer >= 3)
2018 // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2019 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2020 m.texmatrix[2] = r_shadow_entitytoattenuationz;
2021 m.pointer_texcoord3f[2] = rsurface_vertex3f;
2024 R_Mesh_TextureState(&m);
2025 R_Mesh_TexBind(0, R_GetTexture(basetexture));
2026 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorbase, ambientcolorbase);
2029 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2030 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorpants, ambientcolorpants);
2034 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2035 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorshirt, ambientcolorshirt);
2039 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i)
2041 // FIXME: support MATERIALFLAG_NODEPTHTEST
2042 vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2043 // calculate colors to render this texture with
2044 lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * rsurface_entity->colormod[0] * rsurface_texture->currentalpha;
2045 lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * rsurface_entity->colormod[1] * rsurface_texture->currentalpha;
2046 lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * rsurface_entity->colormod[2] * rsurface_texture->currentalpha;
2047 if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * rsurface_texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2049 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
2050 GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
2051 if (rsurface_texture->colormapping)
2053 qboolean dopants = rsurface_texture->currentskinframe->pants != NULL && VectorLength2(rsurface_entity->colormap_pantscolor) >= (1.0f / 1048576.0f);
2054 qboolean doshirt = rsurface_texture->currentskinframe->shirt != NULL && VectorLength2(rsurface_entity->colormap_shirtcolor) >= (1.0f / 1048576.0f);
2057 lightcolorpants[0] = lightcolorbase[0] * rsurface_entity->colormap_pantscolor[0];
2058 lightcolorpants[1] = lightcolorbase[1] * rsurface_entity->colormap_pantscolor[1];
2059 lightcolorpants[2] = lightcolorbase[2] * rsurface_entity->colormap_pantscolor[2];
2062 VectorClear(lightcolorpants);
2065 lightcolorshirt[0] = lightcolorbase[0] * rsurface_entity->colormap_shirtcolor[0];
2066 lightcolorshirt[1] = lightcolorbase[1] * rsurface_entity->colormap_shirtcolor[1];
2067 lightcolorshirt[2] = lightcolorbase[2] * rsurface_entity->colormap_shirtcolor[2];
2070 VectorClear(lightcolorshirt);
2071 switch (r_shadow_rendermode)
2073 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2074 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2075 R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
2077 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2078 R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
2080 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2081 R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
2083 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2084 R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface_texture->basetexture, rsurface_texture->currentskinframe->pants, rsurface_texture->currentskinframe->shirt, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, dopants, doshirt);
2087 Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2093 switch (r_shadow_rendermode)
2095 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2096 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2097 R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
2099 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2100 R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
2102 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2103 R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
2105 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2106 R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, vec3_origin, vec3_origin, rsurface_texture->basetexture, r_texture_black, r_texture_black, rsurface_texture->currentskinframe->nmap, rsurface_texture->glosstexture, r_shadow_rtlight->specularscale * rsurface_texture->specularscale, false, false);
2109 Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2115 void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, qboolean shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
2117 matrix4x4_t tempmatrix = *matrix;
2118 Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2120 // if this light has been compiled before, free the associated data
2121 R_RTLight_Uncompile(rtlight);
2123 // clear it completely to avoid any lingering data
2124 memset(rtlight, 0, sizeof(*rtlight));
2126 // copy the properties
2127 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2128 Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2129 rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2130 VectorCopy(color, rtlight->color);
2131 rtlight->cubemapname[0] = 0;
2132 if (cubemapname && cubemapname[0])
2133 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2134 rtlight->shadow = shadow;
2135 rtlight->corona = corona;
2136 rtlight->style = style;
2137 rtlight->isstatic = isstatic;
2138 rtlight->coronasizescale = coronasizescale;
2139 rtlight->ambientscale = ambientscale;
2140 rtlight->diffusescale = diffusescale;
2141 rtlight->specularscale = specularscale;
2142 rtlight->flags = flags;
2144 // compute derived data
2145 //rtlight->cullradius = rtlight->radius;
2146 //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2147 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2148 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2149 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2150 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2151 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2152 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2155 // compiles rtlight geometry
2156 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2157 void R_RTLight_Compile(rtlight_t *rtlight)
2160 int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2161 int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2162 entity_render_t *ent = r_refdef.worldentity;
2163 model_t *model = r_refdef.worldmodel;
2164 unsigned char *data;
2166 // compile the light
2167 rtlight->compiled = true;
2168 rtlight->static_numleafs = 0;
2169 rtlight->static_numleafpvsbytes = 0;
2170 rtlight->static_leaflist = NULL;
2171 rtlight->static_leafpvs = NULL;
2172 rtlight->static_numsurfaces = 0;
2173 rtlight->static_surfacelist = NULL;
2174 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2175 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2176 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2177 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2178 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2179 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2181 if (model && model->GetLightInfo)
2183 // this variable must be set for the CompileShadowVolume code
2184 r_shadow_compilingrtlight = rtlight;
2185 R_Shadow_EnlargeLeafSurfaceTrisBuffer(model->brush.num_leafs, model->num_surfaces, model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles, model->surfmesh.num_triangles);
2186 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, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2187 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2188 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2189 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2190 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2191 rtlight->static_numsurfaces = numsurfaces;
2192 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2193 rtlight->static_numleafs = numleafs;
2194 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2195 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2196 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2197 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2198 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2199 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2200 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2201 if (rtlight->static_numsurfaces)
2202 memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2203 if (rtlight->static_numleafs)
2204 memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2205 if (rtlight->static_numleafpvsbytes)
2206 memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2207 if (rtlight->static_numshadowtrispvsbytes)
2208 memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2209 if (rtlight->static_numlighttrispvsbytes)
2210 memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2211 if (model->CompileShadowVolume && rtlight->shadow)
2212 model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2213 // now we're done compiling the rtlight
2214 r_shadow_compilingrtlight = NULL;
2218 // use smallest available cullradius - box radius or light radius
2219 //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2220 //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2224 if (rtlight->static_meshchain_shadow)
2227 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2230 shadowmeshtris += mesh->numtriangles;
2235 if (rtlight->static_numlighttrispvsbytes)
2236 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2237 if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2241 if (rtlight->static_numlighttrispvsbytes)
2242 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2243 if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2246 if (developer.integer >= 10)
2247 Con_Printf("static light built: %f %f %f : %f %f %f box, %i light triangles, %i shadow triangles, %i compiled 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], lighttris, shadowtris, shadowmeshtris, shadowmeshes);
2250 void R_RTLight_Uncompile(rtlight_t *rtlight)
2252 if (rtlight->compiled)
2254 if (rtlight->static_meshchain_shadow)
2255 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2256 rtlight->static_meshchain_shadow = NULL;
2257 // these allocations are grouped
2258 if (rtlight->static_surfacelist)
2259 Mem_Free(rtlight->static_surfacelist);
2260 rtlight->static_numleafs = 0;
2261 rtlight->static_numleafpvsbytes = 0;
2262 rtlight->static_leaflist = NULL;
2263 rtlight->static_leafpvs = NULL;
2264 rtlight->static_numsurfaces = 0;
2265 rtlight->static_surfacelist = NULL;
2266 rtlight->static_numshadowtrispvsbytes = 0;
2267 rtlight->static_shadowtrispvs = NULL;
2268 rtlight->static_numlighttrispvsbytes = 0;
2269 rtlight->static_lighttrispvs = NULL;
2270 rtlight->compiled = false;
2274 void R_Shadow_UncompileWorldLights(void)
2277 for (light = r_shadow_worldlightchain;light;light = light->next)
2278 R_RTLight_Uncompile(&light->rtlight);
2281 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2283 RSurf_ActiveWorldEntity();
2284 if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2288 for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2290 r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2291 R_Mesh_VertexPointer(mesh->vertex3f);
2292 GL_LockArrays(0, mesh->numverts);
2293 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2295 // decrement stencil if backface is behind depthbuffer
2296 GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
2297 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2298 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2299 // increment stencil if frontface is behind depthbuffer
2300 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
2301 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2303 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2304 GL_LockArrays(0, 0);
2308 else if (numsurfaces && r_refdef.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2311 int surfacelistindex;
2312 msurface_t *surface;
2313 R_Shadow_PrepareShadowMark(r_refdef.worldmodel->brush.shadowmesh->numtriangles);
2314 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2316 surface = r_refdef.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2317 for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2318 if (CHECKPVSBIT(trispvs, t))
2319 shadowmarklist[numshadowmark++] = t;
2321 R_Shadow_VolumeFromList(r_refdef.worldmodel->brush.shadowmesh->numverts, r_refdef.worldmodel->brush.shadowmesh->numtriangles, r_refdef.worldmodel->brush.shadowmesh->vertex3f, r_refdef.worldmodel->brush.shadowmesh->element3i, r_refdef.worldmodel->brush.shadowmesh->neighbor3i, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius + r_refdef.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
2323 else if (numsurfaces)
2324 r_refdef.worldmodel->DrawShadowVolume(r_refdef.worldentity, r_shadow_rtlight->shadoworigin, NULL, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs);
2327 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2329 vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2330 vec_t relativeshadowradius;
2331 RSurf_ActiveModelEntity(ent, false, false);
2332 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
2333 relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
2334 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2335 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2336 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2337 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2338 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2339 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2340 ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2343 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2345 // set up properties for rendering light onto this entity
2346 RSurf_ActiveModelEntity(ent, true, true);
2347 Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
2348 Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2349 Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2350 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2351 if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2352 R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
2355 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2357 if (!r_refdef.worldmodel->DrawLight)
2360 // set up properties for rendering light onto this entity
2361 RSurf_ActiveWorldEntity();
2362 r_shadow_entitytolight = r_shadow_rtlight->matrix_worldtolight;
2363 Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2364 Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2365 VectorCopy(r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2366 if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2367 R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
2369 r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
2372 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2374 model_t *model = ent->model;
2375 if (!model->DrawLight)
2378 R_Shadow_SetupEntityLight(ent);
2380 model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2383 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2387 int numleafs, numsurfaces;
2388 int *leaflist, *surfacelist;
2389 unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2390 int numlightentities;
2391 int numshadowentities;
2392 entity_render_t *lightentities[MAX_EDICTS];
2393 entity_render_t *shadowentities[MAX_EDICTS];
2395 // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2396 // skip lights that are basically invisible (color 0 0 0)
2397 if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2400 // loading is done before visibility checks because loading should happen
2401 // all at once at the start of a level, not when it stalls gameplay.
2402 // (especially important to benchmarks)
2404 if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2405 R_RTLight_Compile(rtlight);
2407 rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2409 // look up the light style value at this time
2410 f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2411 VectorScale(rtlight->color, f, rtlight->currentcolor);
2413 if (rtlight->selected)
2415 f = 2 + sin(realtime * M_PI * 4.0);
2416 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2420 // if lightstyle is currently off, don't draw the light
2421 if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2424 // if the light box is offscreen, skip it
2425 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2428 VectorCopy(rtlight->cullmins, r_shadow_rtlight_cullmins);
2429 VectorCopy(rtlight->cullmaxs, r_shadow_rtlight_cullmaxs);
2431 if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2433 // compiled light, world available and can receive realtime lighting
2434 // retrieve leaf information
2435 numleafs = rtlight->static_numleafs;
2436 leaflist = rtlight->static_leaflist;
2437 leafpvs = rtlight->static_leafpvs;
2438 numsurfaces = rtlight->static_numsurfaces;
2439 surfacelist = rtlight->static_surfacelist;
2440 shadowtrispvs = rtlight->static_shadowtrispvs;
2441 lighttrispvs = rtlight->static_lighttrispvs;
2443 else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2445 // dynamic light, world available and can receive realtime lighting
2446 // calculate lit surfaces and leafs
2447 R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces, r_refdef.worldmodel->brush.shadowmesh ? r_refdef.worldmodel->brush.shadowmesh->numtriangles : r_refdef.worldmodel->surfmesh.num_triangles, r_refdef.worldmodel->surfmesh.num_triangles);
2448 r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2449 leaflist = r_shadow_buffer_leaflist;
2450 leafpvs = r_shadow_buffer_leafpvs;
2451 surfacelist = r_shadow_buffer_surfacelist;
2452 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2453 lighttrispvs = r_shadow_buffer_lighttrispvs;
2454 // if the reduced leaf bounds are offscreen, skip it
2455 if (R_CullBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2466 shadowtrispvs = NULL;
2467 lighttrispvs = NULL;
2469 // check if light is illuminating any visible leafs
2472 for (i = 0;i < numleafs;i++)
2473 if (r_viewcache.world_leafvisible[leaflist[i]])
2478 // set up a scissor rectangle for this light
2479 if (R_Shadow_ScissorForBBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2482 // make a list of lit entities and shadow casting entities
2483 numlightentities = 0;
2484 numshadowentities = 0;
2485 // add dynamic entities that are lit by the light
2486 if (r_drawentities.integer)
2488 for (i = 0;i < r_refdef.numentities;i++)
2491 entity_render_t *ent = r_refdef.entities[i];
2493 if (!BoxesOverlap(ent->mins, ent->maxs, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2495 if (!(model = ent->model))
2497 if (r_viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2499 // this entity wants to receive light, is visible, and is
2500 // inside the light box
2501 // TODO: check if the surfaces in the model can receive light
2502 // so now check if it's in a leaf seen by the light
2503 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2505 lightentities[numlightentities++] = ent;
2506 // since it is lit, it probably also casts a shadow...
2507 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2508 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2509 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2510 shadowentities[numshadowentities++] = ent;
2512 else if (ent->flags & RENDER_SHADOW)
2514 // this entity is not receiving light, but may still need to
2516 // TODO: check if the surfaces in the model can cast shadow
2517 // now check if it is in a leaf seen by the light
2518 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2520 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2521 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2522 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2523 shadowentities[numshadowentities++] = ent;
2528 // return if there's nothing at all to light
2529 if (!numlightentities && !numsurfaces)
2532 // don't let sound skip if going slow
2533 if (r_refdef.extraupdate)
2536 // make this the active rtlight for rendering purposes
2537 R_Shadow_RenderMode_ActiveLight(rtlight);
2538 // count this light in the r_speeds
2539 r_refdef.stats.lights++;
2542 if (numsurfaces + numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
2544 // draw stencil shadow volumes to mask off pixels that are in shadow
2545 // so that they won't receive lighting
2549 R_Shadow_RenderMode_StencilShadowVolumes();
2551 R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
2552 for (i = 0;i < numshadowentities;i++)
2553 R_Shadow_DrawEntityShadow(shadowentities[i]);
2556 // optionally draw visible shape of the shadow volumes
2557 // for performance analysis by level designers
2558 if (r_showshadowvolumes.integer)
2560 R_Shadow_RenderMode_VisibleShadowVolumes();
2562 R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
2563 for (i = 0;i < numshadowentities;i++)
2564 R_Shadow_DrawEntityShadow(shadowentities[i]);
2568 if (numsurfaces + numlightentities)
2570 // draw lighting in the unmasked areas
2571 R_Shadow_RenderMode_Lighting(usestencil, false);
2573 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
2574 for (i = 0;i < numlightentities;i++)
2575 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2577 // optionally draw the illuminated areas
2578 // for performance analysis by level designers
2579 if (r_showlighting.integer)
2581 R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
2583 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
2584 for (i = 0;i < numlightentities;i++)
2585 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2590 void R_Shadow_DrawLightSprites(void);
2591 void R_ShadowVolumeLighting(qboolean visible)
2596 if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2597 R_Shadow_EditLights_Reload_f();
2599 if (r_editlights.integer)
2600 R_Shadow_DrawLightSprites();
2602 R_Shadow_RenderMode_Begin();
2604 flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2605 if (r_shadow_debuglight.integer >= 0)
2607 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2608 if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2609 R_DrawRTLight(&light->rtlight, visible);
2612 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2613 if (light->flags & flag)
2614 R_DrawRTLight(&light->rtlight, visible);
2615 if (r_refdef.rtdlight)
2616 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
2617 R_DrawRTLight(&r_refdef.lights[lnum], visible);
2619 R_Shadow_RenderMode_End();
2622 extern void R_SetupView(const matrix4x4_t *matrix);
2623 extern cvar_t r_shadows_throwdistance;
2624 void R_DrawModelShadows(void)
2627 float relativethrowdistance;
2628 entity_render_t *ent;
2629 vec3_t relativelightorigin;
2630 vec3_t relativelightdirection;
2631 vec3_t relativeshadowmins, relativeshadowmaxs;
2634 if (!r_drawentities.integer || !gl_stencil)
2638 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
2640 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
2642 if (gl_ext_separatestencil.integer)
2643 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
2644 else if (gl_ext_stenciltwoside.integer)
2645 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
2647 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
2649 R_Shadow_RenderMode_StencilShadowVolumes();
2651 for (i = 0;i < r_refdef.numentities;i++)
2653 ent = r_refdef.entities[i];
2654 // cast shadows from anything that is not a submodel of the map
2655 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
2657 relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
2658 VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
2659 VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
2660 VectorNegate(ent->modellight_lightdir, relativelightdirection);
2661 VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
2662 RSurf_ActiveModelEntity(ent, false, false);
2663 ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2667 // not really the right mode, but this will disable any silly stencil features
2668 R_Shadow_RenderMode_VisibleLighting(true, true);
2670 // vertex coordinates for a quad that covers the screen exactly
2671 vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
2672 vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
2673 vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
2674 vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
2676 // set up ortho view for rendering this pass
2677 GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
2678 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
2679 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
2680 GL_ScissorTest(true);
2681 R_Mesh_Matrix(&identitymatrix);
2682 R_Mesh_ResetTextureState();
2683 R_Mesh_VertexPointer(vertex3f);
2684 R_Mesh_ColorPointer(NULL);
2686 // set up a 50% darkening blend on shadowed areas
2687 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2688 GL_DepthTest(false);
2689 GL_DepthMask(false);
2690 qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
2691 GL_Color(0, 0, 0, 0.5);
2692 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
2693 qglDepthFunc(GL_ALWAYS);CHECKGLERROR
2694 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
2695 qglStencilMask(~0);CHECKGLERROR
2696 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
2697 qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
2699 // apply the blend to the shadowed areas
2700 R_Mesh_Draw(0, 4, 2, polygonelements);
2702 // restoring the perspective view is done by R_RenderScene
2703 //R_SetupView(&r_view.matrix);
2705 // restore other state to normal
2706 R_Shadow_RenderMode_End();
2710 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2711 typedef struct suffixinfo_s
2714 qboolean flipx, flipy, flipdiagonal;
2717 static suffixinfo_t suffix[3][6] =
2720 {"px", false, false, false},
2721 {"nx", false, false, false},
2722 {"py", false, false, false},
2723 {"ny", false, false, false},
2724 {"pz", false, false, false},
2725 {"nz", false, false, false}
2728 {"posx", false, false, false},
2729 {"negx", false, false, false},
2730 {"posy", false, false, false},
2731 {"negy", false, false, false},
2732 {"posz", false, false, false},
2733 {"negz", false, false, false}
2736 {"rt", true, false, true},
2737 {"lf", false, true, true},
2738 {"ft", true, true, false},
2739 {"bk", false, false, false},
2740 {"up", true, false, true},
2741 {"dn", true, false, true}
2745 static int componentorder[4] = {0, 1, 2, 3};
2747 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2749 int i, j, cubemapsize;
2750 unsigned char *cubemappixels, *image_rgba;
2751 rtexture_t *cubemaptexture;
2753 // must start 0 so the first loadimagepixels has no requested width/height
2755 cubemappixels = NULL;
2756 cubemaptexture = NULL;
2757 // keep trying different suffix groups (posx, px, rt) until one loads
2758 for (j = 0;j < 3 && !cubemappixels;j++)
2760 // load the 6 images in the suffix group
2761 for (i = 0;i < 6;i++)
2763 // generate an image name based on the base and and suffix
2764 dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2766 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2768 // an image loaded, make sure width and height are equal
2769 if (image_width == image_height)
2771 // if this is the first image to load successfully, allocate the cubemap memory
2772 if (!cubemappixels && image_width >= 1)
2774 cubemapsize = image_width;
2775 // note this clears to black, so unavailable sides are black
2776 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2778 // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2780 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);
2783 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2785 Mem_Free(image_rgba);
2789 // if a cubemap loaded, upload it
2792 if (!r_shadow_filters_texturepool)
2793 r_shadow_filters_texturepool = R_AllocTexturePool();
2794 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2795 Mem_Free(cubemappixels);
2799 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2800 for (j = 0;j < 3;j++)
2801 for (i = 0;i < 6;i++)
2802 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2803 Con_Print(" and was unable to find any of them.\n");
2805 return cubemaptexture;
2808 rtexture_t *R_Shadow_Cubemap(const char *basename)
2811 for (i = 0;i < numcubemaps;i++)
2812 if (!strcasecmp(cubemaps[i].basename, basename))
2813 return cubemaps[i].texture;
2814 if (i >= MAX_CUBEMAPS)
2815 return r_texture_whitecube;
2817 strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
2818 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2819 if (!cubemaps[i].texture)
2820 cubemaps[i].texture = r_texture_whitecube;
2821 return cubemaps[i].texture;
2824 void R_Shadow_FreeCubemaps(void)
2827 R_FreeTexturePool(&r_shadow_filters_texturepool);
2830 dlight_t *R_Shadow_NewWorldLight(void)
2833 light = (dlight_t *)Mem_Alloc(r_main_mempool, sizeof(dlight_t));
2834 light->next = r_shadow_worldlightchain;
2835 r_shadow_worldlightchain = light;
2839 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)
2842 // validate parameters
2843 if (style < 0 || style >= MAX_LIGHTSTYLES)
2845 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2851 // copy to light properties
2852 VectorCopy(origin, light->origin);
2853 light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2854 light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2855 light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2856 light->color[0] = max(color[0], 0);
2857 light->color[1] = max(color[1], 0);
2858 light->color[2] = max(color[2], 0);
2859 light->radius = max(radius, 0);
2860 light->style = style;
2861 light->shadow = shadowenable;
2862 light->corona = corona;
2863 strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
2864 light->coronasizescale = coronasizescale;
2865 light->ambientscale = ambientscale;
2866 light->diffusescale = diffusescale;
2867 light->specularscale = specularscale;
2868 light->flags = flags;
2870 // update renderable light data
2871 Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
2872 R_RTLight_Update(&light->rtlight, true, &matrix, light->color, light->style, light->cubemapname[0] ? light->cubemapname : NULL, light->shadow, light->corona, light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
2875 void R_Shadow_FreeWorldLight(dlight_t *light)
2877 dlight_t **lightpointer;
2878 R_RTLight_Uncompile(&light->rtlight);
2879 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2880 if (*lightpointer != light)
2881 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
2882 *lightpointer = light->next;
2886 void R_Shadow_ClearWorldLights(void)
2888 while (r_shadow_worldlightchain)
2889 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2890 r_shadow_selectedlight = NULL;
2891 R_Shadow_FreeCubemaps();
2894 void R_Shadow_SelectLight(dlight_t *light)
2896 if (r_shadow_selectedlight)
2897 r_shadow_selectedlight->selected = false;
2898 r_shadow_selectedlight = light;
2899 if (r_shadow_selectedlight)
2900 r_shadow_selectedlight->selected = true;
2903 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2905 // this is never batched (there can be only one)
2906 float scale = r_editlights_cursorgrid.value * 0.5f;
2907 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[1]->tex, NULL, false, r_editlights_cursorlocation, r_view.right, r_view.up, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2910 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2912 // this is never batched (due to the ent parameter changing every time)
2913 // so numsurfaces == 1 and surfacelist[0] == lightnumber
2915 const dlight_t *light = (dlight_t *)ent;
2917 if (light->selected)
2918 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2921 R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[surfacelist[0]]->tex, NULL, false, light->origin, r_view.right, r_view.up, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2924 void R_Shadow_DrawLightSprites(void)
2929 for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
2930 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
2931 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
2934 void R_Shadow_SelectLightInView(void)
2936 float bestrating, rating, temp[3];
2937 dlight_t *best, *light;
2940 for (light = r_shadow_worldlightchain;light;light = light->next)
2942 VectorSubtract(light->origin, r_view.origin, temp);
2943 rating = (DotProduct(temp, r_view.forward) / sqrt(DotProduct(temp, temp)));
2946 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2947 if (bestrating < rating && CL_Move(light->origin, vec3_origin, vec3_origin, r_view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1.0f)
2949 bestrating = rating;
2954 R_Shadow_SelectLight(best);
2957 void R_Shadow_LoadWorldLights(void)
2959 int n, a, style, shadow, flags;
2960 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
2961 float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
2962 if (r_refdef.worldmodel == NULL)
2964 Con_Print("No map loaded.\n");
2967 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
2968 strlcat (name, ".rtlights", sizeof (name));
2969 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
2979 for (;COM_Parse(t, true) && strcmp(
2980 if (COM_Parse(t, true))
2982 if (com_token[0] == '!')
2985 origin[0] = atof(com_token+1);
2988 origin[0] = atof(com_token);
2993 while (*s && *s != '\n' && *s != '\r')
2999 // check for modifier flags
3006 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);
3009 flags = LIGHTFLAG_REALTIMEMODE;
3017 coronasizescale = 0.25f;
3019 VectorClear(angles);
3022 if (a < 9 || !strcmp(cubemapname, "\"\""))
3024 // remove quotes on cubemapname
3025 if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3028 namelen = strlen(cubemapname) - 2;
3029 memmove(cubemapname, cubemapname + 1, namelen);
3030 cubemapname[namelen] = '\0';
3034 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);
3037 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3045 Con_Printf("invalid rtlights file \"%s\"\n", name);
3046 Mem_Free(lightsstring);
3050 void R_Shadow_SaveWorldLights(void)
3053 size_t bufchars, bufmaxchars;
3055 char name[MAX_QPATH];
3056 char line[MAX_INPUTLINE];
3057 if (!r_shadow_worldlightchain)
3059 if (r_refdef.worldmodel == NULL)
3061 Con_Print("No map loaded.\n");
3064 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3065 strlcat (name, ".rtlights", sizeof (name));
3066 bufchars = bufmaxchars = 0;
3068 for (light = r_shadow_worldlightchain;light;light = light->next)
3070 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3071 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);
3072 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3073 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]);
3075 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);
3076 if (bufchars + strlen(line) > bufmaxchars)
3078 bufmaxchars = bufchars + strlen(line) + 2048;
3080 buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3084 memcpy(buf, oldbuf, bufchars);
3090 memcpy(buf + bufchars, line, strlen(line));
3091 bufchars += strlen(line);
3095 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3100 void R_Shadow_LoadLightsFile(void)
3103 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3104 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3105 if (r_refdef.worldmodel == NULL)
3107 Con_Print("No map loaded.\n");
3110 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3111 strlcat (name, ".lights", sizeof (name));
3112 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3120 while (*s && *s != '\n' && *s != '\r')
3126 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);
3130 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);
3133 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3134 radius = bound(15, radius, 4096);
3135 VectorScale(color, (2.0f / (8388608.0f)), color);
3136 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3144 Con_Printf("invalid lights file \"%s\"\n", name);
3145 Mem_Free(lightsstring);
3149 // tyrlite/hmap2 light types in the delay field
3150 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3152 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3154 int entnum, style, islight, skin, pflags, effects, type, n;
3157 float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3158 char key[256], value[MAX_INPUTLINE];
3160 if (r_refdef.worldmodel == NULL)
3162 Con_Print("No map loaded.\n");
3165 // try to load a .ent file first
3166 FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3167 strlcat (key, ".ent", sizeof (key));
3168 data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3169 // and if that is not found, fall back to the bsp file entity string
3171 data = r_refdef.worldmodel->brush.entities;
3174 for (entnum = 0;COM_ParseTokenConsole(&data) && com_token[0] == '{';entnum++)
3176 type = LIGHTTYPE_MINUSX;
3177 origin[0] = origin[1] = origin[2] = 0;
3178 originhack[0] = originhack[1] = originhack[2] = 0;
3179 angles[0] = angles[1] = angles[2] = 0;
3180 color[0] = color[1] = color[2] = 1;
3181 light[0] = light[1] = light[2] = 1;light[3] = 300;
3182 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3192 if (!COM_ParseTokenConsole(&data))
3194 if (com_token[0] == '}')
3195 break; // end of entity
3196 if (com_token[0] == '_')
3197 strlcpy(key, com_token + 1, sizeof(key));
3199 strlcpy(key, com_token, sizeof(key));
3200 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3201 key[strlen(key)-1] = 0;
3202 if (!COM_ParseTokenConsole(&data))
3204 strlcpy(value, com_token, sizeof(value));
3206 // now that we have the key pair worked out...
3207 if (!strcmp("light", key))
3209 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3213 light[0] = vec[0] * (1.0f / 256.0f);
3214 light[1] = vec[0] * (1.0f / 256.0f);
3215 light[2] = vec[0] * (1.0f / 256.0f);
3221 light[0] = vec[0] * (1.0f / 255.0f);
3222 light[1] = vec[1] * (1.0f / 255.0f);
3223 light[2] = vec[2] * (1.0f / 255.0f);
3227 else if (!strcmp("delay", key))
3229 else if (!strcmp("origin", key))
3230 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3231 else if (!strcmp("angle", key))
3232 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3233 else if (!strcmp("angles", key))
3234 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3235 else if (!strcmp("color", key))
3236 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3237 else if (!strcmp("wait", key))
3238 fadescale = atof(value);
3239 else if (!strcmp("classname", key))
3241 if (!strncmp(value, "light", 5))
3244 if (!strcmp(value, "light_fluoro"))
3249 overridecolor[0] = 1;
3250 overridecolor[1] = 1;
3251 overridecolor[2] = 1;
3253 if (!strcmp(value, "light_fluorospark"))
3258 overridecolor[0] = 1;
3259 overridecolor[1] = 1;
3260 overridecolor[2] = 1;
3262 if (!strcmp(value, "light_globe"))
3267 overridecolor[0] = 1;
3268 overridecolor[1] = 0.8;
3269 overridecolor[2] = 0.4;
3271 if (!strcmp(value, "light_flame_large_yellow"))
3276 overridecolor[0] = 1;
3277 overridecolor[1] = 0.5;
3278 overridecolor[2] = 0.1;
3280 if (!strcmp(value, "light_flame_small_yellow"))
3285 overridecolor[0] = 1;
3286 overridecolor[1] = 0.5;
3287 overridecolor[2] = 0.1;
3289 if (!strcmp(value, "light_torch_small_white"))
3294 overridecolor[0] = 1;
3295 overridecolor[1] = 0.5;
3296 overridecolor[2] = 0.1;
3298 if (!strcmp(value, "light_torch_small_walltorch"))
3303 overridecolor[0] = 1;
3304 overridecolor[1] = 0.5;
3305 overridecolor[2] = 0.1;
3309 else if (!strcmp("style", key))
3310 style = atoi(value);
3311 else if (!strcmp("skin", key))
3312 skin = (int)atof(value);
3313 else if (!strcmp("pflags", key))
3314 pflags = (int)atof(value);
3315 else if (!strcmp("effects", key))
3316 effects = (int)atof(value);
3317 else if (r_refdef.worldmodel->type == mod_brushq3)
3319 if (!strcmp("scale", key))
3320 lightscale = atof(value);
3321 if (!strcmp("fade", key))
3322 fadescale = atof(value);
3327 if (lightscale <= 0)
3331 if (color[0] == color[1] && color[0] == color[2])
3333 color[0] *= overridecolor[0];
3334 color[1] *= overridecolor[1];
3335 color[2] *= overridecolor[2];
3337 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3338 color[0] = color[0] * light[0];
3339 color[1] = color[1] * light[1];
3340 color[2] = color[2] * light[2];
3343 case LIGHTTYPE_MINUSX:
3345 case LIGHTTYPE_RECIPX:
3347 VectorScale(color, (1.0f / 16.0f), color);
3349 case LIGHTTYPE_RECIPXX:
3351 VectorScale(color, (1.0f / 16.0f), color);
3354 case LIGHTTYPE_NONE:
3358 case LIGHTTYPE_MINUSXX:
3361 VectorAdd(origin, originhack, origin);
3363 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);
3366 Mem_Free(entfiledata);
3370 void R_Shadow_SetCursorLocationForView(void)
3373 vec3_t dest, endpos;
3375 VectorMA(r_view.origin, r_editlights_cursordistance.value, r_view.forward, dest);
3376 trace = CL_Move(r_view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
3377 if (trace.fraction < 1)
3379 dist = trace.fraction * r_editlights_cursordistance.value;
3380 push = r_editlights_cursorpushback.value;
3384 VectorMA(trace.endpos, push, r_view.forward, endpos);
3385 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
3389 VectorClear( endpos );
3391 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3392 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3393 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3396 void R_Shadow_UpdateWorldLightSelection(void)
3398 if (r_editlights.integer)
3400 R_Shadow_SetCursorLocationForView();
3401 R_Shadow_SelectLightInView();
3404 R_Shadow_SelectLight(NULL);
3407 void R_Shadow_EditLights_Clear_f(void)
3409 R_Shadow_ClearWorldLights();
3412 void R_Shadow_EditLights_Reload_f(void)
3414 if (!r_refdef.worldmodel)
3416 strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
3417 R_Shadow_ClearWorldLights();
3418 R_Shadow_LoadWorldLights();
3419 if (r_shadow_worldlightchain == NULL)
3421 R_Shadow_LoadLightsFile();
3422 if (r_shadow_worldlightchain == NULL)
3423 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3427 void R_Shadow_EditLights_Save_f(void)
3429 if (!r_refdef.worldmodel)
3431 R_Shadow_SaveWorldLights();
3434 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3436 R_Shadow_ClearWorldLights();
3437 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3440 void R_Shadow_EditLights_ImportLightsFile_f(void)
3442 R_Shadow_ClearWorldLights();
3443 R_Shadow_LoadLightsFile();
3446 void R_Shadow_EditLights_Spawn_f(void)
3449 if (!r_editlights.integer)
3451 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3454 if (Cmd_Argc() != 1)
3456 Con_Print("r_editlights_spawn does not take parameters\n");
3459 color[0] = color[1] = color[2] = 1;
3460 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3463 void R_Shadow_EditLights_Edit_f(void)
3465 vec3_t origin, angles, color;
3466 vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3467 int style, shadows, flags, normalmode, realtimemode;
3468 char cubemapname[MAX_INPUTLINE];
3469 if (!r_editlights.integer)
3471 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3474 if (!r_shadow_selectedlight)
3476 Con_Print("No selected light.\n");
3479 VectorCopy(r_shadow_selectedlight->origin, origin);
3480 VectorCopy(r_shadow_selectedlight->angles, angles);
3481 VectorCopy(r_shadow_selectedlight->color, color);
3482 radius = r_shadow_selectedlight->radius;
3483 style = r_shadow_selectedlight->style;
3484 if (r_shadow_selectedlight->cubemapname)
3485 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
3488 shadows = r_shadow_selectedlight->shadow;
3489 corona = r_shadow_selectedlight->corona;
3490 coronasizescale = r_shadow_selectedlight->coronasizescale;
3491 ambientscale = r_shadow_selectedlight->ambientscale;
3492 diffusescale = r_shadow_selectedlight->diffusescale;
3493 specularscale = r_shadow_selectedlight->specularscale;
3494 flags = r_shadow_selectedlight->flags;
3495 normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3496 realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3497 if (!strcmp(Cmd_Argv(1), "origin"))
3499 if (Cmd_Argc() != 5)
3501 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3504 origin[0] = atof(Cmd_Argv(2));
3505 origin[1] = atof(Cmd_Argv(3));
3506 origin[2] = atof(Cmd_Argv(4));
3508 else if (!strcmp(Cmd_Argv(1), "originx"))
3510 if (Cmd_Argc() != 3)
3512 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3515 origin[0] = atof(Cmd_Argv(2));
3517 else if (!strcmp(Cmd_Argv(1), "originy"))
3519 if (Cmd_Argc() != 3)
3521 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3524 origin[1] = atof(Cmd_Argv(2));
3526 else if (!strcmp(Cmd_Argv(1), "originz"))
3528 if (Cmd_Argc() != 3)
3530 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3533 origin[2] = atof(Cmd_Argv(2));
3535 else if (!strcmp(Cmd_Argv(1), "move"))
3537 if (Cmd_Argc() != 5)
3539 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3542 origin[0] += atof(Cmd_Argv(2));
3543 origin[1] += atof(Cmd_Argv(3));
3544 origin[2] += atof(Cmd_Argv(4));
3546 else if (!strcmp(Cmd_Argv(1), "movex"))
3548 if (Cmd_Argc() != 3)
3550 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3553 origin[0] += atof(Cmd_Argv(2));
3555 else if (!strcmp(Cmd_Argv(1), "movey"))
3557 if (Cmd_Argc() != 3)
3559 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3562 origin[1] += atof(Cmd_Argv(2));
3564 else if (!strcmp(Cmd_Argv(1), "movez"))
3566 if (Cmd_Argc() != 3)
3568 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3571 origin[2] += atof(Cmd_Argv(2));
3573 else if (!strcmp(Cmd_Argv(1), "angles"))
3575 if (Cmd_Argc() != 5)
3577 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3580 angles[0] = atof(Cmd_Argv(2));
3581 angles[1] = atof(Cmd_Argv(3));
3582 angles[2] = atof(Cmd_Argv(4));
3584 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3586 if (Cmd_Argc() != 3)
3588 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3591 angles[0] = atof(Cmd_Argv(2));
3593 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3595 if (Cmd_Argc() != 3)
3597 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3600 angles[1] = atof(Cmd_Argv(2));
3602 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3604 if (Cmd_Argc() != 3)
3606 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3609 angles[2] = atof(Cmd_Argv(2));
3611 else if (!strcmp(Cmd_Argv(1), "color"))
3613 if (Cmd_Argc() != 5)
3615 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3618 color[0] = atof(Cmd_Argv(2));
3619 color[1] = atof(Cmd_Argv(3));
3620 color[2] = atof(Cmd_Argv(4));
3622 else if (!strcmp(Cmd_Argv(1), "radius"))
3624 if (Cmd_Argc() != 3)
3626 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3629 radius = atof(Cmd_Argv(2));
3631 else if (!strcmp(Cmd_Argv(1), "colorscale"))
3633 if (Cmd_Argc() == 3)
3635 double scale = atof(Cmd_Argv(2));
3642 if (Cmd_Argc() != 5)
3644 Con_Printf("usage: r_editlights_edit %s red green blue (OR grey instead of red green blue)\n", Cmd_Argv(1));
3647 color[0] *= atof(Cmd_Argv(2));
3648 color[1] *= atof(Cmd_Argv(3));
3649 color[2] *= atof(Cmd_Argv(4));
3652 else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
3654 if (Cmd_Argc() != 3)
3656 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3659 radius *= atof(Cmd_Argv(2));
3661 else if (!strcmp(Cmd_Argv(1), "style"))
3663 if (Cmd_Argc() != 3)
3665 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3668 style = atoi(Cmd_Argv(2));
3670 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3674 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3677 if (Cmd_Argc() == 3)
3678 strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
3682 else if (!strcmp(Cmd_Argv(1), "shadows"))
3684 if (Cmd_Argc() != 3)
3686 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3689 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3691 else if (!strcmp(Cmd_Argv(1), "corona"))
3693 if (Cmd_Argc() != 3)
3695 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3698 corona = atof(Cmd_Argv(2));
3700 else if (!strcmp(Cmd_Argv(1), "coronasize"))
3702 if (Cmd_Argc() != 3)
3704 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3707 coronasizescale = atof(Cmd_Argv(2));
3709 else if (!strcmp(Cmd_Argv(1), "ambient"))
3711 if (Cmd_Argc() != 3)
3713 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3716 ambientscale = atof(Cmd_Argv(2));
3718 else if (!strcmp(Cmd_Argv(1), "diffuse"))
3720 if (Cmd_Argc() != 3)
3722 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3725 diffusescale = atof(Cmd_Argv(2));
3727 else if (!strcmp(Cmd_Argv(1), "specular"))
3729 if (Cmd_Argc() != 3)
3731 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3734 specularscale = atof(Cmd_Argv(2));
3736 else if (!strcmp(Cmd_Argv(1), "normalmode"))
3738 if (Cmd_Argc() != 3)
3740 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3743 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3745 else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3747 if (Cmd_Argc() != 3)
3749 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3752 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3756 Con_Print("usage: r_editlights_edit [property] [value]\n");
3757 Con_Print("Selected light's properties:\n");
3758 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3759 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3760 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3761 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3762 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3763 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
3764 Con_Printf("Shadows : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3765 Con_Printf("Cubemap : %s\n", r_shadow_selectedlight->cubemapname);
3766 Con_Printf("CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);
3767 Con_Printf("Ambient : %f\n", r_shadow_selectedlight->ambientscale);
3768 Con_Printf("Diffuse : %f\n", r_shadow_selectedlight->diffusescale);
3769 Con_Printf("Specular : %f\n", r_shadow_selectedlight->specularscale);
3770 Con_Printf("NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
3771 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
3774 flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
3775 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3778 void R_Shadow_EditLights_EditAll_f(void)
3782 if (!r_editlights.integer)
3784 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
3788 for (light = r_shadow_worldlightchain;light;light = light->next)
3790 R_Shadow_SelectLight(light);
3791 R_Shadow_EditLights_Edit_f();
3795 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3797 int lightnumber, lightcount;
3801 if (!r_editlights.integer)
3807 for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
3808 if (light == r_shadow_selectedlight)
3809 lightnumber = lightcount;
3810 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;
3811 if (r_shadow_selectedlight == NULL)
3813 sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3814 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;
3815 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;
3816 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;
3817 sprintf(temp, "Radius : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3818 sprintf(temp, "Corona : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3819 sprintf(temp, "Style : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3820 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;
3821 sprintf(temp, "Cubemap : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3822 sprintf(temp, "CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3823 sprintf(temp, "Ambient : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3824 sprintf(temp, "Diffuse : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3825 sprintf(temp, "Specular : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3826 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;
3827 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;
3830 void R_Shadow_EditLights_ToggleShadow_f(void)
3832 if (!r_editlights.integer)
3834 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3837 if (!r_shadow_selectedlight)
3839 Con_Print("No selected light.\n");
3842 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);
3845 void R_Shadow_EditLights_ToggleCorona_f(void)
3847 if (!r_editlights.integer)
3849 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3852 if (!r_shadow_selectedlight)
3854 Con_Print("No selected light.\n");
3857 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);
3860 void R_Shadow_EditLights_Remove_f(void)
3862 if (!r_editlights.integer)
3864 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
3867 if (!r_shadow_selectedlight)
3869 Con_Print("No selected light.\n");
3872 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3873 r_shadow_selectedlight = NULL;
3876 void R_Shadow_EditLights_Help_f(void)
3879 "Documentation on r_editlights system:\n"
3881 "r_editlights : enable/disable editing mode\n"
3882 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3883 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3884 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3885 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3886 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3888 "r_editlights_help : this help\n"
3889 "r_editlights_clear : remove all lights\n"
3890 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3891 "r_editlights_save : save to .rtlights file\n"
3892 "r_editlights_spawn : create a light with default settings\n"
3893 "r_editlights_edit command : edit selected light - more documentation below\n"
3894 "r_editlights_remove : remove selected light\n"
3895 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3896 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3897 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3899 "origin x y z : set light location\n"
3900 "originx x: set x component of light location\n"
3901 "originy y: set y component of light location\n"
3902 "originz z: set z component of light location\n"
3903 "move x y z : adjust light location\n"
3904 "movex x: adjust x component of light location\n"
3905 "movey y: adjust y component of light location\n"
3906 "movez z: adjust z component of light location\n"
3907 "angles x y z : set light angles\n"
3908 "anglesx x: set x component of light angles\n"
3909 "anglesy y: set y component of light angles\n"
3910 "anglesz z: set z component of light angles\n"
3911 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3912 "radius radius : set radius (size) of light\n"
3913 "colorscale grey : multiply color of light (1 does nothing)\n"
3914 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
3915 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
3916 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
3917 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3918 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3919 "shadows 1/0 : turn on/off shadows\n"
3920 "corona n : set corona intensity\n"
3921 "coronasize n : set corona size (0-1)\n"
3922 "ambient n : set ambient intensity (0-1)\n"
3923 "diffuse n : set diffuse intensity (0-1)\n"
3924 "specular n : set specular intensity (0-1)\n"
3925 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
3926 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
3927 "<nothing> : print light properties to console\n"
3931 void R_Shadow_EditLights_CopyInfo_f(void)
3933 if (!r_editlights.integer)
3935 Con_Print("Cannot copy light info when not in editing mode. Set r_editlights to 1.\n");
3938 if (!r_shadow_selectedlight)
3940 Con_Print("No selected light.\n");
3943 VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
3944 VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
3945 r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
3946 r_shadow_bufferlight.style = r_shadow_selectedlight->style;
3947 if (r_shadow_selectedlight->cubemapname)
3948 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
3950 r_shadow_bufferlight.cubemapname[0] = 0;
3951 r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
3952 r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
3953 r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
3954 r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
3955 r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
3956 r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
3957 r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
3960 void R_Shadow_EditLights_PasteInfo_f(void)
3962 if (!r_editlights.integer)
3964 Con_Print("Cannot paste light info when not in editing mode. Set r_editlights to 1.\n");
3967 if (!r_shadow_selectedlight)
3969 Con_Print("No selected light.\n");
3972 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);
3975 void R_Shadow_EditLights_Init(void)
3977 Cvar_RegisterVariable(&r_editlights);
3978 Cvar_RegisterVariable(&r_editlights_cursordistance);
3979 Cvar_RegisterVariable(&r_editlights_cursorpushback);
3980 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3981 Cvar_RegisterVariable(&r_editlights_cursorgrid);
3982 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3983 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
3984 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
3985 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)");
3986 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
3987 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
3988 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
3989 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)");
3990 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
3991 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
3992 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
3993 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
3994 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
3995 Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
3996 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)");