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;
196 // current light's culling planes
197 int r_shadow_rtlight_numfrustumplanes;
198 mplane_t r_shadow_rtlight_frustumplanes[12+6+6]; // see R_Shadow_ComputeShadowCasterCullingPlanes
200 rtexturepool_t *r_shadow_texturepool;
201 rtexture_t *r_shadow_attenuationgradienttexture;
202 rtexture_t *r_shadow_attenuation2dtexture;
203 rtexture_t *r_shadow_attenuation3dtexture;
205 // lights are reloaded when this changes
206 char r_shadow_mapname[MAX_QPATH];
208 // used only for light filters (cubemaps)
209 rtexturepool_t *r_shadow_filters_texturepool;
211 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"};
212 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"};
213 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
214 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
215 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)"};
216 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"};
217 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
218 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
219 cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
220 cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
221 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
222 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
223 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
224 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
225 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)"};
226 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
227 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
228 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
229 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
230 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)"};
231 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"};
232 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"};
233 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
234 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
235 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"};
236 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
237 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
238 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)"};
239 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
240 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
241 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)"};
242 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)"};
243 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
244 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
245 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
246 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
247 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
248 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
249 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
250 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
252 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
253 #define ATTENTABLESIZE 256
254 // 1D gradient, 2D circle and 3D sphere attenuation textures
255 #define ATTEN1DSIZE 32
256 #define ATTEN2DSIZE 64
257 #define ATTEN3DSIZE 32
259 static float r_shadow_attendividebias; // r_shadow_lightattenuationdividebias
260 static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
261 static float r_shadow_attentable[ATTENTABLESIZE+1];
263 rtlight_t *r_shadow_compilingrtlight;
264 dlight_t *r_shadow_worldlightchain;
265 dlight_t *r_shadow_selectedlight;
266 dlight_t r_shadow_bufferlight;
267 vec3_t r_editlights_cursorlocation;
269 extern int con_vislines;
271 typedef struct cubemapinfo_s
278 #define MAX_CUBEMAPS 256
279 static int numcubemaps;
280 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
282 void R_Shadow_UncompileWorldLights(void);
283 void R_Shadow_ClearWorldLights(void);
284 void R_Shadow_SaveWorldLights(void);
285 void R_Shadow_LoadWorldLights(void);
286 void R_Shadow_LoadLightsFile(void);
287 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
288 void R_Shadow_EditLights_Reload_f(void);
289 void R_Shadow_ValidateCvars(void);
290 static void R_Shadow_MakeTextures(void);
292 void r_shadow_start(void)
294 // allocate vertex processing arrays
296 r_shadow_attenuationgradienttexture = NULL;
297 r_shadow_attenuation2dtexture = NULL;
298 r_shadow_attenuation3dtexture = NULL;
299 r_shadow_texturepool = NULL;
300 r_shadow_filters_texturepool = NULL;
301 R_Shadow_ValidateCvars();
302 R_Shadow_MakeTextures();
303 maxshadowtriangles = 0;
304 shadowelements = NULL;
305 maxshadowvertices = 0;
306 shadowvertex3f = NULL;
314 shadowmarklist = NULL;
316 r_shadow_buffer_numleafpvsbytes = 0;
317 r_shadow_buffer_leafpvs = NULL;
318 r_shadow_buffer_leaflist = NULL;
319 r_shadow_buffer_numsurfacepvsbytes = 0;
320 r_shadow_buffer_surfacepvs = NULL;
321 r_shadow_buffer_surfacelist = NULL;
322 r_shadow_buffer_numshadowtrispvsbytes = 0;
323 r_shadow_buffer_shadowtrispvs = NULL;
324 r_shadow_buffer_numlighttrispvsbytes = 0;
325 r_shadow_buffer_lighttrispvs = NULL;
328 void r_shadow_shutdown(void)
330 R_Shadow_UncompileWorldLights();
332 r_shadow_attenuationgradienttexture = NULL;
333 r_shadow_attenuation2dtexture = NULL;
334 r_shadow_attenuation3dtexture = NULL;
335 R_FreeTexturePool(&r_shadow_texturepool);
336 R_FreeTexturePool(&r_shadow_filters_texturepool);
337 maxshadowtriangles = 0;
339 Mem_Free(shadowelements);
340 shadowelements = NULL;
342 Mem_Free(shadowvertex3f);
343 shadowvertex3f = NULL;
346 Mem_Free(vertexupdate);
349 Mem_Free(vertexremap);
355 Mem_Free(shadowmark);
358 Mem_Free(shadowmarklist);
359 shadowmarklist = NULL;
361 r_shadow_buffer_numleafpvsbytes = 0;
362 if (r_shadow_buffer_leafpvs)
363 Mem_Free(r_shadow_buffer_leafpvs);
364 r_shadow_buffer_leafpvs = NULL;
365 if (r_shadow_buffer_leaflist)
366 Mem_Free(r_shadow_buffer_leaflist);
367 r_shadow_buffer_leaflist = NULL;
368 r_shadow_buffer_numsurfacepvsbytes = 0;
369 if (r_shadow_buffer_surfacepvs)
370 Mem_Free(r_shadow_buffer_surfacepvs);
371 r_shadow_buffer_surfacepvs = NULL;
372 if (r_shadow_buffer_surfacelist)
373 Mem_Free(r_shadow_buffer_surfacelist);
374 r_shadow_buffer_surfacelist = NULL;
375 r_shadow_buffer_numshadowtrispvsbytes = 0;
376 if (r_shadow_buffer_shadowtrispvs)
377 Mem_Free(r_shadow_buffer_shadowtrispvs);
378 r_shadow_buffer_numlighttrispvsbytes = 0;
379 if (r_shadow_buffer_lighttrispvs)
380 Mem_Free(r_shadow_buffer_lighttrispvs);
383 void r_shadow_newmap(void)
387 void R_Shadow_Help_f(void)
390 "Documentation on r_shadow system:\n"
392 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
393 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
394 "r_shadow_debuglight : render only this light number (-1 = all)\n"
395 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
396 "r_shadow_gloss2intensity : brightness of forced gloss\n"
397 "r_shadow_glossintensity : brightness of textured gloss\n"
398 "r_shadow_lightattenuationlinearscale : used to generate attenuation texture\n"
399 "r_shadow_lightattenuationdividebias : used to generate attenuation texture\n"
400 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
401 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
402 "r_shadow_portallight : use portal visibility for static light precomputation\n"
403 "r_shadow_projectdistance : shadow volume projection distance\n"
404 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
405 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
406 "r_shadow_realtime_world : use high quality world lighting mode\n"
407 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
408 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
409 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
410 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
411 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
412 "r_shadow_scissor : use scissor optimization\n"
413 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
414 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
415 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
416 "r_showlighting : useful for performance testing; bright = slow!\n"
417 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
419 "r_shadow_help : this help\n"
423 void R_Shadow_Init(void)
425 Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
426 Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
427 Cvar_RegisterVariable(&r_shadow_usenormalmap);
428 Cvar_RegisterVariable(&r_shadow_debuglight);
429 Cvar_RegisterVariable(&r_shadow_gloss);
430 Cvar_RegisterVariable(&r_shadow_gloss2intensity);
431 Cvar_RegisterVariable(&r_shadow_glossintensity);
432 Cvar_RegisterVariable(&r_shadow_glossexponent);
433 Cvar_RegisterVariable(&r_shadow_lightattenuationdividebias);
434 Cvar_RegisterVariable(&r_shadow_lightattenuationlinearscale);
435 Cvar_RegisterVariable(&r_shadow_lightintensityscale);
436 Cvar_RegisterVariable(&r_shadow_lightradiusscale);
437 Cvar_RegisterVariable(&r_shadow_portallight);
438 Cvar_RegisterVariable(&r_shadow_projectdistance);
439 Cvar_RegisterVariable(&r_shadow_frontsidecasting);
440 Cvar_RegisterVariable(&r_shadow_realtime_dlight);
441 Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
442 Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
443 Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
444 Cvar_RegisterVariable(&r_shadow_realtime_world);
445 Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
446 Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
447 Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
448 Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
449 Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
450 Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
451 Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
452 Cvar_RegisterVariable(&r_shadow_scissor);
453 Cvar_RegisterVariable(&r_shadow_culltriangles);
454 Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
455 Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
456 Cvar_RegisterVariable(&r_shadow_texture3d);
457 Cvar_RegisterVariable(&gl_ext_separatestencil);
458 Cvar_RegisterVariable(&gl_ext_stenciltwoside);
459 if (gamemode == GAME_TENEBRAE)
461 Cvar_SetValue("r_shadow_gloss", 2);
462 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
464 Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
465 R_Shadow_EditLights_Init();
466 r_shadow_worldlightchain = NULL;
467 maxshadowtriangles = 0;
468 shadowelements = NULL;
469 maxshadowvertices = 0;
470 shadowvertex3f = NULL;
478 shadowmarklist = NULL;
480 r_shadow_buffer_numleafpvsbytes = 0;
481 r_shadow_buffer_leafpvs = NULL;
482 r_shadow_buffer_leaflist = NULL;
483 r_shadow_buffer_numsurfacepvsbytes = 0;
484 r_shadow_buffer_surfacepvs = NULL;
485 r_shadow_buffer_surfacelist = NULL;
486 r_shadow_buffer_shadowtrispvs = NULL;
487 r_shadow_buffer_lighttrispvs = NULL;
488 R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
491 matrix4x4_t matrix_attenuationxyz =
494 {0.5, 0.0, 0.0, 0.5},
495 {0.0, 0.5, 0.0, 0.5},
496 {0.0, 0.0, 0.5, 0.5},
501 matrix4x4_t matrix_attenuationz =
504 {0.0, 0.0, 0.5, 0.5},
505 {0.0, 0.0, 0.0, 0.5},
506 {0.0, 0.0, 0.0, 0.5},
511 void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
513 // make sure shadowelements is big enough for this volume
514 if (maxshadowtriangles < numtriangles)
516 maxshadowtriangles = numtriangles;
518 Mem_Free(shadowelements);
519 shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
521 // make sure shadowvertex3f is big enough for this volume
522 if (maxshadowvertices < numvertices)
524 maxshadowvertices = numvertices;
526 Mem_Free(shadowvertex3f);
527 shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
531 static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
533 int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
534 int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
535 int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
536 int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
537 if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
539 if (r_shadow_buffer_leafpvs)
540 Mem_Free(r_shadow_buffer_leafpvs);
541 if (r_shadow_buffer_leaflist)
542 Mem_Free(r_shadow_buffer_leaflist);
543 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
544 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
545 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
547 if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
549 if (r_shadow_buffer_surfacepvs)
550 Mem_Free(r_shadow_buffer_surfacepvs);
551 if (r_shadow_buffer_surfacelist)
552 Mem_Free(r_shadow_buffer_surfacelist);
553 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
554 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
555 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
557 if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
559 if (r_shadow_buffer_shadowtrispvs)
560 Mem_Free(r_shadow_buffer_shadowtrispvs);
561 r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
562 r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
564 if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
566 if (r_shadow_buffer_lighttrispvs)
567 Mem_Free(r_shadow_buffer_lighttrispvs);
568 r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
569 r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
573 void R_Shadow_PrepareShadowMark(int numtris)
575 // make sure shadowmark is big enough for this volume
576 if (maxshadowmark < numtris)
578 maxshadowmark = numtris;
580 Mem_Free(shadowmark);
582 Mem_Free(shadowmarklist);
583 shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
584 shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
588 // if shadowmarkcount wrapped we clear the array and adjust accordingly
589 if (shadowmarkcount == 0)
592 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
597 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)
600 int outtriangles = 0, outvertices = 0;
603 float ratio, direction[3], projectvector[3];
605 if (projectdirection)
606 VectorScale(projectdirection, projectdistance, projectvector);
608 VectorClear(projectvector);
610 if (maxvertexupdate < innumvertices)
612 maxvertexupdate = innumvertices;
614 Mem_Free(vertexupdate);
616 Mem_Free(vertexremap);
617 vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
618 vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
622 if (vertexupdatenum == 0)
625 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
626 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
629 for (i = 0;i < numshadowmarktris;i++)
630 shadowmark[shadowmarktris[i]] = shadowmarkcount;
632 // create the vertices
633 if (projectdirection)
635 for (i = 0;i < numshadowmarktris;i++)
637 element = inelement3i + shadowmarktris[i] * 3;
638 for (j = 0;j < 3;j++)
640 if (vertexupdate[element[j]] != vertexupdatenum)
642 vertexupdate[element[j]] = vertexupdatenum;
643 vertexremap[element[j]] = outvertices;
644 vertex = invertex3f + element[j] * 3;
645 // project one copy of the vertex according to projectvector
646 VectorCopy(vertex, outvertex3f);
647 VectorAdd(vertex, projectvector, (outvertex3f + 3));
656 for (i = 0;i < numshadowmarktris;i++)
658 element = inelement3i + shadowmarktris[i] * 3;
659 for (j = 0;j < 3;j++)
661 if (vertexupdate[element[j]] != vertexupdatenum)
663 vertexupdate[element[j]] = vertexupdatenum;
664 vertexremap[element[j]] = outvertices;
665 vertex = invertex3f + element[j] * 3;
666 // project one copy of the vertex to the sphere radius of the light
667 // (FIXME: would projecting it to the light box be better?)
668 VectorSubtract(vertex, projectorigin, direction);
669 ratio = projectdistance / VectorLength(direction);
670 VectorCopy(vertex, outvertex3f);
671 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
679 if (r_shadow_frontsidecasting.integer)
681 for (i = 0;i < numshadowmarktris;i++)
683 int remappedelement[3];
685 const int *neighbortriangle;
687 markindex = shadowmarktris[i] * 3;
688 element = inelement3i + markindex;
689 neighbortriangle = inneighbor3i + markindex;
690 // output the front and back triangles
691 outelement3i[0] = vertexremap[element[0]];
692 outelement3i[1] = vertexremap[element[1]];
693 outelement3i[2] = vertexremap[element[2]];
694 outelement3i[3] = vertexremap[element[2]] + 1;
695 outelement3i[4] = vertexremap[element[1]] + 1;
696 outelement3i[5] = vertexremap[element[0]] + 1;
700 // output the sides (facing outward from this triangle)
701 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
703 remappedelement[0] = vertexremap[element[0]];
704 remappedelement[1] = vertexremap[element[1]];
705 outelement3i[0] = remappedelement[1];
706 outelement3i[1] = remappedelement[0];
707 outelement3i[2] = remappedelement[0] + 1;
708 outelement3i[3] = remappedelement[1];
709 outelement3i[4] = remappedelement[0] + 1;
710 outelement3i[5] = remappedelement[1] + 1;
715 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
717 remappedelement[1] = vertexremap[element[1]];
718 remappedelement[2] = vertexremap[element[2]];
719 outelement3i[0] = remappedelement[2];
720 outelement3i[1] = remappedelement[1];
721 outelement3i[2] = remappedelement[1] + 1;
722 outelement3i[3] = remappedelement[2];
723 outelement3i[4] = remappedelement[1] + 1;
724 outelement3i[5] = remappedelement[2] + 1;
729 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
731 remappedelement[0] = vertexremap[element[0]];
732 remappedelement[2] = vertexremap[element[2]];
733 outelement3i[0] = remappedelement[0];
734 outelement3i[1] = remappedelement[2];
735 outelement3i[2] = remappedelement[2] + 1;
736 outelement3i[3] = remappedelement[0];
737 outelement3i[4] = remappedelement[2] + 1;
738 outelement3i[5] = remappedelement[0] + 1;
747 for (i = 0;i < numshadowmarktris;i++)
749 int remappedelement[3];
751 const int *neighbortriangle;
753 markindex = shadowmarktris[i] * 3;
754 element = inelement3i + markindex;
755 neighbortriangle = inneighbor3i + markindex;
756 // output the front and back triangles
757 outelement3i[0] = vertexremap[element[2]];
758 outelement3i[1] = vertexremap[element[1]];
759 outelement3i[2] = vertexremap[element[0]];
760 outelement3i[3] = vertexremap[element[0]] + 1;
761 outelement3i[4] = vertexremap[element[1]] + 1;
762 outelement3i[5] = vertexremap[element[2]] + 1;
766 // output the sides (facing outward from this triangle)
767 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
769 remappedelement[0] = vertexremap[element[0]];
770 remappedelement[1] = vertexremap[element[1]];
771 outelement3i[0] = remappedelement[0];
772 outelement3i[1] = remappedelement[1];
773 outelement3i[2] = remappedelement[1] + 1;
774 outelement3i[3] = remappedelement[0];
775 outelement3i[4] = remappedelement[1] + 1;
776 outelement3i[5] = remappedelement[0] + 1;
781 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
783 remappedelement[1] = vertexremap[element[1]];
784 remappedelement[2] = vertexremap[element[2]];
785 outelement3i[0] = remappedelement[1];
786 outelement3i[1] = remappedelement[2];
787 outelement3i[2] = remappedelement[2] + 1;
788 outelement3i[3] = remappedelement[1];
789 outelement3i[4] = remappedelement[2] + 1;
790 outelement3i[5] = remappedelement[1] + 1;
795 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
797 remappedelement[0] = vertexremap[element[0]];
798 remappedelement[2] = vertexremap[element[2]];
799 outelement3i[0] = remappedelement[2];
800 outelement3i[1] = remappedelement[0];
801 outelement3i[2] = remappedelement[0] + 1;
802 outelement3i[3] = remappedelement[2];
803 outelement3i[4] = remappedelement[0] + 1;
804 outelement3i[5] = remappedelement[2] + 1;
812 *outnumvertices = outvertices;
816 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)
819 if (projectdistance < 0.1)
821 Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
824 if (!numverts || !nummarktris)
826 // make sure shadowelements is big enough for this volume
827 if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
828 R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
829 tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
830 r_refdef.stats.lights_dynamicshadowtriangles += tris;
831 R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
834 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)
840 if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
842 tend = firsttriangle + numtris;
843 if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
845 // surface box entirely inside light box, no box cull
846 if (projectdirection)
848 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
850 TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
851 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
852 shadowmarklist[numshadowmark++] = t;
857 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
858 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
859 shadowmarklist[numshadowmark++] = t;
864 // surface box not entirely inside light box, cull each triangle
865 if (projectdirection)
867 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
869 v[0] = invertex3f + e[0] * 3;
870 v[1] = invertex3f + e[1] * 3;
871 v[2] = invertex3f + e[2] * 3;
872 TriangleNormal(v[0], v[1], v[2], normal);
873 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
874 && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
875 shadowmarklist[numshadowmark++] = t;
880 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
882 v[0] = invertex3f + e[0] * 3;
883 v[1] = invertex3f + e[1] * 3;
884 v[2] = invertex3f + e[2] * 3;
885 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
886 && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
887 shadowmarklist[numshadowmark++] = t;
893 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
895 if (r_shadow_compilingrtlight)
897 // if we're compiling an rtlight, capture the mesh
898 Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
901 r_refdef.stats.lights_shadowtriangles += numtriangles;
903 R_Mesh_VertexPointer(vertex3f);
904 GL_LockArrays(0, numvertices);
905 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
907 // decrement stencil if backface is behind depthbuffer
908 GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
909 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
910 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
911 // increment stencil if frontface is behind depthbuffer
912 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
913 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
915 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
920 static unsigned char R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
922 float dist = sqrt(x*x+y*y+z*z);
923 float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
924 return (unsigned char)bound(0, intensity * 256.0f, 255);
927 static void R_Shadow_MakeTextures(void)
930 float intensity, dist;
932 unsigned int palette[256];
933 R_FreeTexturePool(&r_shadow_texturepool);
934 r_shadow_texturepool = R_AllocTexturePool();
935 r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
936 r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
937 // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
938 for (x = 0;x < 256;x++)
939 palette[x] = x * 0x01010101;
940 data = (unsigned char *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE));
941 // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
942 for (x = 0;x <= ATTENTABLESIZE;x++)
944 dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
945 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
946 r_shadow_attentable[x] = bound(0, intensity, 1);
948 // 1D gradient texture
949 for (x = 0;x < ATTEN1DSIZE;x++)
950 data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
951 r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
953 for (y = 0;y < ATTEN2DSIZE;y++)
954 for (x = 0;x < ATTEN2DSIZE;x++)
955 data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
956 r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
958 if (r_shadow_texture3d.integer && gl_texture3d)
960 for (z = 0;z < ATTEN3DSIZE;z++)
961 for (y = 0;y < ATTEN3DSIZE;y++)
962 for (x = 0;x < ATTEN3DSIZE;x++)
963 data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
964 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_PALETTE, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, palette);
967 r_shadow_attenuation3dtexture = NULL;
971 void R_Shadow_ValidateCvars(void)
973 if (r_shadow_texture3d.integer && !gl_texture3d)
974 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
975 if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
976 Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
977 if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
978 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
981 // light currently being rendered
982 rtlight_t *r_shadow_rtlight;
984 // this is the location of the light in entity space
985 vec3_t r_shadow_entitylightorigin;
986 // this transforms entity coordinates to light filter cubemap coordinates
987 // (also often used for other purposes)
988 matrix4x4_t r_shadow_entitytolight;
989 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
990 // of attenuation texturing in full 3D (Z result often ignored)
991 matrix4x4_t r_shadow_entitytoattenuationxyz;
992 // this transforms only the Z to S, and T is always 0.5
993 matrix4x4_t r_shadow_entitytoattenuationz;
995 void R_Shadow_RenderMode_Begin(void)
997 R_Shadow_ValidateCvars();
999 if (!r_shadow_attenuation2dtexture
1000 || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
1001 || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
1002 || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
1003 R_Shadow_MakeTextures();
1006 R_Mesh_ColorPointer(NULL);
1007 R_Mesh_ResetTextureState();
1008 GL_BlendFunc(GL_ONE, GL_ZERO);
1010 GL_DepthMask(false);
1011 GL_Color(0, 0, 0, 1);
1012 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1014 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1016 if (gl_ext_separatestencil.integer)
1017 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
1018 else if (gl_ext_stenciltwoside.integer)
1019 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
1021 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
1023 if (r_glsl.integer && gl_support_fragment_shader)
1024 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
1025 else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1026 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
1028 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
1031 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
1033 r_shadow_rtlight = rtlight;
1036 void R_Shadow_RenderMode_Reset(void)
1039 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1041 qglUseProgramObjectARB(0);CHECKGLERROR
1043 else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1045 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1047 R_Mesh_ColorPointer(NULL);
1048 R_Mesh_ResetTextureState();
1050 GL_DepthMask(false);
1051 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1052 qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
1053 qglDisable(GL_STENCIL_TEST);CHECKGLERROR
1054 qglStencilMask(~0);CHECKGLERROR
1055 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
1056 qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
1057 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
1058 GL_Color(1, 1, 1, 1);
1059 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
1060 GL_BlendFunc(GL_ONE, GL_ZERO);
1063 void R_Shadow_RenderMode_StencilShadowVolumes(void)
1066 R_Shadow_RenderMode_Reset();
1067 GL_ColorMask(0, 0, 0, 0);
1068 qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1069 qglDepthFunc(GL_LESS);CHECKGLERROR
1070 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1071 r_shadow_rendermode = r_shadow_shadowingrendermode;
1072 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
1074 GL_CullFace(GL_NONE);
1075 qglStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR // quake is backwards, this is front faces
1076 qglStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR // quake is backwards, this is back faces
1078 else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1080 GL_CullFace(GL_NONE);
1081 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1082 qglActiveStencilFaceEXT(GL_BACK);CHECKGLERROR // quake is backwards, this is front faces
1083 qglStencilMask(~0);CHECKGLERROR
1084 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1085 qglActiveStencilFaceEXT(GL_FRONT);CHECKGLERROR // quake is backwards, this is back faces
1086 qglStencilMask(~0);CHECKGLERROR
1087 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1089 GL_Clear(GL_STENCIL_BUFFER_BIT);
1090 r_refdef.stats.lights_clears++;
1093 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
1096 R_Shadow_RenderMode_Reset();
1097 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1100 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1104 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1105 // only draw light where this geometry was already rendered AND the
1106 // stencil is 128 (values other than this mean shadow)
1107 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1109 r_shadow_rendermode = r_shadow_lightingrendermode;
1110 // do global setup needed for the chosen lighting mode
1111 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1113 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
1114 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
1115 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
1116 R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap)); // light filter
1117 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
1118 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
1119 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
1120 R_Mesh_TexBind(7, R_GetTexture(r_texture_white)); // lightmap
1121 R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap)); // deluxemap
1122 R_Mesh_TexBind(9, R_GetTexture(r_texture_black)); // glow
1123 //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
1124 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1125 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1130 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1133 R_Shadow_RenderMode_Reset();
1134 GL_BlendFunc(GL_ONE, GL_ONE);
1135 GL_DepthTest(r_showshadowvolumes.integer < 2);
1136 GL_Color(0.0, 0.0125 * r_view.colorscale, 0.1 * r_view.colorscale, 1);
1137 qglPolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1138 GL_CullFace(GL_NONE);
1139 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1142 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1145 R_Shadow_RenderMode_Reset();
1146 GL_BlendFunc(GL_ONE, GL_ONE);
1147 GL_DepthTest(r_showlighting.integer < 2);
1148 GL_Color(0.1 * r_view.colorscale, 0.0125 * r_view.colorscale, 0, 1);
1151 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1155 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1156 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1158 r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1161 void R_Shadow_RenderMode_End(void)
1164 R_Shadow_RenderMode_Reset();
1165 R_Shadow_RenderMode_ActiveLight(NULL);
1167 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1168 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1171 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1173 int i, ix1, iy1, ix2, iy2;
1174 float x1, y1, x2, y2;
1177 mplane_t planes[11];
1178 float vertex3f[256*3];
1180 // if view is inside the light box, just say yes it's visible
1181 if (BoxesOverlap(r_view.origin, r_view.origin, mins, maxs))
1183 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1187 // create a temporary brush describing the area the light can affect in worldspace
1188 VectorNegate(r_view.frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -r_view.frustum[0].dist;
1189 VectorNegate(r_view.frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -r_view.frustum[1].dist;
1190 VectorNegate(r_view.frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -r_view.frustum[2].dist;
1191 VectorNegate(r_view.frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -r_view.frustum[3].dist;
1192 VectorNegate(r_view.frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -r_view.frustum[4].dist;
1193 VectorSet (planes[ 5].normal, 1, 0, 0); planes[ 5].dist = maxs[0];
1194 VectorSet (planes[ 6].normal, -1, 0, 0); planes[ 6].dist = -mins[0];
1195 VectorSet (planes[ 7].normal, 0, 1, 0); planes[ 7].dist = maxs[1];
1196 VectorSet (planes[ 8].normal, 0, -1, 0); planes[ 8].dist = -mins[1];
1197 VectorSet (planes[ 9].normal, 0, 0, 1); planes[ 9].dist = maxs[2];
1198 VectorSet (planes[10].normal, 0, 0, -1); planes[10].dist = -mins[2];
1200 // turn the brush into a mesh
1201 memset(&mesh, 0, sizeof(rmesh_t));
1202 mesh.maxvertices = 256;
1203 mesh.vertex3f = vertex3f;
1204 mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
1205 R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
1207 // if that mesh is empty, the light is not visible at all
1208 if (!mesh.numvertices)
1211 if (!r_shadow_scissor.integer)
1214 // if that mesh is not empty, check what area of the screen it covers
1215 x1 = y1 = x2 = y2 = 0;
1217 //Con_Printf("%i vertices to transform...\n", mesh.numvertices);
1218 for (i = 0;i < mesh.numvertices;i++)
1220 VectorCopy(mesh.vertex3f + i * 3, v);
1221 GL_TransformToScreen(v, v2);
1222 //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]);
1225 if (x1 > v2[0]) x1 = v2[0];
1226 if (x2 < v2[0]) x2 = v2[0];
1227 if (y1 > v2[1]) y1 = v2[1];
1228 if (y2 < v2[1]) y2 = v2[1];
1237 // now convert the scissor rectangle to integer screen coordinates
1238 ix1 = (int)(x1 - 1.0f);
1239 iy1 = (int)(y1 - 1.0f);
1240 ix2 = (int)(x2 + 1.0f);
1241 iy2 = (int)(y2 + 1.0f);
1242 //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1244 // clamp it to the screen
1245 if (ix1 < r_view.x) ix1 = r_view.x;
1246 if (iy1 < r_view.y) iy1 = r_view.y;
1247 if (ix2 > r_view.x + r_view.width) ix2 = r_view.x + r_view.width;
1248 if (iy2 > r_view.y + r_view.height) iy2 = r_view.y + r_view.height;
1250 // if it is inside out, it's not visible
1251 if (ix2 <= ix1 || iy2 <= iy1)
1254 // the light area is visible, set up the scissor rectangle
1255 GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1256 //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1257 //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1258 r_refdef.stats.lights_scissored++;
1262 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1264 float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1265 float *normal3f = rsurface_normal3f + 3 * firstvertex;
1266 float *color4f = rsurface_array_color4f + 4 * firstvertex;
1267 float dist, dot, distintensity, shadeintensity, v[3], n[3];
1268 if (r_textureunits.integer >= 3)
1270 if (VectorLength2(diffusecolor) > 0)
1272 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1274 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1275 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1276 if ((dot = DotProduct(n, v)) < 0)
1278 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1279 VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
1282 VectorCopy(ambientcolor, color4f);
1283 if (r_refdef.fogenabled)
1285 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1286 VectorScale(color4f, f, color4f);
1293 for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1295 VectorCopy(ambientcolor, color4f);
1296 if (r_refdef.fogenabled)
1299 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1300 f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1301 VectorScale(color4f, f, color4f);
1307 else if (r_textureunits.integer >= 2)
1309 if (VectorLength2(diffusecolor) > 0)
1311 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1313 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1314 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1316 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1317 if ((dot = DotProduct(n, v)) < 0)
1319 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1320 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1321 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1322 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1326 color4f[0] = ambientcolor[0] * distintensity;
1327 color4f[1] = ambientcolor[1] * distintensity;
1328 color4f[2] = ambientcolor[2] * distintensity;
1330 if (r_refdef.fogenabled)
1332 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1333 VectorScale(color4f, f, color4f);
1337 VectorClear(color4f);
1343 for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1345 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1346 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1348 color4f[0] = ambientcolor[0] * distintensity;
1349 color4f[1] = ambientcolor[1] * distintensity;
1350 color4f[2] = ambientcolor[2] * distintensity;
1351 if (r_refdef.fogenabled)
1353 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1354 VectorScale(color4f, f, color4f);
1358 VectorClear(color4f);
1365 if (VectorLength2(diffusecolor) > 0)
1367 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1369 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1370 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1372 distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1373 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1374 if ((dot = DotProduct(n, v)) < 0)
1376 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1377 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1378 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1379 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1383 color4f[0] = ambientcolor[0] * distintensity;
1384 color4f[1] = ambientcolor[1] * distintensity;
1385 color4f[2] = ambientcolor[2] * distintensity;
1387 if (r_refdef.fogenabled)
1389 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1390 VectorScale(color4f, f, color4f);
1394 VectorClear(color4f);
1400 for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1402 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1403 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1405 distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1406 color4f[0] = ambientcolor[0] * distintensity;
1407 color4f[1] = ambientcolor[1] * distintensity;
1408 color4f[2] = ambientcolor[2] * distintensity;
1409 if (r_refdef.fogenabled)
1411 float f = VERTEXFOGTABLE(VectorDistance(v, rsurface_modelorg));
1412 VectorScale(color4f, f, color4f);
1416 VectorClear(color4f);
1423 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1425 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1428 float *out3f = rsurface_array_texcoord3f + 3 * firstvertex;
1429 const float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1430 const float *svector3f = rsurface_svector3f + 3 * firstvertex;
1431 const float *tvector3f = rsurface_tvector3f + 3 * firstvertex;
1432 const float *normal3f = rsurface_normal3f + 3 * firstvertex;
1434 for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1436 VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
1437 // the cubemap normalizes this for us
1438 out3f[0] = DotProduct(svector3f, lightdir);
1439 out3f[1] = DotProduct(tvector3f, lightdir);
1440 out3f[2] = DotProduct(normal3f, lightdir);
1444 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1447 float *out3f = rsurface_array_texcoord3f + 3 * firstvertex;
1448 const float *vertex3f = rsurface_vertex3f + 3 * firstvertex;
1449 const float *svector3f = rsurface_svector3f + 3 * firstvertex;
1450 const float *tvector3f = rsurface_tvector3f + 3 * firstvertex;
1451 const float *normal3f = rsurface_normal3f + 3 * firstvertex;
1452 float lightdir[3], eyedir[3], halfdir[3];
1453 for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1455 VectorSubtract(r_shadow_entitylightorigin, vertex3f, lightdir);
1456 VectorNormalize(lightdir);
1457 VectorSubtract(rsurface_modelorg, vertex3f, eyedir);
1458 VectorNormalize(eyedir);
1459 VectorAdd(lightdir, eyedir, halfdir);
1460 // the cubemap normalizes this for us
1461 out3f[0] = DotProduct(svector3f, halfdir);
1462 out3f[1] = DotProduct(tvector3f, halfdir);
1463 out3f[2] = DotProduct(normal3f, halfdir);
1467 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1469 // used to display how many times a surface is lit for level design purposes
1470 GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
1471 R_Mesh_ColorPointer(NULL);
1472 R_Mesh_ResetTextureState();
1473 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1476 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1478 // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1479 R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale);
1480 R_Mesh_TexCoordPointer(0, 2, rsurface_model->surfmesh.data_texcoordtexture2f);
1481 R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
1482 R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
1483 R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
1484 if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1486 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1488 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1489 if (rsurface_texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1491 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1495 static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, float r, float g, float b)
1497 // shared final code for all the dot3 layers
1499 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1500 for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1502 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1503 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1507 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)
1510 // colorscale accounts for how much we multiply the brightness
1513 // mult is how many times the final pass of the lighting will be
1514 // performed to get more brightness than otherwise possible.
1516 // Limit mult to 64 for sanity sake.
1518 if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1520 // 3 3D combine path (Geforce3, Radeon 8500)
1521 memset(&m, 0, sizeof(m));
1522 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1523 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1524 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1525 m.tex[1] = R_GetTexture(basetexture);
1526 m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
1527 m.texmatrix[1] = rsurface_texture->currenttexmatrix;
1528 m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1529 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1530 m.texmatrix[2] = r_shadow_entitytolight;
1531 GL_BlendFunc(GL_ONE, GL_ONE);
1533 else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1535 // 2 3D combine path (Geforce3, original Radeon)
1536 memset(&m, 0, sizeof(m));
1537 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1538 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1539 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1540 m.tex[1] = R_GetTexture(basetexture);
1541 m.pointer_texcoord[1] = rsurface_model->surfmesh.data_texcoordtexture2f;
1542 m.texmatrix[1] = rsurface_texture->currenttexmatrix;
1543 GL_BlendFunc(GL_ONE, GL_ONE);
1545 else if (r_textureunits.integer >= 4 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1547 // 4 2D combine path (Geforce3, Radeon 8500)
1548 memset(&m, 0, sizeof(m));
1549 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1550 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1551 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1552 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1553 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1554 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1555 m.tex[2] = R_GetTexture(basetexture);
1556 m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
1557 m.texmatrix[2] = rsurface_texture->currenttexmatrix;
1558 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1560 m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1561 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1562 m.texmatrix[3] = r_shadow_entitytolight;
1564 GL_BlendFunc(GL_ONE, GL_ONE);
1566 else if (r_textureunits.integer >= 3 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1568 // 3 2D combine path (Geforce3, original Radeon)
1569 memset(&m, 0, sizeof(m));
1570 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1571 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1572 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1573 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1574 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1575 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1576 m.tex[2] = R_GetTexture(basetexture);
1577 m.pointer_texcoord[2] = rsurface_model->surfmesh.data_texcoordtexture2f;
1578 m.texmatrix[2] = rsurface_texture->currenttexmatrix;
1579 GL_BlendFunc(GL_ONE, GL_ONE);
1583 // 2/2/2 2D combine path (any dot3 card)
1584 memset(&m, 0, sizeof(m));
1585 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1586 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1587 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1588 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1589 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1590 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1591 R_Mesh_TextureState(&m);
1592 GL_ColorMask(0,0,0,1);
1593 GL_BlendFunc(GL_ONE, GL_ZERO);
1594 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1597 memset(&m, 0, sizeof(m));
1598 m.tex[0] = R_GetTexture(basetexture);
1599 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1600 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1601 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1603 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1604 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1605 m.texmatrix[1] = r_shadow_entitytolight;
1607 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1609 // this final code is shared
1610 R_Mesh_TextureState(&m);
1611 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1614 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)
1617 // colorscale accounts for how much we multiply the brightness
1620 // mult is how many times the final pass of the lighting will be
1621 // performed to get more brightness than otherwise possible.
1623 // Limit mult to 64 for sanity sake.
1625 // generate normalization cubemap texcoords
1626 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1627 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1629 // 3/2 3D combine path (Geforce3, Radeon 8500)
1630 memset(&m, 0, sizeof(m));
1631 m.tex[0] = R_GetTexture(normalmaptexture);
1632 m.texcombinergb[0] = GL_REPLACE;
1633 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1634 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1635 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1636 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1637 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1638 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1639 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1640 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1641 R_Mesh_TextureState(&m);
1642 GL_ColorMask(0,0,0,1);
1643 GL_BlendFunc(GL_ONE, GL_ZERO);
1644 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1647 memset(&m, 0, sizeof(m));
1648 m.tex[0] = R_GetTexture(basetexture);
1649 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1650 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1651 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1653 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1654 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1655 m.texmatrix[1] = r_shadow_entitytolight;
1657 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1659 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1661 // 1/2/2 3D combine path (original Radeon)
1662 memset(&m, 0, sizeof(m));
1663 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1664 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1665 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
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(normalmaptexture);
1674 m.texcombinergb[0] = GL_REPLACE;
1675 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1676 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1677 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1678 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1679 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1680 R_Mesh_TextureState(&m);
1681 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1682 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1685 memset(&m, 0, sizeof(m));
1686 m.tex[0] = R_GetTexture(basetexture);
1687 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1688 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1689 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1691 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1692 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1693 m.texmatrix[1] = r_shadow_entitytolight;
1695 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1697 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1699 // 2/2 3D combine path (original Radeon)
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_ColorMask(0,0,0,1);
1710 GL_BlendFunc(GL_ONE, GL_ZERO);
1711 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1714 memset(&m, 0, sizeof(m));
1715 m.tex[0] = R_GetTexture(basetexture);
1716 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1717 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1718 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1719 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1720 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1721 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1723 else if (r_textureunits.integer >= 4)
1725 // 4/2 2D combine path (Geforce3, Radeon 8500)
1726 memset(&m, 0, sizeof(m));
1727 m.tex[0] = R_GetTexture(normalmaptexture);
1728 m.texcombinergb[0] = GL_REPLACE;
1729 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1730 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1731 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1732 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1733 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1734 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1735 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1736 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1737 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1738 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1739 m.texmatrix[3] = r_shadow_entitytoattenuationz;
1740 R_Mesh_TextureState(&m);
1741 GL_ColorMask(0,0,0,1);
1742 GL_BlendFunc(GL_ONE, GL_ZERO);
1743 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1746 memset(&m, 0, sizeof(m));
1747 m.tex[0] = R_GetTexture(basetexture);
1748 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1749 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1750 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1752 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1753 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1754 m.texmatrix[1] = r_shadow_entitytolight;
1756 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1760 // 2/2/2 2D combine path (any dot3 card)
1761 memset(&m, 0, sizeof(m));
1762 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1763 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1764 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1765 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1766 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1767 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1768 R_Mesh_TextureState(&m);
1769 GL_ColorMask(0,0,0,1);
1770 GL_BlendFunc(GL_ONE, GL_ZERO);
1771 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1774 memset(&m, 0, sizeof(m));
1775 m.tex[0] = R_GetTexture(normalmaptexture);
1776 m.texcombinergb[0] = GL_REPLACE;
1777 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1778 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1779 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1780 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1781 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1782 R_Mesh_TextureState(&m);
1783 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1784 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1787 memset(&m, 0, sizeof(m));
1788 m.tex[0] = R_GetTexture(basetexture);
1789 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1790 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1791 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1793 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1794 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1795 m.texmatrix[1] = r_shadow_entitytolight;
1797 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1799 // this final code is shared
1800 R_Mesh_TextureState(&m);
1801 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1804 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)
1806 float glossexponent;
1808 // FIXME: detect blendsquare!
1809 //if (!gl_support_blendsquare)
1812 // generate normalization cubemap texcoords
1813 R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1814 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1816 // 2/0/0/1/2 3D combine blendsquare path
1817 memset(&m, 0, sizeof(m));
1818 m.tex[0] = R_GetTexture(normalmaptexture);
1819 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1820 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1821 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1822 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1823 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1824 R_Mesh_TextureState(&m);
1825 GL_ColorMask(0,0,0,1);
1826 // this squares the result
1827 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1828 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1830 // second and third pass
1831 R_Mesh_ResetTextureState();
1832 // square alpha in framebuffer a few times to make it shiny
1833 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1834 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1835 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1838 memset(&m, 0, sizeof(m));
1839 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1840 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1841 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1842 R_Mesh_TextureState(&m);
1843 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1844 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1847 memset(&m, 0, sizeof(m));
1848 m.tex[0] = R_GetTexture(glosstexture);
1849 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1850 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1851 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1853 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1854 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1855 m.texmatrix[1] = r_shadow_entitytolight;
1857 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1859 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1861 // 2/0/0/2 3D combine blendsquare path
1862 memset(&m, 0, sizeof(m));
1863 m.tex[0] = R_GetTexture(normalmaptexture);
1864 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1865 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1866 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1867 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1868 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1869 R_Mesh_TextureState(&m);
1870 GL_ColorMask(0,0,0,1);
1871 // this squares the result
1872 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1873 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1875 // second and third pass
1876 R_Mesh_ResetTextureState();
1877 // square alpha in framebuffer a few times to make it shiny
1878 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1879 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1880 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1883 memset(&m, 0, sizeof(m));
1884 m.tex[0] = R_GetTexture(glosstexture);
1885 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1886 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1887 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1888 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1889 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1890 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1894 // 2/0/0/2/2 2D combine blendsquare path
1895 memset(&m, 0, sizeof(m));
1896 m.tex[0] = R_GetTexture(normalmaptexture);
1897 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1898 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1899 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1900 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1901 m.pointer_texcoord3f[1] = rsurface_array_texcoord3f;
1902 R_Mesh_TextureState(&m);
1903 GL_ColorMask(0,0,0,1);
1904 // this squares the result
1905 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1906 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1908 // second and third pass
1909 R_Mesh_ResetTextureState();
1910 // square alpha in framebuffer a few times to make it shiny
1911 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1912 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1913 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1916 memset(&m, 0, sizeof(m));
1917 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1918 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1919 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1920 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1921 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1922 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1923 R_Mesh_TextureState(&m);
1924 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1925 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i);
1928 memset(&m, 0, sizeof(m));
1929 m.tex[0] = R_GetTexture(glosstexture);
1930 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
1931 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
1932 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1934 m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1935 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1936 m.texmatrix[1] = r_shadow_entitytolight;
1938 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1940 // this final code is shared
1941 R_Mesh_TextureState(&m);
1942 R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1945 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1947 // ARB path (any Geforce, any Radeon)
1948 qboolean doambient = ambientscale > 0;
1949 qboolean dodiffuse = diffusescale > 0;
1950 qboolean dospecular = specularscale > 0;
1951 if (!doambient && !dodiffuse && !dospecular)
1953 R_Mesh_ColorPointer(NULL);
1955 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, ambientscale * r_view.colorscale);
1957 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_view.colorscale);
1961 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, ambientscale * r_view.colorscale);
1963 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_view.colorscale);
1968 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, ambientscale * r_view.colorscale);
1970 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_view.colorscale);
1973 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
1976 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)
1983 int newnumtriangles;
1987 int newelements[3072];
1988 R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
1989 for (renders = 0;renders < 64;renders++)
1994 newnumtriangles = 0;
1996 // due to low fillrate on the cards this vertex lighting path is
1997 // designed for, we manually cull all triangles that do not
1998 // contain a lit vertex
1999 // this builds batches of triangles from multiple surfaces and
2000 // renders them at once
2001 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
2003 if (VectorLength2(rsurface_array_color4f + e[0] * 4) + VectorLength2(rsurface_array_color4f + e[1] * 4) + VectorLength2(rsurface_array_color4f + e[2] * 4) >= 0.01)
2005 if (newnumtriangles)
2007 newfirstvertex = min(newfirstvertex, e[0]);
2008 newlastvertex = max(newlastvertex, e[0]);
2012 newfirstvertex = e[0];
2013 newlastvertex = e[0];
2015 newfirstvertex = min(newfirstvertex, e[1]);
2016 newlastvertex = max(newlastvertex, e[1]);
2017 newfirstvertex = min(newfirstvertex, e[2]);
2018 newlastvertex = max(newlastvertex, e[2]);
2024 if (newnumtriangles >= 1024)
2026 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
2027 newnumtriangles = 0;
2033 if (newnumtriangles >= 1)
2035 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements);
2038 // if we couldn't find any lit triangles, exit early
2041 // now reduce the intensity for the next overbright pass
2042 // we have to clamp to 0 here incase the drivers have improper
2043 // handling of negative colors
2044 // (some old drivers even have improper handling of >1 color)
2046 for (i = 0, c = rsurface_array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
2048 if (c[0] > 1 || c[1] > 1 || c[2] > 1)
2050 c[0] = max(0, c[0] - 1);
2051 c[1] = max(0, c[1] - 1);
2052 c[2] = max(0, c[2] - 1);
2064 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
2066 // OpenGL 1.1 path (anything)
2067 model_t *model = rsurface_entity->model;
2068 float ambientcolorbase[3], diffusecolorbase[3];
2069 float ambientcolorpants[3], diffusecolorpants[3];
2070 float ambientcolorshirt[3], diffusecolorshirt[3];
2072 VectorScale(lightcolorbase, ambientscale * 2 * r_view.colorscale, ambientcolorbase);
2073 VectorScale(lightcolorbase, diffusescale * 2 * r_view.colorscale, diffusecolorbase);
2074 VectorScale(lightcolorpants, ambientscale * 2 * r_view.colorscale, ambientcolorpants);
2075 VectorScale(lightcolorpants, diffusescale * 2 * r_view.colorscale, diffusecolorpants);
2076 VectorScale(lightcolorshirt, ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
2077 VectorScale(lightcolorshirt, diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
2078 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2079 R_Mesh_ColorPointer(rsurface_array_color4f);
2080 memset(&m, 0, sizeof(m));
2081 m.tex[0] = R_GetTexture(basetexture);
2082 m.texmatrix[0] = rsurface_texture->currenttexmatrix;
2083 m.pointer_texcoord[0] = rsurface_model->surfmesh.data_texcoordtexture2f;
2084 if (r_textureunits.integer >= 2)
2087 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2088 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2089 m.pointer_texcoord3f[1] = rsurface_vertex3f;
2090 if (r_textureunits.integer >= 3)
2092 // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2093 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2094 m.texmatrix[2] = r_shadow_entitytoattenuationz;
2095 m.pointer_texcoord3f[2] = rsurface_vertex3f;
2098 R_Mesh_TextureState(&m);
2099 //R_Mesh_TexBind(0, R_GetTexture(basetexture));
2100 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorbase, ambientcolorbase);
2103 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2104 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorpants, ambientcolorpants);
2108 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2109 R_Shadow_RenderLighting_Light_Vertex_Pass(model, firstvertex, numvertices, numtriangles, element3i, diffusecolorshirt, ambientcolorshirt);
2113 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i)
2115 float ambientscale, diffusescale, specularscale;
2116 // FIXME: support MATERIALFLAG_NODEPTHTEST
2117 vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2118 // calculate colors to render this texture with
2119 lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * rsurface_entity->colormod[0] * rsurface_texture->currentalpha;
2120 lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * rsurface_entity->colormod[1] * rsurface_texture->currentalpha;
2121 lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * rsurface_entity->colormod[2] * rsurface_texture->currentalpha;
2122 ambientscale = r_shadow_rtlight->ambientscale;
2123 diffusescale = r_shadow_rtlight->diffusescale;
2124 specularscale = specularscale;
2125 if (!r_shadow_usenormalmap.integer)
2127 ambientscale += 1.0f * diffusescale;
2131 if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2133 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
2134 GL_CullFace((rsurface_texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : GL_FRONT); // quake is backwards, this culls back faces
2135 if (rsurface_texture->colormapping)
2137 qboolean dopants = rsurface_texture->currentskinframe->pants != NULL && VectorLength2(rsurface_entity->colormap_pantscolor) >= (1.0f / 1048576.0f);
2138 qboolean doshirt = rsurface_texture->currentskinframe->shirt != NULL && VectorLength2(rsurface_entity->colormap_shirtcolor) >= (1.0f / 1048576.0f);
2141 lightcolorpants[0] = lightcolorbase[0] * rsurface_entity->colormap_pantscolor[0];
2142 lightcolorpants[1] = lightcolorbase[1] * rsurface_entity->colormap_pantscolor[1];
2143 lightcolorpants[2] = lightcolorbase[2] * rsurface_entity->colormap_pantscolor[2];
2146 VectorClear(lightcolorpants);
2149 lightcolorshirt[0] = lightcolorbase[0] * rsurface_entity->colormap_shirtcolor[0];
2150 lightcolorshirt[1] = lightcolorbase[1] * rsurface_entity->colormap_shirtcolor[1];
2151 lightcolorshirt[2] = lightcolorbase[2] * rsurface_entity->colormap_shirtcolor[2];
2154 VectorClear(lightcolorshirt);
2155 switch (r_shadow_rendermode)
2157 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2158 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2159 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, ambientscale, diffusescale, specularscale, dopants, doshirt);
2161 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2162 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, ambientscale, diffusescale, specularscale, dopants, doshirt);
2164 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2165 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, ambientscale, diffusescale, specularscale, dopants, doshirt);
2167 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2168 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, ambientscale, diffusescale, specularscale, dopants, doshirt);
2171 Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2177 switch (r_shadow_rendermode)
2179 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2180 GL_DepthTest(!(rsurface_texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2181 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, ambientscale, diffusescale, specularscale, false, false);
2183 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2184 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, ambientscale, diffusescale, specularscale, false, false);
2186 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2187 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, ambientscale, diffusescale, specularscale, false, false);
2189 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2190 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, ambientscale, diffusescale, specularscale, false, false);
2193 Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2199 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)
2201 matrix4x4_t tempmatrix = *matrix;
2202 Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2204 // if this light has been compiled before, free the associated data
2205 R_RTLight_Uncompile(rtlight);
2207 // clear it completely to avoid any lingering data
2208 memset(rtlight, 0, sizeof(*rtlight));
2210 // copy the properties
2211 rtlight->matrix_lighttoworld = tempmatrix;
2212 Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2213 Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2214 rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2215 VectorCopy(color, rtlight->color);
2216 rtlight->cubemapname[0] = 0;
2217 if (cubemapname && cubemapname[0])
2218 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2219 rtlight->shadow = shadow;
2220 rtlight->corona = corona;
2221 rtlight->style = style;
2222 rtlight->isstatic = isstatic;
2223 rtlight->coronasizescale = coronasizescale;
2224 rtlight->ambientscale = ambientscale;
2225 rtlight->diffusescale = diffusescale;
2226 rtlight->specularscale = specularscale;
2227 rtlight->flags = flags;
2229 // compute derived data
2230 //rtlight->cullradius = rtlight->radius;
2231 //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2232 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2233 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2234 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2235 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2236 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2237 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2240 // compiles rtlight geometry
2241 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2242 void R_RTLight_Compile(rtlight_t *rtlight)
2245 int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2246 int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2247 entity_render_t *ent = r_refdef.worldentity;
2248 model_t *model = r_refdef.worldmodel;
2249 unsigned char *data;
2251 // compile the light
2252 rtlight->compiled = true;
2253 rtlight->static_numleafs = 0;
2254 rtlight->static_numleafpvsbytes = 0;
2255 rtlight->static_leaflist = NULL;
2256 rtlight->static_leafpvs = NULL;
2257 rtlight->static_numsurfaces = 0;
2258 rtlight->static_surfacelist = NULL;
2259 rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2260 rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2261 rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2262 rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2263 rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2264 rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2266 if (model && model->GetLightInfo)
2268 // this variable must be set for the CompileShadowVolume code
2269 r_shadow_compilingrtlight = rtlight;
2270 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);
2271 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);
2272 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2273 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2274 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2275 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2276 rtlight->static_numsurfaces = numsurfaces;
2277 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2278 rtlight->static_numleafs = numleafs;
2279 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2280 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2281 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2282 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2283 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2284 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2285 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2286 if (rtlight->static_numsurfaces)
2287 memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2288 if (rtlight->static_numleafs)
2289 memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2290 if (rtlight->static_numleafpvsbytes)
2291 memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2292 if (rtlight->static_numshadowtrispvsbytes)
2293 memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2294 if (rtlight->static_numlighttrispvsbytes)
2295 memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2296 if (model->CompileShadowVolume && rtlight->shadow)
2297 model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2298 // now we're done compiling the rtlight
2299 r_shadow_compilingrtlight = NULL;
2303 // use smallest available cullradius - box radius or light radius
2304 //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2305 //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2309 if (rtlight->static_meshchain_shadow)
2312 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2315 shadowmeshtris += mesh->numtriangles;
2320 if (rtlight->static_numlighttrispvsbytes)
2321 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2322 if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2326 if (rtlight->static_numlighttrispvsbytes)
2327 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2328 if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2331 if (developer.integer >= 10)
2332 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);
2335 void R_RTLight_Uncompile(rtlight_t *rtlight)
2337 if (rtlight->compiled)
2339 if (rtlight->static_meshchain_shadow)
2340 Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2341 rtlight->static_meshchain_shadow = NULL;
2342 // these allocations are grouped
2343 if (rtlight->static_surfacelist)
2344 Mem_Free(rtlight->static_surfacelist);
2345 rtlight->static_numleafs = 0;
2346 rtlight->static_numleafpvsbytes = 0;
2347 rtlight->static_leaflist = NULL;
2348 rtlight->static_leafpvs = NULL;
2349 rtlight->static_numsurfaces = 0;
2350 rtlight->static_surfacelist = NULL;
2351 rtlight->static_numshadowtrispvsbytes = 0;
2352 rtlight->static_shadowtrispvs = NULL;
2353 rtlight->static_numlighttrispvsbytes = 0;
2354 rtlight->static_lighttrispvs = NULL;
2355 rtlight->compiled = false;
2359 void R_Shadow_UncompileWorldLights(void)
2362 for (light = r_shadow_worldlightchain;light;light = light->next)
2363 R_RTLight_Uncompile(&light->rtlight);
2366 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
2370 // reset the count of frustum planes
2371 // see r_shadow_rtlight_frustumplanes definition for how much this array
2373 r_shadow_rtlight_numfrustumplanes = 0;
2376 // generate a deformed frustum that includes the light origin, this is
2377 // used to cull shadow casting surfaces that can not possibly cast a
2378 // shadow onto the visible light-receiving surfaces, which can be a
2381 // if the light origin is onscreen the result will be 4 planes exactly
2382 // if the light origin is offscreen on only one axis the result will
2383 // be exactly 5 planes (split-side case)
2384 // if the light origin is offscreen on two axes the result will be
2385 // exactly 4 planes (stretched corner case)
2386 for (i = 0;i < 4;i++)
2388 // quickly reject standard frustum planes that put the light
2389 // origin outside the frustum
2390 if (PlaneDiff(rtlight->shadoworigin, &r_view.frustum[i]) < -0.03125)
2393 r_shadow_rtlight_frustumplanes[r_shadow_rtlight_numfrustumplanes++] = r_view.frustum[i];
2395 // if all the standard frustum planes were accepted, the light is onscreen
2396 // otherwise we need to generate some more planes below...
2397 if (r_shadow_rtlight_numfrustumplanes < 4)
2399 // at least one of the stock frustum planes failed, so we need to
2400 // create one or two custom planes to enclose the light origin
2401 for (i = 0;i < 4;i++)
2403 // create a plane using the view origin and light origin, and a
2404 // single point from the frustum corner set
2405 TriangleNormal(r_view.origin, r_view.frustumcorner[i], rtlight->shadoworigin, plane.normal);
2406 VectorNormalize(plane.normal);
2407 plane.dist = DotProduct(r_view.origin, plane.normal);
2408 // see if this plane is backwards and flip it if so
2409 for (j = 0;j < 4;j++)
2410 if (j != i && DotProduct(r_view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2414 VectorNegate(plane.normal, plane.normal);
2416 // flipped plane, test again to see if it is now valid
2417 for (j = 0;j < 4;j++)
2418 if (j != i && DotProduct(r_view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2420 // if the plane is still not valid, then it is dividing the
2421 // frustum and has to be rejected
2425 // we have created a valid plane, compute extra info
2426 PlaneClassify(&plane);
2428 r_shadow_rtlight_frustumplanes[r_shadow_rtlight_numfrustumplanes++] = plane;
2430 // if we've found 5 frustum planes then we have constructed a
2431 // proper split-side case and do not need to keep searching for
2432 // planes to enclose the light origin
2433 if (r_shadow_rtlight_numfrustumplanes == 5)
2441 for (i = 0;i < r_shadow_rtlight_numfrustumplanes;i++)
2443 plane = r_shadow_rtlight_frustumplanes[i];
2444 Con_Printf("light %p plane #%i %f %f %f : %f (%f %f %f %f %f)\n", rtlight, i, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist, PlaneDiff(r_view.frustumcorner[0], &plane), PlaneDiff(r_view.frustumcorner[1], &plane), PlaneDiff(r_view.frustumcorner[2], &plane), PlaneDiff(r_view.frustumcorner[3], &plane), PlaneDiff(rtlight->shadoworigin, &plane));
2449 // now add the light-space box planes if the light box is rotated, as any
2450 // caster outside the oriented light box is irrelevant (even if it passed
2451 // the worldspace light box, which is axial)
2452 if (rtlight->matrix_lighttoworld.m[0][0] != 1 || rtlight->matrix_lighttoworld.m[1][1] != 1 || rtlight->matrix_lighttoworld.m[2][2] != 1)
2454 for (i = 0;i < 6;i++)
2458 v[i >> 1] = (i & 1) ? -1 : 1;
2459 Matrix4x4_Transform(&rtlight->matrix_lighttoworld, v, plane.normal);
2460 VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
2461 plane.dist = VectorNormalizeLength(plane.normal);
2462 plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
2463 r_shadow_rtlight_frustumplanes[r_shadow_rtlight_numfrustumplanes++] = plane;
2469 // add the world-space reduced box planes
2470 for (i = 0;i < 6;i++)
2472 VectorClear(plane.normal);
2473 plane.normal[i >> 1] = (i & 1) ? -1 : 1;
2474 plane.dist = (i & 1) ? -r_shadow_rtlight_cullmaxs[i >> 1] : r_shadow_rtlight_cullmins[i >> 1];
2475 r_shadow_rtlight_frustumplanes[r_shadow_rtlight_numfrustumplanes++] = plane;
2484 // reduce all plane distances to tightly fit the rtlight cull box, which
2486 VectorSet(points[0], r_shadow_rtlight_cullmins[0], r_shadow_rtlight_cullmins[1], r_shadow_rtlight_cullmins[2]);
2487 VectorSet(points[1], r_shadow_rtlight_cullmaxs[0], r_shadow_rtlight_cullmins[1], r_shadow_rtlight_cullmins[2]);
2488 VectorSet(points[2], r_shadow_rtlight_cullmins[0], r_shadow_rtlight_cullmaxs[1], r_shadow_rtlight_cullmins[2]);
2489 VectorSet(points[3], r_shadow_rtlight_cullmaxs[0], r_shadow_rtlight_cullmaxs[1], r_shadow_rtlight_cullmins[2]);
2490 VectorSet(points[4], r_shadow_rtlight_cullmins[0], r_shadow_rtlight_cullmins[1], r_shadow_rtlight_cullmaxs[2]);
2491 VectorSet(points[5], r_shadow_rtlight_cullmaxs[0], r_shadow_rtlight_cullmins[1], r_shadow_rtlight_cullmaxs[2]);
2492 VectorSet(points[6], r_shadow_rtlight_cullmins[0], r_shadow_rtlight_cullmaxs[1], r_shadow_rtlight_cullmaxs[2]);
2493 VectorSet(points[7], r_shadow_rtlight_cullmaxs[0], r_shadow_rtlight_cullmaxs[1], r_shadow_rtlight_cullmaxs[2]);
2494 oldnum = r_shadow_rtlight_numfrustumplanes;
2495 r_shadow_rtlight_numfrustumplanes = 0;
2496 for (j = 0;j < oldnum;j++)
2498 // find the nearest point on the box to this plane
2499 bestdist = DotProduct(r_shadow_rtlight_frustumplanes[j].normal, points[0]);
2500 for (i = 1;i < 8;i++)
2502 dist = DotProduct(r_shadow_rtlight_frustumplanes[j].normal, points[i]);
2503 if (bestdist > dist)
2506 Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, r_shadow_rtlight_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, r_shadow_rtlight_frustumplanes[j].normal[0], r_shadow_rtlight_frustumplanes[j].normal[1], r_shadow_rtlight_frustumplanes[j].normal[2], r_shadow_rtlight_frustumplanes[j].dist, bestdist);
2507 // if the nearest point is near or behind the plane, we want this
2508 // plane, otherwise the plane is useless as it won't cull anything
2509 if (r_shadow_rtlight_frustumplanes[j].dist < bestdist + 0.03125)
2511 PlaneClassify(&r_shadow_rtlight_frustumplanes[j]);
2512 r_shadow_rtlight_frustumplanes[r_shadow_rtlight_numfrustumplanes++] = r_shadow_rtlight_frustumplanes[j];
2519 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2521 RSurf_ActiveWorldEntity();
2522 if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2526 for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2528 r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2529 R_Mesh_VertexPointer(mesh->vertex3f);
2530 GL_LockArrays(0, mesh->numverts);
2531 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2533 // decrement stencil if backface is behind depthbuffer
2534 GL_CullFace(GL_BACK); // quake is backwards, this culls front faces
2535 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2536 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2537 // increment stencil if frontface is behind depthbuffer
2538 GL_CullFace(GL_FRONT); // quake is backwards, this culls back faces
2539 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2541 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2542 GL_LockArrays(0, 0);
2546 else if (numsurfaces && r_refdef.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2549 int surfacelistindex;
2550 msurface_t *surface;
2551 R_Shadow_PrepareShadowMark(r_refdef.worldmodel->brush.shadowmesh->numtriangles);
2552 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2554 surface = r_refdef.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2555 for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2556 if (CHECKPVSBIT(trispvs, t))
2557 shadowmarklist[numshadowmark++] = t;
2559 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);
2561 else if (numsurfaces)
2562 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);
2565 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2567 vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2568 vec_t relativeshadowradius;
2569 RSurf_ActiveModelEntity(ent, false, false);
2570 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
2571 relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
2572 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2573 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2574 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2575 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2576 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2577 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2578 ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2581 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2583 // set up properties for rendering light onto this entity
2584 RSurf_ActiveModelEntity(ent, true, true);
2585 Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
2586 Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2587 Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2588 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2589 if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2590 R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
2593 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2595 if (!r_refdef.worldmodel->DrawLight)
2598 // set up properties for rendering light onto this entity
2599 RSurf_ActiveWorldEntity();
2600 r_shadow_entitytolight = r_shadow_rtlight->matrix_worldtolight;
2601 Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2602 Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2603 VectorCopy(r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2604 if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2605 R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
2607 r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
2610 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2612 model_t *model = ent->model;
2613 if (!model->DrawLight)
2616 R_Shadow_SetupEntityLight(ent);
2618 model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2621 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2625 int numleafs, numsurfaces;
2626 int *leaflist, *surfacelist;
2627 unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2628 int numlightentities;
2629 int numshadowentities;
2630 entity_render_t *lightentities[MAX_EDICTS];
2631 entity_render_t *shadowentities[MAX_EDICTS];
2633 // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2634 // skip lights that are basically invisible (color 0 0 0)
2635 if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2638 // loading is done before visibility checks because loading should happen
2639 // all at once at the start of a level, not when it stalls gameplay.
2640 // (especially important to benchmarks)
2642 if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2643 R_RTLight_Compile(rtlight);
2645 rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2647 // look up the light style value at this time
2648 f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2649 VectorScale(rtlight->color, f, rtlight->currentcolor);
2651 if (rtlight->selected)
2653 f = 2 + sin(realtime * M_PI * 4.0);
2654 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2658 // if lightstyle is currently off, don't draw the light
2659 if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2662 // if the light box is offscreen, skip it
2663 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2666 VectorCopy(rtlight->cullmins, r_shadow_rtlight_cullmins);
2667 VectorCopy(rtlight->cullmaxs, r_shadow_rtlight_cullmaxs);
2669 if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2671 // compiled light, world available and can receive realtime lighting
2672 // retrieve leaf information
2673 numleafs = rtlight->static_numleafs;
2674 leaflist = rtlight->static_leaflist;
2675 leafpvs = rtlight->static_leafpvs;
2676 numsurfaces = rtlight->static_numsurfaces;
2677 surfacelist = rtlight->static_surfacelist;
2678 shadowtrispvs = rtlight->static_shadowtrispvs;
2679 lighttrispvs = rtlight->static_lighttrispvs;
2681 else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2683 // dynamic light, world available and can receive realtime lighting
2684 // calculate lit surfaces and leafs
2685 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);
2686 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);
2687 leaflist = r_shadow_buffer_leaflist;
2688 leafpvs = r_shadow_buffer_leafpvs;
2689 surfacelist = r_shadow_buffer_surfacelist;
2690 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2691 lighttrispvs = r_shadow_buffer_lighttrispvs;
2692 // if the reduced leaf bounds are offscreen, skip it
2693 if (R_CullBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2704 shadowtrispvs = NULL;
2705 lighttrispvs = NULL;
2707 // check if light is illuminating any visible leafs
2710 for (i = 0;i < numleafs;i++)
2711 if (r_viewcache.world_leafvisible[leaflist[i]])
2716 // set up a scissor rectangle for this light
2717 if (R_Shadow_ScissorForBBox(r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2720 R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
2722 // make a list of lit entities and shadow casting entities
2723 numlightentities = 0;
2724 numshadowentities = 0;
2725 // add dynamic entities that are lit by the light
2726 if (r_drawentities.integer)
2728 for (i = 0;i < r_refdef.numentities;i++)
2731 entity_render_t *ent = r_refdef.entities[i];
2733 if (!BoxesOverlap(ent->mins, ent->maxs, r_shadow_rtlight_cullmins, r_shadow_rtlight_cullmaxs))
2735 // skip the object entirely if it is not within the valid
2736 // shadow-casting region (which includes the lit region)
2737 if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, r_shadow_rtlight_numfrustumplanes, r_shadow_rtlight_frustumplanes))
2739 if (!(model = ent->model))
2741 if (r_viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2743 // this entity wants to receive light, is visible, and is
2744 // inside the light box
2745 // TODO: check if the surfaces in the model can receive light
2746 // so now check if it's in a leaf seen by the light
2747 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2749 lightentities[numlightentities++] = ent;
2750 // since it is lit, it probably also casts a shadow...
2751 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2752 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2753 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2754 shadowentities[numshadowentities++] = ent;
2756 else if (ent->flags & RENDER_SHADOW)
2758 // this entity is not receiving light, but may still need to
2760 // TODO: check if the surfaces in the model can cast shadow
2761 // now check if it is in a leaf seen by the light
2762 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2764 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2765 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2766 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2767 shadowentities[numshadowentities++] = ent;
2772 // return if there's nothing at all to light
2773 if (!numlightentities && !numsurfaces)
2776 // don't let sound skip if going slow
2777 if (r_refdef.extraupdate)
2780 // make this the active rtlight for rendering purposes
2781 R_Shadow_RenderMode_ActiveLight(rtlight);
2782 // count this light in the r_speeds
2783 r_refdef.stats.lights++;
2786 if (numsurfaces + numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
2788 // draw stencil shadow volumes to mask off pixels that are in shadow
2789 // so that they won't receive lighting
2793 R_Shadow_RenderMode_StencilShadowVolumes();
2795 R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
2796 for (i = 0;i < numshadowentities;i++)
2797 R_Shadow_DrawEntityShadow(shadowentities[i]);
2800 // optionally draw visible shape of the shadow volumes
2801 // for performance analysis by level designers
2802 if (r_showshadowvolumes.integer)
2804 R_Shadow_RenderMode_VisibleShadowVolumes();
2806 R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
2807 for (i = 0;i < numshadowentities;i++)
2808 R_Shadow_DrawEntityShadow(shadowentities[i]);
2812 if (numsurfaces + numlightentities)
2814 // draw lighting in the unmasked areas
2815 R_Shadow_RenderMode_Lighting(usestencil, false);
2817 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
2818 for (i = 0;i < numlightentities;i++)
2819 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2821 // optionally draw the illuminated areas
2822 // for performance analysis by level designers
2823 if (r_showlighting.integer)
2825 R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
2827 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
2828 for (i = 0;i < numlightentities;i++)
2829 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2834 void R_Shadow_DrawLightSprites(void);
2835 void R_ShadowVolumeLighting(qboolean visible)
2840 if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2841 R_Shadow_EditLights_Reload_f();
2843 if (r_editlights.integer)
2844 R_Shadow_DrawLightSprites();
2846 R_Shadow_RenderMode_Begin();
2848 flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2849 if (r_shadow_debuglight.integer >= 0)
2851 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2852 if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2853 R_DrawRTLight(&light->rtlight, visible);
2856 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2857 if (light->flags & flag)
2858 R_DrawRTLight(&light->rtlight, visible);
2859 if (r_refdef.rtdlight)
2860 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
2861 R_DrawRTLight(&r_refdef.lights[lnum], visible);
2863 R_Shadow_RenderMode_End();
2866 extern void R_SetupView(const matrix4x4_t *matrix);
2867 extern cvar_t r_shadows_throwdistance;
2868 void R_DrawModelShadows(void)
2871 float relativethrowdistance;
2872 entity_render_t *ent;
2873 vec3_t relativelightorigin;
2874 vec3_t relativelightdirection;
2875 vec3_t relativeshadowmins, relativeshadowmaxs;
2878 if (!r_drawentities.integer || !gl_stencil)
2882 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
2884 r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
2886 if (gl_ext_separatestencil.integer)
2887 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
2888 else if (gl_ext_stenciltwoside.integer)
2889 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
2891 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
2893 R_Shadow_RenderMode_StencilShadowVolumes();
2895 for (i = 0;i < r_refdef.numentities;i++)
2897 ent = r_refdef.entities[i];
2898 // cast shadows from anything that is not a submodel of the map
2899 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
2901 relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
2902 VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
2903 VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
2904 VectorNegate(ent->modellight_lightdir, relativelightdirection);
2905 VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
2906 RSurf_ActiveModelEntity(ent, false, false);
2907 ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2911 // not really the right mode, but this will disable any silly stencil features
2912 R_Shadow_RenderMode_VisibleLighting(true, true);
2914 // vertex coordinates for a quad that covers the screen exactly
2915 vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
2916 vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
2917 vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
2918 vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
2920 // set up ortho view for rendering this pass
2921 GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
2922 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
2923 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
2924 GL_ScissorTest(true);
2925 R_Mesh_Matrix(&identitymatrix);
2926 R_Mesh_ResetTextureState();
2927 R_Mesh_VertexPointer(vertex3f);
2928 R_Mesh_ColorPointer(NULL);
2930 // set up a 50% darkening blend on shadowed areas
2931 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2932 GL_DepthTest(false);
2933 GL_DepthMask(false);
2934 qglPolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
2935 GL_Color(0, 0, 0, 0.5);
2936 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
2937 qglDepthFunc(GL_ALWAYS);CHECKGLERROR
2938 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
2939 qglStencilMask(~0);CHECKGLERROR
2940 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
2941 qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
2943 // apply the blend to the shadowed areas
2944 R_Mesh_Draw(0, 4, 2, polygonelements);
2946 // restoring the perspective view is done by R_RenderScene
2947 //R_SetupView(&r_view.matrix);
2949 // restore other state to normal
2950 R_Shadow_RenderMode_End();
2954 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2955 typedef struct suffixinfo_s
2958 qboolean flipx, flipy, flipdiagonal;
2961 static suffixinfo_t suffix[3][6] =
2964 {"px", false, false, false},
2965 {"nx", false, false, false},
2966 {"py", false, false, false},
2967 {"ny", false, false, false},
2968 {"pz", false, false, false},
2969 {"nz", false, false, false}
2972 {"posx", false, false, false},
2973 {"negx", false, false, false},
2974 {"posy", false, false, false},
2975 {"negy", false, false, false},
2976 {"posz", false, false, false},
2977 {"negz", false, false, false}
2980 {"rt", true, false, true},
2981 {"lf", false, true, true},
2982 {"ft", true, true, false},
2983 {"bk", false, false, false},
2984 {"up", true, false, true},
2985 {"dn", true, false, true}
2989 static int componentorder[4] = {0, 1, 2, 3};
2991 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2993 int i, j, cubemapsize;
2994 unsigned char *cubemappixels, *image_rgba;
2995 rtexture_t *cubemaptexture;
2997 // must start 0 so the first loadimagepixels has no requested width/height
2999 cubemappixels = NULL;
3000 cubemaptexture = NULL;
3001 // keep trying different suffix groups (posx, px, rt) until one loads
3002 for (j = 0;j < 3 && !cubemappixels;j++)
3004 // load the 6 images in the suffix group
3005 for (i = 0;i < 6;i++)
3007 // generate an image name based on the base and and suffix
3008 dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3010 if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
3012 // an image loaded, make sure width and height are equal
3013 if (image_width == image_height)
3015 // if this is the first image to load successfully, allocate the cubemap memory
3016 if (!cubemappixels && image_width >= 1)
3018 cubemapsize = image_width;
3019 // note this clears to black, so unavailable sides are black
3020 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3022 // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3024 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);
3027 Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3029 Mem_Free(image_rgba);
3033 // if a cubemap loaded, upload it
3036 if (!r_shadow_filters_texturepool)
3037 r_shadow_filters_texturepool = R_AllocTexturePool();
3038 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
3039 Mem_Free(cubemappixels);
3043 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3044 for (j = 0;j < 3;j++)
3045 for (i = 0;i < 6;i++)
3046 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3047 Con_Print(" and was unable to find any of them.\n");
3049 return cubemaptexture;
3052 rtexture_t *R_Shadow_Cubemap(const char *basename)
3055 for (i = 0;i < numcubemaps;i++)
3056 if (!strcasecmp(cubemaps[i].basename, basename))
3057 return cubemaps[i].texture;
3058 if (i >= MAX_CUBEMAPS)
3059 return r_texture_whitecube;
3061 strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
3062 cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3063 if (!cubemaps[i].texture)
3064 cubemaps[i].texture = r_texture_whitecube;
3065 return cubemaps[i].texture;
3068 void R_Shadow_FreeCubemaps(void)
3071 R_FreeTexturePool(&r_shadow_filters_texturepool);
3074 dlight_t *R_Shadow_NewWorldLight(void)
3077 light = (dlight_t *)Mem_Alloc(r_main_mempool, sizeof(dlight_t));
3078 light->next = r_shadow_worldlightchain;
3079 r_shadow_worldlightchain = light;
3083 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)
3086 // validate parameters
3087 if (style < 0 || style >= MAX_LIGHTSTYLES)
3089 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3095 // copy to light properties
3096 VectorCopy(origin, light->origin);
3097 light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3098 light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3099 light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3100 light->color[0] = max(color[0], 0);
3101 light->color[1] = max(color[1], 0);
3102 light->color[2] = max(color[2], 0);
3103 light->radius = max(radius, 0);
3104 light->style = style;
3105 light->shadow = shadowenable;
3106 light->corona = corona;
3107 strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3108 light->coronasizescale = coronasizescale;
3109 light->ambientscale = ambientscale;
3110 light->diffusescale = diffusescale;
3111 light->specularscale = specularscale;
3112 light->flags = flags;
3114 // update renderable light data
3115 Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
3116 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);
3119 void R_Shadow_FreeWorldLight(dlight_t *light)
3121 dlight_t **lightpointer;
3122 R_RTLight_Uncompile(&light->rtlight);
3123 for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
3124 if (*lightpointer != light)
3125 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
3126 *lightpointer = light->next;
3130 void R_Shadow_ClearWorldLights(void)
3132 while (r_shadow_worldlightchain)
3133 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
3134 r_shadow_selectedlight = NULL;
3135 R_Shadow_FreeCubemaps();
3138 void R_Shadow_SelectLight(dlight_t *light)
3140 if (r_shadow_selectedlight)
3141 r_shadow_selectedlight->selected = false;
3142 r_shadow_selectedlight = light;
3143 if (r_shadow_selectedlight)
3144 r_shadow_selectedlight->selected = true;
3147 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3149 // this is never batched (there can be only one)
3150 float scale = r_editlights_cursorgrid.value * 0.5f;
3151 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);
3154 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3156 // this is never batched (due to the ent parameter changing every time)
3157 // so numsurfaces == 1 and surfacelist[0] == lightnumber
3159 const dlight_t *light = (dlight_t *)ent;
3161 if (light->selected)
3162 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
3165 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);
3168 void R_Shadow_DrawLightSprites(void)
3173 for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
3174 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
3175 R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
3178 void R_Shadow_SelectLightInView(void)
3180 float bestrating, rating, temp[3];
3181 dlight_t *best, *light;
3184 for (light = r_shadow_worldlightchain;light;light = light->next)
3186 VectorSubtract(light->origin, r_view.origin, temp);
3187 rating = (DotProduct(temp, r_view.forward) / sqrt(DotProduct(temp, temp)));
3190 rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3191 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)
3193 bestrating = rating;
3198 R_Shadow_SelectLight(best);
3201 void R_Shadow_LoadWorldLights(void)
3203 int n, a, style, shadow, flags;
3204 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3205 float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3206 if (r_refdef.worldmodel == NULL)
3208 Con_Print("No map loaded.\n");
3211 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3212 strlcat (name, ".rtlights", sizeof (name));
3213 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3223 for (;COM_Parse(t, true) && strcmp(
3224 if (COM_Parse(t, true))
3226 if (com_token[0] == '!')
3229 origin[0] = atof(com_token+1);
3232 origin[0] = atof(com_token);
3237 while (*s && *s != '\n' && *s != '\r')
3243 // check for modifier flags
3250 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);
3253 flags = LIGHTFLAG_REALTIMEMODE;
3261 coronasizescale = 0.25f;
3263 VectorClear(angles);
3266 if (a < 9 || !strcmp(cubemapname, "\"\""))
3268 // remove quotes on cubemapname
3269 if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3272 namelen = strlen(cubemapname) - 2;
3273 memmove(cubemapname, cubemapname + 1, namelen);
3274 cubemapname[namelen] = '\0';
3278 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);
3281 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3289 Con_Printf("invalid rtlights file \"%s\"\n", name);
3290 Mem_Free(lightsstring);
3294 void R_Shadow_SaveWorldLights(void)
3297 size_t bufchars, bufmaxchars;
3299 char name[MAX_QPATH];
3300 char line[MAX_INPUTLINE];
3301 if (!r_shadow_worldlightchain)
3303 if (r_refdef.worldmodel == NULL)
3305 Con_Print("No map loaded.\n");
3308 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3309 strlcat (name, ".rtlights", sizeof (name));
3310 bufchars = bufmaxchars = 0;
3312 for (light = r_shadow_worldlightchain;light;light = light->next)
3314 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3315 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);
3316 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3317 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]);
3319 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);
3320 if (bufchars + strlen(line) > bufmaxchars)
3322 bufmaxchars = bufchars + strlen(line) + 2048;
3324 buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3328 memcpy(buf, oldbuf, bufchars);
3334 memcpy(buf + bufchars, line, strlen(line));
3335 bufchars += strlen(line);
3339 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3344 void R_Shadow_LoadLightsFile(void)
3347 char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3348 float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3349 if (r_refdef.worldmodel == NULL)
3351 Con_Print("No map loaded.\n");
3354 FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3355 strlcat (name, ".lights", sizeof (name));
3356 lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3364 while (*s && *s != '\n' && *s != '\r')
3370 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);
3374 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);
3377 radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3378 radius = bound(15, radius, 4096);
3379 VectorScale(color, (2.0f / (8388608.0f)), color);
3380 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3388 Con_Printf("invalid lights file \"%s\"\n", name);
3389 Mem_Free(lightsstring);
3393 // tyrlite/hmap2 light types in the delay field
3394 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3396 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3398 int entnum, style, islight, skin, pflags, effects, type, n;
3401 float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3402 char key[256], value[MAX_INPUTLINE];
3404 if (r_refdef.worldmodel == NULL)
3406 Con_Print("No map loaded.\n");
3409 // try to load a .ent file first
3410 FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3411 strlcat (key, ".ent", sizeof (key));
3412 data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3413 // and if that is not found, fall back to the bsp file entity string
3415 data = r_refdef.worldmodel->brush.entities;
3418 for (entnum = 0;COM_ParseTokenConsole(&data) && com_token[0] == '{';entnum++)
3420 type = LIGHTTYPE_MINUSX;
3421 origin[0] = origin[1] = origin[2] = 0;
3422 originhack[0] = originhack[1] = originhack[2] = 0;
3423 angles[0] = angles[1] = angles[2] = 0;
3424 color[0] = color[1] = color[2] = 1;
3425 light[0] = light[1] = light[2] = 1;light[3] = 300;
3426 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3436 if (!COM_ParseTokenConsole(&data))
3438 if (com_token[0] == '}')
3439 break; // end of entity
3440 if (com_token[0] == '_')
3441 strlcpy(key, com_token + 1, sizeof(key));
3443 strlcpy(key, com_token, sizeof(key));
3444 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3445 key[strlen(key)-1] = 0;
3446 if (!COM_ParseTokenConsole(&data))
3448 strlcpy(value, com_token, sizeof(value));
3450 // now that we have the key pair worked out...
3451 if (!strcmp("light", key))
3453 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3457 light[0] = vec[0] * (1.0f / 256.0f);
3458 light[1] = vec[0] * (1.0f / 256.0f);
3459 light[2] = vec[0] * (1.0f / 256.0f);
3465 light[0] = vec[0] * (1.0f / 255.0f);
3466 light[1] = vec[1] * (1.0f / 255.0f);
3467 light[2] = vec[2] * (1.0f / 255.0f);
3471 else if (!strcmp("delay", key))
3473 else if (!strcmp("origin", key))
3474 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3475 else if (!strcmp("angle", key))
3476 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3477 else if (!strcmp("angles", key))
3478 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3479 else if (!strcmp("color", key))
3480 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3481 else if (!strcmp("wait", key))
3482 fadescale = atof(value);
3483 else if (!strcmp("classname", key))
3485 if (!strncmp(value, "light", 5))
3488 if (!strcmp(value, "light_fluoro"))
3493 overridecolor[0] = 1;
3494 overridecolor[1] = 1;
3495 overridecolor[2] = 1;
3497 if (!strcmp(value, "light_fluorospark"))
3502 overridecolor[0] = 1;
3503 overridecolor[1] = 1;
3504 overridecolor[2] = 1;
3506 if (!strcmp(value, "light_globe"))
3511 overridecolor[0] = 1;
3512 overridecolor[1] = 0.8;
3513 overridecolor[2] = 0.4;
3515 if (!strcmp(value, "light_flame_large_yellow"))
3520 overridecolor[0] = 1;
3521 overridecolor[1] = 0.5;
3522 overridecolor[2] = 0.1;
3524 if (!strcmp(value, "light_flame_small_yellow"))
3529 overridecolor[0] = 1;
3530 overridecolor[1] = 0.5;
3531 overridecolor[2] = 0.1;
3533 if (!strcmp(value, "light_torch_small_white"))
3538 overridecolor[0] = 1;
3539 overridecolor[1] = 0.5;
3540 overridecolor[2] = 0.1;
3542 if (!strcmp(value, "light_torch_small_walltorch"))
3547 overridecolor[0] = 1;
3548 overridecolor[1] = 0.5;
3549 overridecolor[2] = 0.1;
3553 else if (!strcmp("style", key))
3554 style = atoi(value);
3555 else if (!strcmp("skin", key))
3556 skin = (int)atof(value);
3557 else if (!strcmp("pflags", key))
3558 pflags = (int)atof(value);
3559 else if (!strcmp("effects", key))
3560 effects = (int)atof(value);
3561 else if (r_refdef.worldmodel->type == mod_brushq3)
3563 if (!strcmp("scale", key))
3564 lightscale = atof(value);
3565 if (!strcmp("fade", key))
3566 fadescale = atof(value);
3571 if (lightscale <= 0)
3575 if (color[0] == color[1] && color[0] == color[2])
3577 color[0] *= overridecolor[0];
3578 color[1] *= overridecolor[1];
3579 color[2] *= overridecolor[2];
3581 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3582 color[0] = color[0] * light[0];
3583 color[1] = color[1] * light[1];
3584 color[2] = color[2] * light[2];
3587 case LIGHTTYPE_MINUSX:
3589 case LIGHTTYPE_RECIPX:
3591 VectorScale(color, (1.0f / 16.0f), color);
3593 case LIGHTTYPE_RECIPXX:
3595 VectorScale(color, (1.0f / 16.0f), color);
3598 case LIGHTTYPE_NONE:
3602 case LIGHTTYPE_MINUSXX:
3605 VectorAdd(origin, originhack, origin);
3607 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);
3610 Mem_Free(entfiledata);
3614 void R_Shadow_SetCursorLocationForView(void)
3617 vec3_t dest, endpos;
3619 VectorMA(r_view.origin, r_editlights_cursordistance.value, r_view.forward, dest);
3620 trace = CL_Move(r_view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
3621 if (trace.fraction < 1)
3623 dist = trace.fraction * r_editlights_cursordistance.value;
3624 push = r_editlights_cursorpushback.value;
3628 VectorMA(trace.endpos, push, r_view.forward, endpos);
3629 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
3633 VectorClear( endpos );
3635 r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3636 r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3637 r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3640 void R_Shadow_UpdateWorldLightSelection(void)
3642 if (r_editlights.integer)
3644 R_Shadow_SetCursorLocationForView();
3645 R_Shadow_SelectLightInView();
3648 R_Shadow_SelectLight(NULL);
3651 void R_Shadow_EditLights_Clear_f(void)
3653 R_Shadow_ClearWorldLights();
3656 void R_Shadow_EditLights_Reload_f(void)
3658 if (!r_refdef.worldmodel)
3660 strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
3661 R_Shadow_ClearWorldLights();
3662 R_Shadow_LoadWorldLights();
3663 if (r_shadow_worldlightchain == NULL)
3665 R_Shadow_LoadLightsFile();
3666 if (r_shadow_worldlightchain == NULL)
3667 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3671 void R_Shadow_EditLights_Save_f(void)
3673 if (!r_refdef.worldmodel)
3675 R_Shadow_SaveWorldLights();
3678 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3680 R_Shadow_ClearWorldLights();
3681 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3684 void R_Shadow_EditLights_ImportLightsFile_f(void)
3686 R_Shadow_ClearWorldLights();
3687 R_Shadow_LoadLightsFile();
3690 void R_Shadow_EditLights_Spawn_f(void)
3693 if (!r_editlights.integer)
3695 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3698 if (Cmd_Argc() != 1)
3700 Con_Print("r_editlights_spawn does not take parameters\n");
3703 color[0] = color[1] = color[2] = 1;
3704 R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3707 void R_Shadow_EditLights_Edit_f(void)
3709 vec3_t origin, angles, color;
3710 vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3711 int style, shadows, flags, normalmode, realtimemode;
3712 char cubemapname[MAX_INPUTLINE];
3713 if (!r_editlights.integer)
3715 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
3718 if (!r_shadow_selectedlight)
3720 Con_Print("No selected light.\n");
3723 VectorCopy(r_shadow_selectedlight->origin, origin);
3724 VectorCopy(r_shadow_selectedlight->angles, angles);
3725 VectorCopy(r_shadow_selectedlight->color, color);
3726 radius = r_shadow_selectedlight->radius;
3727 style = r_shadow_selectedlight->style;
3728 if (r_shadow_selectedlight->cubemapname)
3729 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
3732 shadows = r_shadow_selectedlight->shadow;
3733 corona = r_shadow_selectedlight->corona;
3734 coronasizescale = r_shadow_selectedlight->coronasizescale;
3735 ambientscale = r_shadow_selectedlight->ambientscale;
3736 diffusescale = r_shadow_selectedlight->diffusescale;
3737 specularscale = r_shadow_selectedlight->specularscale;
3738 flags = r_shadow_selectedlight->flags;
3739 normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3740 realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3741 if (!strcmp(Cmd_Argv(1), "origin"))
3743 if (Cmd_Argc() != 5)
3745 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3748 origin[0] = atof(Cmd_Argv(2));
3749 origin[1] = atof(Cmd_Argv(3));
3750 origin[2] = atof(Cmd_Argv(4));
3752 else if (!strcmp(Cmd_Argv(1), "originx"))
3754 if (Cmd_Argc() != 3)
3756 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3759 origin[0] = atof(Cmd_Argv(2));
3761 else if (!strcmp(Cmd_Argv(1), "originy"))
3763 if (Cmd_Argc() != 3)
3765 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3768 origin[1] = atof(Cmd_Argv(2));
3770 else if (!strcmp(Cmd_Argv(1), "originz"))
3772 if (Cmd_Argc() != 3)
3774 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3777 origin[2] = atof(Cmd_Argv(2));
3779 else if (!strcmp(Cmd_Argv(1), "move"))
3781 if (Cmd_Argc() != 5)
3783 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3786 origin[0] += atof(Cmd_Argv(2));
3787 origin[1] += atof(Cmd_Argv(3));
3788 origin[2] += atof(Cmd_Argv(4));
3790 else if (!strcmp(Cmd_Argv(1), "movex"))
3792 if (Cmd_Argc() != 3)
3794 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3797 origin[0] += atof(Cmd_Argv(2));
3799 else if (!strcmp(Cmd_Argv(1), "movey"))
3801 if (Cmd_Argc() != 3)
3803 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3806 origin[1] += atof(Cmd_Argv(2));
3808 else if (!strcmp(Cmd_Argv(1), "movez"))
3810 if (Cmd_Argc() != 3)
3812 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3815 origin[2] += atof(Cmd_Argv(2));
3817 else if (!strcmp(Cmd_Argv(1), "angles"))
3819 if (Cmd_Argc() != 5)
3821 Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3824 angles[0] = atof(Cmd_Argv(2));
3825 angles[1] = atof(Cmd_Argv(3));
3826 angles[2] = atof(Cmd_Argv(4));
3828 else if (!strcmp(Cmd_Argv(1), "anglesx"))
3830 if (Cmd_Argc() != 3)
3832 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3835 angles[0] = atof(Cmd_Argv(2));
3837 else if (!strcmp(Cmd_Argv(1), "anglesy"))
3839 if (Cmd_Argc() != 3)
3841 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3844 angles[1] = atof(Cmd_Argv(2));
3846 else if (!strcmp(Cmd_Argv(1), "anglesz"))
3848 if (Cmd_Argc() != 3)
3850 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3853 angles[2] = atof(Cmd_Argv(2));
3855 else if (!strcmp(Cmd_Argv(1), "color"))
3857 if (Cmd_Argc() != 5)
3859 Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3862 color[0] = atof(Cmd_Argv(2));
3863 color[1] = atof(Cmd_Argv(3));
3864 color[2] = atof(Cmd_Argv(4));
3866 else if (!strcmp(Cmd_Argv(1), "radius"))
3868 if (Cmd_Argc() != 3)
3870 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3873 radius = atof(Cmd_Argv(2));
3875 else if (!strcmp(Cmd_Argv(1), "colorscale"))
3877 if (Cmd_Argc() == 3)
3879 double scale = atof(Cmd_Argv(2));
3886 if (Cmd_Argc() != 5)
3888 Con_Printf("usage: r_editlights_edit %s red green blue (OR grey instead of red green blue)\n", Cmd_Argv(1));
3891 color[0] *= atof(Cmd_Argv(2));
3892 color[1] *= atof(Cmd_Argv(3));
3893 color[2] *= atof(Cmd_Argv(4));
3896 else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
3898 if (Cmd_Argc() != 3)
3900 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3903 radius *= atof(Cmd_Argv(2));
3905 else if (!strcmp(Cmd_Argv(1), "style"))
3907 if (Cmd_Argc() != 3)
3909 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3912 style = atoi(Cmd_Argv(2));
3914 else if (!strcmp(Cmd_Argv(1), "cubemap"))
3918 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3921 if (Cmd_Argc() == 3)
3922 strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
3926 else if (!strcmp(Cmd_Argv(1), "shadows"))
3928 if (Cmd_Argc() != 3)
3930 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3933 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3935 else if (!strcmp(Cmd_Argv(1), "corona"))
3937 if (Cmd_Argc() != 3)
3939 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3942 corona = atof(Cmd_Argv(2));
3944 else if (!strcmp(Cmd_Argv(1), "coronasize"))
3946 if (Cmd_Argc() != 3)
3948 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3951 coronasizescale = atof(Cmd_Argv(2));
3953 else if (!strcmp(Cmd_Argv(1), "ambient"))
3955 if (Cmd_Argc() != 3)
3957 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3960 ambientscale = atof(Cmd_Argv(2));
3962 else if (!strcmp(Cmd_Argv(1), "diffuse"))
3964 if (Cmd_Argc() != 3)
3966 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3969 diffusescale = atof(Cmd_Argv(2));
3971 else if (!strcmp(Cmd_Argv(1), "specular"))
3973 if (Cmd_Argc() != 3)
3975 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3978 specularscale = atof(Cmd_Argv(2));
3980 else if (!strcmp(Cmd_Argv(1), "normalmode"))
3982 if (Cmd_Argc() != 3)
3984 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3987 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3989 else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3991 if (Cmd_Argc() != 3)
3993 Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3996 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4000 Con_Print("usage: r_editlights_edit [property] [value]\n");
4001 Con_Print("Selected light's properties:\n");
4002 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4003 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4004 Con_Printf("Color : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4005 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
4006 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
4007 Con_Printf("Style : %i\n", r_shadow_selectedlight->style);
4008 Con_Printf("Shadows : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4009 Con_Printf("Cubemap : %s\n", r_shadow_selectedlight->cubemapname);
4010 Con_Printf("CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);
4011 Con_Printf("Ambient : %f\n", r_shadow_selectedlight->ambientscale);
4012 Con_Printf("Diffuse : %f\n", r_shadow_selectedlight->diffusescale);
4013 Con_Printf("Specular : %f\n", r_shadow_selectedlight->specularscale);
4014 Con_Printf("NormalMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4015 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4018 flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4019 R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4022 void R_Shadow_EditLights_EditAll_f(void)
4026 if (!r_editlights.integer)
4028 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4032 for (light = r_shadow_worldlightchain;light;light = light->next)
4034 R_Shadow_SelectLight(light);
4035 R_Shadow_EditLights_Edit_f();
4039 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4041 int lightnumber, lightcount;
4045 if (!r_editlights.integer)
4051 for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
4052 if (light == r_shadow_selectedlight)
4053 lightnumber = lightcount;
4054 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;
4055 if (r_shadow_selectedlight == NULL)
4057 sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4058 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;
4059 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;
4060 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;
4061 sprintf(temp, "Radius : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4062 sprintf(temp, "Corona : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4063 sprintf(temp, "Style : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4064 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;
4065 sprintf(temp, "Cubemap : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4066 sprintf(temp, "CoronaSize : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4067 sprintf(temp, "Ambient : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4068 sprintf(temp, "Diffuse : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4069 sprintf(temp, "Specular : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4070 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;
4071 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;
4074 void R_Shadow_EditLights_ToggleShadow_f(void)
4076 if (!r_editlights.integer)
4078 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
4081 if (!r_shadow_selectedlight)
4083 Con_Print("No selected light.\n");
4086 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);
4089 void R_Shadow_EditLights_ToggleCorona_f(void)
4091 if (!r_editlights.integer)
4093 Con_Print("Cannot spawn light when not in editing mode. Set r_editlights to 1.\n");
4096 if (!r_shadow_selectedlight)
4098 Con_Print("No selected light.\n");
4101 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);
4104 void R_Shadow_EditLights_Remove_f(void)
4106 if (!r_editlights.integer)
4108 Con_Print("Cannot remove light when not in editing mode. Set r_editlights to 1.\n");
4111 if (!r_shadow_selectedlight)
4113 Con_Print("No selected light.\n");
4116 R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4117 r_shadow_selectedlight = NULL;
4120 void R_Shadow_EditLights_Help_f(void)
4123 "Documentation on r_editlights system:\n"
4125 "r_editlights : enable/disable editing mode\n"
4126 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4127 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4128 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4129 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4130 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4132 "r_editlights_help : this help\n"
4133 "r_editlights_clear : remove all lights\n"
4134 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4135 "r_editlights_save : save to .rtlights file\n"
4136 "r_editlights_spawn : create a light with default settings\n"
4137 "r_editlights_edit command : edit selected light - more documentation below\n"
4138 "r_editlights_remove : remove selected light\n"
4139 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4140 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4141 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4143 "origin x y z : set light location\n"
4144 "originx x: set x component of light location\n"
4145 "originy y: set y component of light location\n"
4146 "originz z: set z component of light location\n"
4147 "move x y z : adjust light location\n"
4148 "movex x: adjust x component of light location\n"
4149 "movey y: adjust y component of light location\n"
4150 "movez z: adjust z component of light location\n"
4151 "angles x y z : set light angles\n"
4152 "anglesx x: set x component of light angles\n"
4153 "anglesy y: set y component of light angles\n"
4154 "anglesz z: set z component of light angles\n"
4155 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4156 "radius radius : set radius (size) of light\n"
4157 "colorscale grey : multiply color of light (1 does nothing)\n"
4158 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
4159 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
4160 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
4161 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4162 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4163 "shadows 1/0 : turn on/off shadows\n"
4164 "corona n : set corona intensity\n"
4165 "coronasize n : set corona size (0-1)\n"
4166 "ambient n : set ambient intensity (0-1)\n"
4167 "diffuse n : set diffuse intensity (0-1)\n"
4168 "specular n : set specular intensity (0-1)\n"
4169 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4170 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4171 "<nothing> : print light properties to console\n"
4175 void R_Shadow_EditLights_CopyInfo_f(void)
4177 if (!r_editlights.integer)
4179 Con_Print("Cannot copy light info when not in editing mode. Set r_editlights to 1.\n");
4182 if (!r_shadow_selectedlight)
4184 Con_Print("No selected light.\n");
4187 VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4188 VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4189 r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4190 r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4191 if (r_shadow_selectedlight->cubemapname)
4192 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
4194 r_shadow_bufferlight.cubemapname[0] = 0;
4195 r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4196 r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4197 r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4198 r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4199 r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4200 r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4201 r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4204 void R_Shadow_EditLights_PasteInfo_f(void)
4206 if (!r_editlights.integer)
4208 Con_Print("Cannot paste light info when not in editing mode. Set r_editlights to 1.\n");
4211 if (!r_shadow_selectedlight)
4213 Con_Print("No selected light.\n");
4216 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);
4219 void R_Shadow_EditLights_Init(void)
4221 Cvar_RegisterVariable(&r_editlights);
4222 Cvar_RegisterVariable(&r_editlights_cursordistance);
4223 Cvar_RegisterVariable(&r_editlights_cursorpushback);
4224 Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4225 Cvar_RegisterVariable(&r_editlights_cursorgrid);
4226 Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4227 Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
4228 Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
4229 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)");
4230 Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
4231 Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
4232 Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
4233 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)");
4234 Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
4235 Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
4236 Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
4237 Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
4238 Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
4239 Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
4240 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)");