]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
redesigned box clipping code in R_Shadow_ScissorForBBox, now clips edges
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
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)
9
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.
15
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).
22
23 Patent warning:
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).
29
30
31
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).
38
39
40
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
46 in some ideal cases).
47
48
49
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.
60
61
62
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.
69
70
71
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.
80
81
82
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
89 texturing).
90
91
92
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).
96
97
98
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.
103
104
105
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
114 this however).
115
116
117
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
127 other areas).
128
129
130
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.
135 */
136
137 #include "quakedef.h"
138 #include "r_shadow.h"
139 #include "cl_collision.h"
140 #include "portals.h"
141 #include "image.h"
142
143 extern void R_Shadow_EditLights_Init(void);
144
145 typedef enum r_shadow_rendermode_e
146 {
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,
156 }
157 r_shadow_rendermode_t;
158
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;
162
163 int maxshadowtriangles;
164 int *shadowelements;
165
166 int maxshadowvertices;
167 float *shadowvertex3f;
168
169 int maxshadowmark;
170 int numshadowmark;
171 int *shadowmark;
172 int *shadowmarklist;
173 int shadowmarkcount;
174
175 int maxvertexupdate;
176 int *vertexupdate;
177 int *vertexremap;
178 int vertexupdatenum;
179
180 int r_shadow_buffer_numleafpvsbytes;
181 unsigned char *r_shadow_buffer_leafpvs;
182 int *r_shadow_buffer_leaflist;
183
184 int r_shadow_buffer_numsurfacepvsbytes;
185 unsigned char *r_shadow_buffer_surfacepvs;
186 int *r_shadow_buffer_surfacelist;
187
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;
192
193 rtexturepool_t *r_shadow_texturepool;
194 rtexture_t *r_shadow_attenuationgradienttexture;
195 rtexture_t *r_shadow_attenuation2dtexture;
196 rtexture_t *r_shadow_attenuation3dtexture;
197 rtexture_t *r_shadow_lightcorona;
198
199 // lights are reloaded when this changes
200 char r_shadow_mapname[MAX_QPATH];
201
202 // used only for light filters (cubemaps)
203 rtexturepool_t *r_shadow_filters_texturepool;
204
205 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"};
206 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"};
207 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
208 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
209 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)"};
210 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"};
211 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
212 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
213 cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
214 cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
215 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
216 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
217 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
218 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
219 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)"};
220 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
221 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
222 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
223 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
224 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)"};
225 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"};
226 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
227 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
228 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"};
229 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
230 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
231 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)"};
232 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
233 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
234 cvar_t r_shadow_polygonoffset = {0, "r_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)"};
235 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)"};
236 cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
237 cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
238 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
239 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
240 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
241 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
242 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
243 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
244 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
245 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
246
247 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
248 #define ATTENTABLESIZE 256
249 // 1D gradient, 2D circle and 3D sphere attenuation textures
250 #define ATTEN1DSIZE 32
251 #define ATTEN2DSIZE 64
252 #define ATTEN3DSIZE 32
253
254 static float r_shadow_attendividebias; // r_shadow_lightattenuationdividebias
255 static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
256 static float r_shadow_attentable[ATTENTABLESIZE+1];
257
258 rtlight_t *r_shadow_compilingrtlight;
259 static memexpandablearray_t r_shadow_worldlightsarray;
260 dlight_t *r_shadow_selectedlight;
261 dlight_t r_shadow_bufferlight;
262 vec3_t r_editlights_cursorlocation;
263
264 extern int con_vislines;
265
266 typedef struct cubemapinfo_s
267 {
268         char basename[64];
269         rtexture_t *texture;
270 }
271 cubemapinfo_t;
272
273 #define MAX_CUBEMAPS 256
274 static int numcubemaps;
275 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
276
277 void R_Shadow_UncompileWorldLights(void);
278 void R_Shadow_ClearWorldLights(void);
279 void R_Shadow_SaveWorldLights(void);
280 void R_Shadow_LoadWorldLights(void);
281 void R_Shadow_LoadLightsFile(void);
282 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
283 void R_Shadow_EditLights_Reload_f(void);
284 void R_Shadow_ValidateCvars(void);
285 static void R_Shadow_MakeTextures(void);
286
287 // VorteX: custom editor light sprites
288 #define EDLIGHTSPRSIZE                  8
289 cachepic_t *r_editlights_sprcursor;
290 cachepic_t *r_editlights_sprlight;
291 cachepic_t *r_editlights_sprnoshadowlight;
292 cachepic_t *r_editlights_sprcubemaplight;
293 cachepic_t *r_editlights_sprcubemapnoshadowlight;
294 cachepic_t *r_editlights_sprselection;
295
296 void r_shadow_start(void)
297 {
298         // allocate vertex processing arrays
299         numcubemaps = 0;
300         r_shadow_attenuationgradienttexture = NULL;
301         r_shadow_attenuation2dtexture = NULL;
302         r_shadow_attenuation3dtexture = NULL;
303         r_shadow_texturepool = NULL;
304         r_shadow_filters_texturepool = NULL;
305         R_Shadow_ValidateCvars();
306         R_Shadow_MakeTextures();
307         maxshadowtriangles = 0;
308         shadowelements = NULL;
309         maxshadowvertices = 0;
310         shadowvertex3f = NULL;
311         maxvertexupdate = 0;
312         vertexupdate = NULL;
313         vertexremap = NULL;
314         vertexupdatenum = 0;
315         maxshadowmark = 0;
316         numshadowmark = 0;
317         shadowmark = NULL;
318         shadowmarklist = NULL;
319         shadowmarkcount = 0;
320         r_shadow_buffer_numleafpvsbytes = 0;
321         r_shadow_buffer_leafpvs = NULL;
322         r_shadow_buffer_leaflist = NULL;
323         r_shadow_buffer_numsurfacepvsbytes = 0;
324         r_shadow_buffer_surfacepvs = NULL;
325         r_shadow_buffer_surfacelist = NULL;
326         r_shadow_buffer_numshadowtrispvsbytes = 0;
327         r_shadow_buffer_shadowtrispvs = NULL;
328         r_shadow_buffer_numlighttrispvsbytes = 0;
329         r_shadow_buffer_lighttrispvs = NULL;
330 }
331
332 void r_shadow_shutdown(void)
333 {
334         R_Shadow_UncompileWorldLights();
335         numcubemaps = 0;
336         r_shadow_attenuationgradienttexture = NULL;
337         r_shadow_attenuation2dtexture = NULL;
338         r_shadow_attenuation3dtexture = NULL;
339         R_FreeTexturePool(&r_shadow_texturepool);
340         R_FreeTexturePool(&r_shadow_filters_texturepool);
341         maxshadowtriangles = 0;
342         if (shadowelements)
343                 Mem_Free(shadowelements);
344         shadowelements = NULL;
345         if (shadowvertex3f)
346                 Mem_Free(shadowvertex3f);
347         shadowvertex3f = NULL;
348         maxvertexupdate = 0;
349         if (vertexupdate)
350                 Mem_Free(vertexupdate);
351         vertexupdate = NULL;
352         if (vertexremap)
353                 Mem_Free(vertexremap);
354         vertexremap = NULL;
355         vertexupdatenum = 0;
356         maxshadowmark = 0;
357         numshadowmark = 0;
358         if (shadowmark)
359                 Mem_Free(shadowmark);
360         shadowmark = NULL;
361         if (shadowmarklist)
362                 Mem_Free(shadowmarklist);
363         shadowmarklist = NULL;
364         shadowmarkcount = 0;
365         r_shadow_buffer_numleafpvsbytes = 0;
366         if (r_shadow_buffer_leafpvs)
367                 Mem_Free(r_shadow_buffer_leafpvs);
368         r_shadow_buffer_leafpvs = NULL;
369         if (r_shadow_buffer_leaflist)
370                 Mem_Free(r_shadow_buffer_leaflist);
371         r_shadow_buffer_leaflist = NULL;
372         r_shadow_buffer_numsurfacepvsbytes = 0;
373         if (r_shadow_buffer_surfacepvs)
374                 Mem_Free(r_shadow_buffer_surfacepvs);
375         r_shadow_buffer_surfacepvs = NULL;
376         if (r_shadow_buffer_surfacelist)
377                 Mem_Free(r_shadow_buffer_surfacelist);
378         r_shadow_buffer_surfacelist = NULL;
379         r_shadow_buffer_numshadowtrispvsbytes = 0;
380         if (r_shadow_buffer_shadowtrispvs)
381                 Mem_Free(r_shadow_buffer_shadowtrispvs);
382         r_shadow_buffer_numlighttrispvsbytes = 0;
383         if (r_shadow_buffer_lighttrispvs)
384                 Mem_Free(r_shadow_buffer_lighttrispvs);
385 }
386
387 void r_shadow_newmap(void)
388 {
389 }
390
391 void R_Shadow_Help_f(void)
392 {
393         Con_Printf(
394 "Documentation on r_shadow system:\n"
395 "Settings:\n"
396 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
397 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
398 "r_shadow_debuglight : render only this light number (-1 = all)\n"
399 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
400 "r_shadow_gloss2intensity : brightness of forced gloss\n"
401 "r_shadow_glossintensity : brightness of textured gloss\n"
402 "r_shadow_lightattenuationlinearscale : used to generate attenuation texture\n"
403 "r_shadow_lightattenuationdividebias : used to generate attenuation texture\n"
404 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
405 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
406 "r_shadow_portallight : use portal visibility for static light precomputation\n"
407 "r_shadow_projectdistance : shadow volume projection distance\n"
408 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
409 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
410 "r_shadow_realtime_world : use high quality world lighting mode\n"
411 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
412 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
413 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
414 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
415 "r_shadow_scissor : use scissor optimization\n"
416 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
417 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
418 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
419 "r_showlighting : useful for performance testing; bright = slow!\n"
420 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
421 "Commands:\n"
422 "r_shadow_help : this help\n"
423         );
424 }
425
426 void R_Shadow_Init(void)
427 {
428         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
429         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
430         Cvar_RegisterVariable(&r_shadow_usenormalmap);
431         Cvar_RegisterVariable(&r_shadow_debuglight);
432         Cvar_RegisterVariable(&r_shadow_gloss);
433         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
434         Cvar_RegisterVariable(&r_shadow_glossintensity);
435         Cvar_RegisterVariable(&r_shadow_glossexponent);
436         Cvar_RegisterVariable(&r_shadow_lightattenuationdividebias);
437         Cvar_RegisterVariable(&r_shadow_lightattenuationlinearscale);
438         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
439         Cvar_RegisterVariable(&r_shadow_lightradiusscale);
440         Cvar_RegisterVariable(&r_shadow_portallight);
441         Cvar_RegisterVariable(&r_shadow_projectdistance);
442         Cvar_RegisterVariable(&r_shadow_frontsidecasting);
443         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
444         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
445         Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
446         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
447         Cvar_RegisterVariable(&r_shadow_realtime_world);
448         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
449         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
450         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
451         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
452         Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
453         Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
454         Cvar_RegisterVariable(&r_shadow_scissor);
455         Cvar_RegisterVariable(&r_shadow_culltriangles);
456         Cvar_RegisterVariable(&r_shadow_polygonfactor);
457         Cvar_RegisterVariable(&r_shadow_polygonoffset);
458         Cvar_RegisterVariable(&r_shadow_texture3d);
459         Cvar_RegisterVariable(&r_coronas);
460         Cvar_RegisterVariable(&gl_flashblend);
461         Cvar_RegisterVariable(&gl_ext_separatestencil);
462         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
463         if (gamemode == GAME_TENEBRAE)
464         {
465                 Cvar_SetValue("r_shadow_gloss", 2);
466                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
467         }
468         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
469         R_Shadow_EditLights_Init();
470         Mem_ExpandableArray_NewArray(&r_shadow_worldlightsarray, r_main_mempool, sizeof(dlight_t), 128);
471         maxshadowtriangles = 0;
472         shadowelements = NULL;
473         maxshadowvertices = 0;
474         shadowvertex3f = NULL;
475         maxvertexupdate = 0;
476         vertexupdate = NULL;
477         vertexremap = NULL;
478         vertexupdatenum = 0;
479         maxshadowmark = 0;
480         numshadowmark = 0;
481         shadowmark = NULL;
482         shadowmarklist = NULL;
483         shadowmarkcount = 0;
484         r_shadow_buffer_numleafpvsbytes = 0;
485         r_shadow_buffer_leafpvs = NULL;
486         r_shadow_buffer_leaflist = NULL;
487         r_shadow_buffer_numsurfacepvsbytes = 0;
488         r_shadow_buffer_surfacepvs = NULL;
489         r_shadow_buffer_surfacelist = NULL;
490         r_shadow_buffer_shadowtrispvs = NULL;
491         r_shadow_buffer_lighttrispvs = NULL;
492         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
493 }
494
495 matrix4x4_t matrix_attenuationxyz =
496 {
497         {
498                 {0.5, 0.0, 0.0, 0.5},
499                 {0.0, 0.5, 0.0, 0.5},
500                 {0.0, 0.0, 0.5, 0.5},
501                 {0.0, 0.0, 0.0, 1.0}
502         }
503 };
504
505 matrix4x4_t matrix_attenuationz =
506 {
507         {
508                 {0.0, 0.0, 0.5, 0.5},
509                 {0.0, 0.0, 0.0, 0.5},
510                 {0.0, 0.0, 0.0, 0.5},
511                 {0.0, 0.0, 0.0, 1.0}
512         }
513 };
514
515 void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
516 {
517         // make sure shadowelements is big enough for this volume
518         if (maxshadowtriangles < numtriangles)
519         {
520                 maxshadowtriangles = numtriangles;
521                 if (shadowelements)
522                         Mem_Free(shadowelements);
523                 shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
524         }
525         // make sure shadowvertex3f is big enough for this volume
526         if (maxshadowvertices < numvertices)
527         {
528                 maxshadowvertices = numvertices;
529                 if (shadowvertex3f)
530                         Mem_Free(shadowvertex3f);
531                 shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
532         }
533 }
534
535 static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
536 {
537         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
538         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
539         int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
540         int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
541         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
542         {
543                 if (r_shadow_buffer_leafpvs)
544                         Mem_Free(r_shadow_buffer_leafpvs);
545                 if (r_shadow_buffer_leaflist)
546                         Mem_Free(r_shadow_buffer_leaflist);
547                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
548                 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
549                 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
550         }
551         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
552         {
553                 if (r_shadow_buffer_surfacepvs)
554                         Mem_Free(r_shadow_buffer_surfacepvs);
555                 if (r_shadow_buffer_surfacelist)
556                         Mem_Free(r_shadow_buffer_surfacelist);
557                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
558                 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
559                 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
560         }
561         if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
562         {
563                 if (r_shadow_buffer_shadowtrispvs)
564                         Mem_Free(r_shadow_buffer_shadowtrispvs);
565                 r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
566                 r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
567         }
568         if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
569         {
570                 if (r_shadow_buffer_lighttrispvs)
571                         Mem_Free(r_shadow_buffer_lighttrispvs);
572                 r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
573                 r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
574         }
575 }
576
577 void R_Shadow_PrepareShadowMark(int numtris)
578 {
579         // make sure shadowmark is big enough for this volume
580         if (maxshadowmark < numtris)
581         {
582                 maxshadowmark = numtris;
583                 if (shadowmark)
584                         Mem_Free(shadowmark);
585                 if (shadowmarklist)
586                         Mem_Free(shadowmarklist);
587                 shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
588                 shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
589                 shadowmarkcount = 0;
590         }
591         shadowmarkcount++;
592         // if shadowmarkcount wrapped we clear the array and adjust accordingly
593         if (shadowmarkcount == 0)
594         {
595                 shadowmarkcount = 1;
596                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
597         }
598         numshadowmark = 0;
599 }
600
601 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)
602 {
603         int i, j;
604         int outtriangles = 0, outvertices = 0;
605         const int *element;
606         const float *vertex;
607         float ratio, direction[3], projectvector[3];
608
609         if (projectdirection)
610                 VectorScale(projectdirection, projectdistance, projectvector);
611         else
612                 VectorClear(projectvector);
613
614         if (maxvertexupdate < innumvertices)
615         {
616                 maxvertexupdate = innumvertices;
617                 if (vertexupdate)
618                         Mem_Free(vertexupdate);
619                 if (vertexremap)
620                         Mem_Free(vertexremap);
621                 vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
622                 vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
623                 vertexupdatenum = 0;
624         }
625         vertexupdatenum++;
626         if (vertexupdatenum == 0)
627         {
628                 vertexupdatenum = 1;
629                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
630                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
631         }
632
633         for (i = 0;i < numshadowmarktris;i++)
634                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
635
636         // create the vertices
637         if (projectdirection)
638         {
639                 for (i = 0;i < numshadowmarktris;i++)
640                 {
641                         element = inelement3i + shadowmarktris[i] * 3;
642                         for (j = 0;j < 3;j++)
643                         {
644                                 if (vertexupdate[element[j]] != vertexupdatenum)
645                                 {
646                                         vertexupdate[element[j]] = vertexupdatenum;
647                                         vertexremap[element[j]] = outvertices;
648                                         vertex = invertex3f + element[j] * 3;
649                                         // project one copy of the vertex according to projectvector
650                                         VectorCopy(vertex, outvertex3f);
651                                         VectorAdd(vertex, projectvector, (outvertex3f + 3));
652                                         outvertex3f += 6;
653                                         outvertices += 2;
654                                 }
655                         }
656                 }
657         }
658         else
659         {
660                 for (i = 0;i < numshadowmarktris;i++)
661                 {
662                         element = inelement3i + shadowmarktris[i] * 3;
663                         for (j = 0;j < 3;j++)
664                         {
665                                 if (vertexupdate[element[j]] != vertexupdatenum)
666                                 {
667                                         vertexupdate[element[j]] = vertexupdatenum;
668                                         vertexremap[element[j]] = outvertices;
669                                         vertex = invertex3f + element[j] * 3;
670                                         // project one copy of the vertex to the sphere radius of the light
671                                         // (FIXME: would projecting it to the light box be better?)
672                                         VectorSubtract(vertex, projectorigin, direction);
673                                         ratio = projectdistance / VectorLength(direction);
674                                         VectorCopy(vertex, outvertex3f);
675                                         VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
676                                         outvertex3f += 6;
677                                         outvertices += 2;
678                                 }
679                         }
680                 }
681         }
682
683         if (r_shadow_frontsidecasting.integer)
684         {
685                 for (i = 0;i < numshadowmarktris;i++)
686                 {
687                         int remappedelement[3];
688                         int markindex;
689                         const int *neighbortriangle;
690
691                         markindex = shadowmarktris[i] * 3;
692                         element = inelement3i + markindex;
693                         neighbortriangle = inneighbor3i + markindex;
694                         // output the front and back triangles
695                         outelement3i[0] = vertexremap[element[0]];
696                         outelement3i[1] = vertexremap[element[1]];
697                         outelement3i[2] = vertexremap[element[2]];
698                         outelement3i[3] = vertexremap[element[2]] + 1;
699                         outelement3i[4] = vertexremap[element[1]] + 1;
700                         outelement3i[5] = vertexremap[element[0]] + 1;
701
702                         outelement3i += 6;
703                         outtriangles += 2;
704                         // output the sides (facing outward from this triangle)
705                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
706                         {
707                                 remappedelement[0] = vertexremap[element[0]];
708                                 remappedelement[1] = vertexremap[element[1]];
709                                 outelement3i[0] = remappedelement[1];
710                                 outelement3i[1] = remappedelement[0];
711                                 outelement3i[2] = remappedelement[0] + 1;
712                                 outelement3i[3] = remappedelement[1];
713                                 outelement3i[4] = remappedelement[0] + 1;
714                                 outelement3i[5] = remappedelement[1] + 1;
715
716                                 outelement3i += 6;
717                                 outtriangles += 2;
718                         }
719                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
720                         {
721                                 remappedelement[1] = vertexremap[element[1]];
722                                 remappedelement[2] = vertexremap[element[2]];
723                                 outelement3i[0] = remappedelement[2];
724                                 outelement3i[1] = remappedelement[1];
725                                 outelement3i[2] = remappedelement[1] + 1;
726                                 outelement3i[3] = remappedelement[2];
727                                 outelement3i[4] = remappedelement[1] + 1;
728                                 outelement3i[5] = remappedelement[2] + 1;
729
730                                 outelement3i += 6;
731                                 outtriangles += 2;
732                         }
733                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
734                         {
735                                 remappedelement[0] = vertexremap[element[0]];
736                                 remappedelement[2] = vertexremap[element[2]];
737                                 outelement3i[0] = remappedelement[0];
738                                 outelement3i[1] = remappedelement[2];
739                                 outelement3i[2] = remappedelement[2] + 1;
740                                 outelement3i[3] = remappedelement[0];
741                                 outelement3i[4] = remappedelement[2] + 1;
742                                 outelement3i[5] = remappedelement[0] + 1;
743
744                                 outelement3i += 6;
745                                 outtriangles += 2;
746                         }
747                 }
748         }
749         else
750         {
751                 for (i = 0;i < numshadowmarktris;i++)
752                 {
753                         int remappedelement[3];
754                         int markindex;
755                         const int *neighbortriangle;
756
757                         markindex = shadowmarktris[i] * 3;
758                         element = inelement3i + markindex;
759                         neighbortriangle = inneighbor3i + markindex;
760                         // output the front and back triangles
761                         outelement3i[0] = vertexremap[element[2]];
762                         outelement3i[1] = vertexremap[element[1]];
763                         outelement3i[2] = vertexremap[element[0]];
764                         outelement3i[3] = vertexremap[element[0]] + 1;
765                         outelement3i[4] = vertexremap[element[1]] + 1;
766                         outelement3i[5] = vertexremap[element[2]] + 1;
767
768                         outelement3i += 6;
769                         outtriangles += 2;
770                         // output the sides (facing outward from this triangle)
771                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
772                         {
773                                 remappedelement[0] = vertexremap[element[0]];
774                                 remappedelement[1] = vertexremap[element[1]];
775                                 outelement3i[0] = remappedelement[0];
776                                 outelement3i[1] = remappedelement[1];
777                                 outelement3i[2] = remappedelement[1] + 1;
778                                 outelement3i[3] = remappedelement[0];
779                                 outelement3i[4] = remappedelement[1] + 1;
780                                 outelement3i[5] = remappedelement[0] + 1;
781
782                                 outelement3i += 6;
783                                 outtriangles += 2;
784                         }
785                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
786                         {
787                                 remappedelement[1] = vertexremap[element[1]];
788                                 remappedelement[2] = vertexremap[element[2]];
789                                 outelement3i[0] = remappedelement[1];
790                                 outelement3i[1] = remappedelement[2];
791                                 outelement3i[2] = remappedelement[2] + 1;
792                                 outelement3i[3] = remappedelement[1];
793                                 outelement3i[4] = remappedelement[2] + 1;
794                                 outelement3i[5] = remappedelement[1] + 1;
795
796                                 outelement3i += 6;
797                                 outtriangles += 2;
798                         }
799                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
800                         {
801                                 remappedelement[0] = vertexremap[element[0]];
802                                 remappedelement[2] = vertexremap[element[2]];
803                                 outelement3i[0] = remappedelement[2];
804                                 outelement3i[1] = remappedelement[0];
805                                 outelement3i[2] = remappedelement[0] + 1;
806                                 outelement3i[3] = remappedelement[2];
807                                 outelement3i[4] = remappedelement[0] + 1;
808                                 outelement3i[5] = remappedelement[2] + 1;
809
810                                 outelement3i += 6;
811                                 outtriangles += 2;
812                         }
813                 }
814         }
815         if (outnumvertices)
816                 *outnumvertices = outvertices;
817         return outtriangles;
818 }
819
820 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)
821 {
822         int tris, outverts;
823         if (projectdistance < 0.1)
824         {
825                 Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
826                 return;
827         }
828         if (!numverts || !nummarktris)
829                 return;
830         // make sure shadowelements is big enough for this volume
831         if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
832                 R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
833         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
834         r_refdef.stats.lights_dynamicshadowtriangles += tris;
835         R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
836 }
837
838 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)
839 {
840         int t, tend;
841         const int *e;
842         const float *v[3];
843         float normal[3];
844         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
845                 return;
846         tend = firsttriangle + numtris;
847         if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
848         {
849                 // surface box entirely inside light box, no box cull
850                 if (projectdirection)
851                 {
852                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
853                         {
854                                 TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
855                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
856                                         shadowmarklist[numshadowmark++] = t;
857                         }
858                 }
859                 else
860                 {
861                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
862                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
863                                         shadowmarklist[numshadowmark++] = t;
864                 }
865         }
866         else
867         {
868                 // surface box not entirely inside light box, cull each triangle
869                 if (projectdirection)
870                 {
871                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
872                         {
873                                 v[0] = invertex3f + e[0] * 3;
874                                 v[1] = invertex3f + e[1] * 3;
875                                 v[2] = invertex3f + e[2] * 3;
876                                 TriangleNormal(v[0], v[1], v[2], normal);
877                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
878                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
879                                         shadowmarklist[numshadowmark++] = t;
880                         }
881                 }
882                 else
883                 {
884                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
885                         {
886                                 v[0] = invertex3f + e[0] * 3;
887                                 v[1] = invertex3f + e[1] * 3;
888                                 v[2] = invertex3f + e[2] * 3;
889                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
890                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
891                                         shadowmarklist[numshadowmark++] = t;
892                         }
893                 }
894         }
895 }
896
897 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
898 {
899         if (r_shadow_compilingrtlight)
900         {
901                 // if we're compiling an rtlight, capture the mesh
902                 Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
903                 return;
904         }
905         r_refdef.stats.lights_shadowtriangles += numtriangles;
906         CHECKGLERROR
907         R_Mesh_VertexPointer(vertex3f, 0, 0);
908         GL_LockArrays(0, numvertices);
909         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
910         {
911                 // decrement stencil if backface is behind depthbuffer
912                 GL_CullFace(r_view.cullface_front);
913                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
914                 R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
915                 // increment stencil if frontface is behind depthbuffer
916                 GL_CullFace(r_view.cullface_back);
917                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
918         }
919         R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
920         GL_LockArrays(0, 0);
921         CHECKGLERROR
922 }
923
924 static void R_Shadow_MakeTextures_MakeCorona(void)
925 {
926         float dx, dy;
927         int x, y, a;
928         unsigned char pixels[32][32][4];
929         for (y = 0;y < 32;y++)
930         {
931                 dy = (y - 15.5f) * (1.0f / 16.0f);
932                 for (x = 0;x < 32;x++)
933                 {
934                         dx = (x - 15.5f) * (1.0f / 16.0f);
935                         a = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));
936                         a = bound(0, a, 255);
937                         pixels[y][x][0] = a;
938                         pixels[y][x][1] = a;
939                         pixels[y][x][2] = a;
940                         pixels[y][x][3] = 255;
941                 }
942         }
943         r_shadow_lightcorona = R_LoadTexture2D(r_shadow_texturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE, NULL);
944 }
945
946 static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
947 {
948         float dist = sqrt(x*x+y*y+z*z);
949         float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
950         // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
951         return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
952 }
953
954 static void R_Shadow_MakeTextures(void)
955 {
956         int x, y, z;
957         float intensity, dist;
958         unsigned int *data;
959         R_FreeTexturePool(&r_shadow_texturepool);
960         r_shadow_texturepool = R_AllocTexturePool();
961         r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
962         r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
963         data = (unsigned int *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE) * 4);
964         // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
965         for (x = 0;x <= ATTENTABLESIZE;x++)
966         {
967                 dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
968                 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
969                 r_shadow_attentable[x] = bound(0, intensity, 1);
970         }
971         // 1D gradient texture
972         for (x = 0;x < ATTEN1DSIZE;x++)
973                 data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
974         r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
975         // 2D circle texture
976         for (y = 0;y < ATTEN2DSIZE;y++)
977                 for (x = 0;x < ATTEN2DSIZE;x++)
978                         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);
979         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
980         // 3D sphere texture
981         if (r_shadow_texture3d.integer && gl_texture3d)
982         {
983                 for (z = 0;z < ATTEN3DSIZE;z++)
984                         for (y = 0;y < ATTEN3DSIZE;y++)
985                                 for (x = 0;x < ATTEN3DSIZE;x++)
986                                         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));
987                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
988         }
989         else
990                 r_shadow_attenuation3dtexture = NULL;
991         Mem_Free(data);
992
993         R_Shadow_MakeTextures_MakeCorona();
994
995         // Editor light sprites
996         r_editlights_sprcursor = Draw_CachePic("gfx/editlights/cursor", true);
997         r_editlights_sprlight = Draw_CachePic("gfx/editlights/light", true);
998         r_editlights_sprnoshadowlight = Draw_CachePic("gfx/editlights/noshadow", true);
999         r_editlights_sprcubemaplight = Draw_CachePic("gfx/editlights/cubemaplight", true);
1000         r_editlights_sprcubemapnoshadowlight = Draw_CachePic("gfx/editlights/cubemapnoshadowlight", true);
1001         r_editlights_sprselection = Draw_CachePic("gfx/editlights/selection", true);
1002 }
1003
1004 void R_Shadow_ValidateCvars(void)
1005 {
1006         if (r_shadow_texture3d.integer && !gl_texture3d)
1007                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
1008         if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
1009                 Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
1010         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
1011                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
1012 }
1013
1014 void R_Shadow_RenderMode_Begin(void)
1015 {
1016         R_Shadow_ValidateCvars();
1017
1018         if (!r_shadow_attenuation2dtexture
1019          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
1020          || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
1021          || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
1022                 R_Shadow_MakeTextures();
1023
1024         CHECKGLERROR
1025         R_Mesh_ColorPointer(NULL, 0, 0);
1026         R_Mesh_ResetTextureState();
1027         GL_BlendFunc(GL_ONE, GL_ZERO);
1028         GL_DepthRange(0, 1);
1029         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
1030         GL_DepthTest(true);
1031         GL_DepthMask(false);
1032         GL_Color(0, 0, 0, 1);
1033         GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1034
1035         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1036
1037         if (gl_ext_separatestencil.integer)
1038                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
1039         else if (gl_ext_stenciltwoside.integer)
1040                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
1041         else
1042                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
1043
1044         if (r_glsl.integer && gl_support_fragment_shader)
1045                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
1046         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1047                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
1048         else
1049                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
1050 }
1051
1052 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
1053 {
1054         rsurface.rtlight = rtlight;
1055 }
1056
1057 void R_Shadow_RenderMode_Reset(void)
1058 {
1059         CHECKGLERROR
1060         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1061         {
1062                 qglUseProgramObjectARB(0);CHECKGLERROR
1063         }
1064         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1065         {
1066                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1067         }
1068         R_Mesh_ColorPointer(NULL, 0, 0);
1069         R_Mesh_ResetTextureState();
1070         GL_DepthRange(0, 1);
1071         GL_DepthTest(true);
1072         GL_DepthMask(false);
1073         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1074         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
1075         qglDisable(GL_STENCIL_TEST);CHECKGLERROR
1076         qglStencilMask(~0);CHECKGLERROR
1077         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
1078         qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
1079         GL_CullFace(r_view.cullface_back);
1080         GL_Color(1, 1, 1, 1);
1081         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
1082         GL_BlendFunc(GL_ONE, GL_ZERO);
1083 }
1084
1085 void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil)
1086 {
1087         CHECKGLERROR
1088         R_Shadow_RenderMode_Reset();
1089         GL_ColorMask(0, 0, 0, 0);
1090         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1091         qglDepthFunc(GL_LESS);CHECKGLERROR
1092         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1093         r_shadow_rendermode = r_shadow_shadowingrendermode;
1094         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
1095         {
1096                 GL_CullFace(GL_NONE);
1097                 qglStencilOpSeparate(r_view.cullface_front, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1098                 qglStencilOpSeparate(r_view.cullface_back, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1099         }
1100         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1101         {
1102                 GL_CullFace(GL_NONE);
1103                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1104                 qglActiveStencilFaceEXT(r_view.cullface_front);CHECKGLERROR
1105                 qglStencilMask(~0);CHECKGLERROR
1106                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1107                 qglActiveStencilFaceEXT(r_view.cullface_back);CHECKGLERROR
1108                 qglStencilMask(~0);CHECKGLERROR
1109                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1110         }
1111         if (clearstencil)
1112                 GL_Clear(GL_STENCIL_BUFFER_BIT);
1113         r_refdef.stats.lights_clears++;
1114 }
1115
1116 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
1117 {
1118         CHECKGLERROR
1119         R_Shadow_RenderMode_Reset();
1120         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1121         if (!transparent)
1122         {
1123                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1124         }
1125         if (stenciltest)
1126         {
1127                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1128                 // only draw light where this geometry was already rendered AND the
1129                 // stencil is 128 (values other than this mean shadow)
1130                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1131         }
1132         r_shadow_rendermode = r_shadow_lightingrendermode;
1133         // do global setup needed for the chosen lighting mode
1134         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1135         {
1136                 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
1137                 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
1138                 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
1139                 R_Mesh_TexBindCubeMap(3, R_GetTexture(rsurface.rtlight->currentcubemap)); // light filter
1140                 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
1141                 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
1142                 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
1143                 R_Mesh_TexBind(7, R_GetTexture(r_texture_white)); // lightmap
1144                 R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap)); // deluxemap
1145                 R_Mesh_TexBind(9, R_GetTexture(r_texture_black)); // glow
1146                 //R_Mesh_TexMatrix(3, rsurface.entitytolight); // light filter matrix
1147                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1148                 GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1149                 CHECKGLERROR
1150         }
1151 }
1152
1153 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1154 {
1155         CHECKGLERROR
1156         R_Shadow_RenderMode_Reset();
1157         GL_BlendFunc(GL_ONE, GL_ONE);
1158         GL_DepthRange(0, 1);
1159         GL_DepthTest(r_showshadowvolumes.integer < 2);
1160         GL_Color(0.0, 0.0125 * r_view.colorscale, 0.1 * r_view.colorscale, 1);
1161         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1162         GL_CullFace(GL_NONE);
1163         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1164 }
1165
1166 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1167 {
1168         CHECKGLERROR
1169         R_Shadow_RenderMode_Reset();
1170         GL_BlendFunc(GL_ONE, GL_ONE);
1171         GL_DepthRange(0, 1);
1172         GL_DepthTest(r_showlighting.integer < 2);
1173         GL_Color(0.1 * r_view.colorscale, 0.0125 * r_view.colorscale, 0, 1);
1174         if (!transparent)
1175         {
1176                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1177         }
1178         if (stenciltest)
1179         {
1180                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1181                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1182         }
1183         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1184 }
1185
1186 void R_Shadow_RenderMode_End(void)
1187 {
1188         CHECKGLERROR
1189         R_Shadow_RenderMode_Reset();
1190         R_Shadow_RenderMode_ActiveLight(NULL);
1191         GL_DepthMask(true);
1192         GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1193         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1194 }
1195
1196 int bboxedges[12][2] =
1197 {
1198         // top
1199         {0, 1}, // +X
1200         {0, 2}, // +Y
1201         {1, 3}, // Y, +X
1202         {2, 3}, // X, +Y
1203         // bottom
1204         {4, 5}, // +X
1205         {4, 6}, // +Y
1206         {5, 7}, // Y, +X
1207         {6, 7}, // X, +Y
1208         // verticals
1209         {0, 4}, // +Z
1210         {1, 5}, // X, +Z
1211         {2, 6}, // Y, +Z
1212         {3, 7}, // XY, +Z
1213 };
1214
1215 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1216 {
1217         int i, ix1, iy1, ix2, iy2;
1218         float x1, y1, x2, y2;
1219         vec4_t v, v2;
1220         float vertex[20][3];
1221         int j, k;
1222         vec4_t plane4f;
1223         int numvertices;
1224         float corner[8][4];
1225         float dist[8];
1226         int sign[8];
1227         float f;
1228
1229         if (!r_shadow_scissor.integer)
1230                 return false;
1231
1232         // if view is inside the light box, just say yes it's visible
1233         if (BoxesOverlap(r_view.origin, r_view.origin, mins, maxs))
1234         {
1235                 GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
1236                 return false;
1237         }
1238
1239         x1 = y1 = x2 = y2 = 0;
1240
1241         // transform all corners that are infront of the nearclip plane
1242         VectorNegate(r_view.frustum[4].normal, plane4f);
1243         plane4f[3] = r_view.frustum[4].dist;
1244         numvertices = 0;
1245         for (i = 0;i < 8;i++)
1246         {
1247                 Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1);
1248                 dist[i] = DotProduct4(corner[i], plane4f);
1249                 sign[i] = dist[i] > 0;
1250                 if (!sign[i])
1251                 {
1252                         VectorCopy(corner[i], vertex[numvertices]);
1253                         numvertices++;
1254                 }
1255         }
1256         // if some points are behind the nearclip, add clipped edge points to make
1257         // sure that the scissor boundary is complete
1258         if (numvertices > 0 && numvertices < 8)
1259         {
1260                 // add clipped edge points
1261                 for (i = 0;i < 12;i++)
1262                 {
1263                         j = bboxedges[i][0];
1264                         k = bboxedges[i][1];
1265                         if (sign[j] != sign[k])
1266                         {
1267                                 f = dist[j] / (dist[j] - dist[k]);
1268                                 VectorLerp(corner[j], f, corner[k], vertex[numvertices]);
1269                                 numvertices++;
1270                         }
1271                 }
1272         }
1273
1274         // if we have no points to check, the light is behind the view plane
1275         if (!numvertices)
1276                 return true;
1277
1278         // if we have some points to transform, check what screen area is covered
1279         x1 = y1 = x2 = y2 = 0;
1280         v[3] = 1.0f;
1281         //Con_Printf("%i vertices to transform...\n", numvertices);
1282         for (i = 0;i < numvertices;i++)
1283         {
1284                 VectorCopy(vertex[i], v);
1285                 GL_TransformToScreen(v, v2);
1286                 //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]);
1287                 if (i)
1288                 {
1289                         if (x1 > v2[0]) x1 = v2[0];
1290                         if (x2 < v2[0]) x2 = v2[0];
1291                         if (y1 > v2[1]) y1 = v2[1];
1292                         if (y2 < v2[1]) y2 = v2[1];
1293                 }
1294                 else
1295                 {
1296                         x1 = x2 = v2[0];
1297                         y1 = y2 = v2[1];
1298                 }
1299         }
1300
1301         // now convert the scissor rectangle to integer screen coordinates
1302         ix1 = (int)(x1 - 1.0f);
1303         iy1 = (int)(y1 - 1.0f);
1304         ix2 = (int)(x2 + 1.0f);
1305         iy2 = (int)(y2 + 1.0f);
1306         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1307
1308         // clamp it to the screen
1309         if (ix1 < r_view.x) ix1 = r_view.x;
1310         if (iy1 < r_view.y) iy1 = r_view.y;
1311         if (ix2 > r_view.x + r_view.width) ix2 = r_view.x + r_view.width;
1312         if (iy2 > r_view.y + r_view.height) iy2 = r_view.y + r_view.height;
1313
1314         // if it is inside out, it's not visible
1315         if (ix2 <= ix1 || iy2 <= iy1)
1316                 return true;
1317
1318         // the light area is visible, set up the scissor rectangle
1319         GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1320         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1321         //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1322         r_refdef.stats.lights_scissored++;
1323         return false;
1324 }
1325
1326 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1327 {
1328         float *vertex3f = rsurface.vertex3f + 3 * firstvertex;
1329         float *normal3f = rsurface.normal3f + 3 * firstvertex;
1330         float *color4f = rsurface.array_color4f + 4 * firstvertex;
1331         float dist, dot, distintensity, shadeintensity, v[3], n[3];
1332         if (r_textureunits.integer >= 3)
1333         {
1334                 if (VectorLength2(diffusecolor) > 0)
1335                 {
1336                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1337                         {
1338                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1339                                 Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1340                                 if ((dot = DotProduct(n, v)) < 0)
1341                                 {
1342                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1343                                         VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
1344                                 }
1345                                 else
1346                                         VectorCopy(ambientcolor, color4f);
1347                                 if (r_refdef.fogenabled)
1348                                 {
1349                                         float f;
1350                                         f = FogPoint_Model(vertex3f);
1351                                         VectorScale(color4f, f, color4f);
1352                                 }
1353                                 color4f[3] = 1;
1354                         }
1355                 }
1356                 else
1357                 {
1358                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1359                         {
1360                                 VectorCopy(ambientcolor, color4f);
1361                                 if (r_refdef.fogenabled)
1362                                 {
1363                                         float f;
1364                                         Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1365                                         f = FogPoint_Model(vertex3f);
1366                                         VectorScale(color4f, f, color4f);
1367                                 }
1368                                 color4f[3] = 1;
1369                         }
1370                 }
1371         }
1372         else if (r_textureunits.integer >= 2)
1373         {
1374                 if (VectorLength2(diffusecolor) > 0)
1375                 {
1376                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1377                         {
1378                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1379                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1380                                 {
1381                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1382                                         if ((dot = DotProduct(n, v)) < 0)
1383                                         {
1384                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1385                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1386                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1387                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1388                                         }
1389                                         else
1390                                         {
1391                                                 color4f[0] = ambientcolor[0] * distintensity;
1392                                                 color4f[1] = ambientcolor[1] * distintensity;
1393                                                 color4f[2] = ambientcolor[2] * distintensity;
1394                                         }
1395                                         if (r_refdef.fogenabled)
1396                                         {
1397                                                 float f;
1398                                                 f = FogPoint_Model(vertex3f);
1399                                                 VectorScale(color4f, f, color4f);
1400                                         }
1401                                 }
1402                                 else
1403                                         VectorClear(color4f);
1404                                 color4f[3] = 1;
1405                         }
1406                 }
1407                 else
1408                 {
1409                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1410                         {
1411                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1412                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1413                                 {
1414                                         color4f[0] = ambientcolor[0] * distintensity;
1415                                         color4f[1] = ambientcolor[1] * distintensity;
1416                                         color4f[2] = ambientcolor[2] * distintensity;
1417                                         if (r_refdef.fogenabled)
1418                                         {
1419                                                 float f;
1420                                                 f = FogPoint_Model(vertex3f);
1421                                                 VectorScale(color4f, f, color4f);
1422                                         }
1423                                 }
1424                                 else
1425                                         VectorClear(color4f);
1426                                 color4f[3] = 1;
1427                         }
1428                 }
1429         }
1430         else
1431         {
1432                 if (VectorLength2(diffusecolor) > 0)
1433                 {
1434                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1435                         {
1436                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1437                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1438                                 {
1439                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1440                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1441                                         if ((dot = DotProduct(n, v)) < 0)
1442                                         {
1443                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1444                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1445                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1446                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1447                                         }
1448                                         else
1449                                         {
1450                                                 color4f[0] = ambientcolor[0] * distintensity;
1451                                                 color4f[1] = ambientcolor[1] * distintensity;
1452                                                 color4f[2] = ambientcolor[2] * distintensity;
1453                                         }
1454                                         if (r_refdef.fogenabled)
1455                                         {
1456                                                 float f;
1457                                                 f = FogPoint_Model(vertex3f);
1458                                                 VectorScale(color4f, f, color4f);
1459                                         }
1460                                 }
1461                                 else
1462                                         VectorClear(color4f);
1463                                 color4f[3] = 1;
1464                         }
1465                 }
1466                 else
1467                 {
1468                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1469                         {
1470                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1471                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1472                                 {
1473                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1474                                         color4f[0] = ambientcolor[0] * distintensity;
1475                                         color4f[1] = ambientcolor[1] * distintensity;
1476                                         color4f[2] = ambientcolor[2] * distintensity;
1477                                         if (r_refdef.fogenabled)
1478                                         {
1479                                                 float f;
1480                                                 f = FogPoint_Model(vertex3f);
1481                                                 VectorScale(color4f, f, color4f);
1482                                         }
1483                                 }
1484                                 else
1485                                         VectorClear(color4f);
1486                                 color4f[3] = 1;
1487                         }
1488                 }
1489         }
1490 }
1491
1492 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1493
1494 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1495 {
1496         int i;
1497         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1498         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1499         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1500         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1501         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1502         float lightdir[3];
1503         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1504         {
1505                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1506                 // the cubemap normalizes this for us
1507                 out3f[0] = DotProduct(svector3f, lightdir);
1508                 out3f[1] = DotProduct(tvector3f, lightdir);
1509                 out3f[2] = DotProduct(normal3f, lightdir);
1510         }
1511 }
1512
1513 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1514 {
1515         int i;
1516         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1517         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1518         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1519         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1520         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1521         float lightdir[3], eyedir[3], halfdir[3];
1522         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1523         {
1524                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1525                 VectorNormalize(lightdir);
1526                 VectorSubtract(rsurface.modelorg, vertex3f, eyedir);
1527                 VectorNormalize(eyedir);
1528                 VectorAdd(lightdir, eyedir, halfdir);
1529                 // the cubemap normalizes this for us
1530                 out3f[0] = DotProduct(svector3f, halfdir);
1531                 out3f[1] = DotProduct(tvector3f, halfdir);
1532                 out3f[2] = DotProduct(normal3f, halfdir);
1533         }
1534 }
1535
1536 static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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)
1537 {
1538         // used to display how many times a surface is lit for level design purposes
1539         GL_Color(0.1 * r_view.colorscale, 0.025 * r_view.colorscale, 0, 1);
1540         R_Mesh_ColorPointer(NULL, 0, 0);
1541         R_Mesh_ResetTextureState();
1542         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1543 }
1544
1545 static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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)
1546 {
1547         // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1548         R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
1549         R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
1550         R_Mesh_TexBind(0, R_GetTexture(rsurface.texture->currentskinframe->nmap));
1551         R_Mesh_TexBind(1, R_GetTexture(rsurface.texture->basetexture));
1552         R_Mesh_TexBind(2, R_GetTexture(rsurface.texture->glosstexture));
1553         R_Mesh_TexBindCubeMap(3, R_GetTexture(rsurface.rtlight->currentcubemap));
1554         R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
1555         R_Mesh_TexBind(5, R_GetTexture(rsurface.texture->currentskinframe->pants));
1556         R_Mesh_TexBind(6, R_GetTexture(rsurface.texture->currentskinframe->shirt));
1557         R_Mesh_TexBind(10, R_GetTexture(r_shadow_attenuationgradienttexture));
1558         R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
1559         R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
1560         R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
1561         R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
1562         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1563         {
1564                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1565         }
1566         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1567         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1568         {
1569                 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1570         }
1571 }
1572
1573 static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, float r, float g, float b)
1574 {
1575         // shared final code for all the dot3 layers
1576         int renders;
1577         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 0);
1578         for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1579         {
1580                 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1581                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1582         }
1583 }
1584
1585 static void R_Shadow_RenderLighting_Light_Dot3_AmbientPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1586 {
1587         rmeshstate_t m;
1588         // colorscale accounts for how much we multiply the brightness
1589         // during combine.
1590         //
1591         // mult is how many times the final pass of the lighting will be
1592         // performed to get more brightness than otherwise possible.
1593         //
1594         // Limit mult to 64 for sanity sake.
1595         GL_Color(1,1,1,1);
1596         if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1597         {
1598                 // 3 3D combine path (Geforce3, Radeon 8500)
1599                 memset(&m, 0, sizeof(m));
1600                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1601                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1602                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1603                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1604                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1605                 m.tex[1] = R_GetTexture(basetexture);
1606                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1607                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1608                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1609                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1610                 m.texcubemap[2] = R_GetTexture(rsurface.rtlight->currentcubemap);
1611                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1612                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1613                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1614                 m.texmatrix[2] = rsurface.entitytolight;
1615                 GL_BlendFunc(GL_ONE, GL_ONE);
1616         }
1617         else if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1618         {
1619                 // 2 3D combine path (Geforce3, original Radeon)
1620                 memset(&m, 0, sizeof(m));
1621                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1622                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1623                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1624                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1625                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1626                 m.tex[1] = R_GetTexture(basetexture);
1627                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1628                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1629                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1630                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1631                 GL_BlendFunc(GL_ONE, GL_ONE);
1632         }
1633         else if (r_textureunits.integer >= 4 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1634         {
1635                 // 4 2D combine path (Geforce3, Radeon 8500)
1636                 memset(&m, 0, sizeof(m));
1637                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1638                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1639                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1640                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1641                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1642                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1643                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1644                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1645                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1646                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1647                 m.tex[2] = R_GetTexture(basetexture);
1648                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1649                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1650                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1651                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1652                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1653                 {
1654                         m.texcubemap[3] = R_GetTexture(rsurface.rtlight->currentcubemap);
1655                         m.pointer_texcoord3f[3] = rsurface.vertex3f;
1656                         m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1657                         m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1658                         m.texmatrix[3] = rsurface.entitytolight;
1659                 }
1660                 GL_BlendFunc(GL_ONE, GL_ONE);
1661         }
1662         else if (r_textureunits.integer >= 3 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1663         {
1664                 // 3 2D combine path (Geforce3, original Radeon)
1665                 memset(&m, 0, sizeof(m));
1666                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1667                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1668                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1669                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1670                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1671                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1672                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1673                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1674                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1675                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1676                 m.tex[2] = R_GetTexture(basetexture);
1677                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1678                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1679                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1680                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1681                 GL_BlendFunc(GL_ONE, GL_ONE);
1682         }
1683         else
1684         {
1685                 // 2/2/2 2D combine path (any dot3 card)
1686                 memset(&m, 0, sizeof(m));
1687                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1688                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1689                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1690                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1691                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1692                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1693                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1694                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1695                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1696                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1697                 R_Mesh_TextureState(&m);
1698                 GL_ColorMask(0,0,0,1);
1699                 GL_BlendFunc(GL_ONE, GL_ZERO);
1700                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1701
1702                 // second pass
1703                 memset(&m, 0, sizeof(m));
1704                 m.tex[0] = R_GetTexture(basetexture);
1705                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1706                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1707                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1708                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1709                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1710                 {
1711                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1712                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1713                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1714                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1715                         m.texmatrix[1] = rsurface.entitytolight;
1716                 }
1717                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1718         }
1719         // this final code is shared
1720         R_Mesh_TextureState(&m);
1721         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1722 }
1723
1724 static void R_Shadow_RenderLighting_Light_Dot3_DiffusePass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1725 {
1726         rmeshstate_t m;
1727         // colorscale accounts for how much we multiply the brightness
1728         // during combine.
1729         //
1730         // mult is how many times the final pass of the lighting will be
1731         // performed to get more brightness than otherwise possible.
1732         //
1733         // Limit mult to 64 for sanity sake.
1734         GL_Color(1,1,1,1);
1735         // generate normalization cubemap texcoords
1736         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1737         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1738         {
1739                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1740                 memset(&m, 0, sizeof(m));
1741                 m.tex[0] = R_GetTexture(normalmaptexture);
1742                 m.texcombinergb[0] = GL_REPLACE;
1743                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1744                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1745                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1746                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1747                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1748                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1749                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1750                 m.pointer_texcoord_bufferobject[1] = 0;
1751                 m.pointer_texcoord_bufferoffset[1] = 0;
1752                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1753                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1754                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1755                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1756                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1757                 R_Mesh_TextureState(&m);
1758                 GL_ColorMask(0,0,0,1);
1759                 GL_BlendFunc(GL_ONE, GL_ZERO);
1760                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1761
1762                 // second pass
1763                 memset(&m, 0, sizeof(m));
1764                 m.tex[0] = R_GetTexture(basetexture);
1765                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1766                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1767                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1768                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1769                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1770                 {
1771                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1772                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1773                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1774                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1775                         m.texmatrix[1] = rsurface.entitytolight;
1776                 }
1777                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1778         }
1779         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1780         {
1781                 // 1/2/2 3D combine path (original Radeon)
1782                 memset(&m, 0, sizeof(m));
1783                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1784                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1785                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1786                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1787                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1788                 R_Mesh_TextureState(&m);
1789                 GL_ColorMask(0,0,0,1);
1790                 GL_BlendFunc(GL_ONE, GL_ZERO);
1791                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1792
1793                 // second pass
1794                 memset(&m, 0, sizeof(m));
1795                 m.tex[0] = R_GetTexture(normalmaptexture);
1796                 m.texcombinergb[0] = GL_REPLACE;
1797                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1798                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1799                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1800                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1801                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1802                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1803                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1804                 m.pointer_texcoord_bufferobject[1] = 0;
1805                 m.pointer_texcoord_bufferoffset[1] = 0;
1806                 R_Mesh_TextureState(&m);
1807                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1808                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1809
1810                 // second pass
1811                 memset(&m, 0, sizeof(m));
1812                 m.tex[0] = R_GetTexture(basetexture);
1813                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1814                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1815                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1816                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1817                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1818                 {
1819                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1820                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1821                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1822                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1823                         m.texmatrix[1] = rsurface.entitytolight;
1824                 }
1825                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1826         }
1827         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1828         {
1829                 // 2/2 3D combine path (original Radeon)
1830                 memset(&m, 0, sizeof(m));
1831                 m.tex[0] = R_GetTexture(normalmaptexture);
1832                 m.texcombinergb[0] = GL_REPLACE;
1833                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1834                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1835                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1836                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1837                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1838                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1839                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1840                 m.pointer_texcoord_bufferobject[1] = 0;
1841                 m.pointer_texcoord_bufferoffset[1] = 0;
1842                 R_Mesh_TextureState(&m);
1843                 GL_ColorMask(0,0,0,1);
1844                 GL_BlendFunc(GL_ONE, GL_ZERO);
1845                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1846
1847                 // second pass
1848                 memset(&m, 0, sizeof(m));
1849                 m.tex[0] = R_GetTexture(basetexture);
1850                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1851                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1852                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1853                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1854                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1855                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1856                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1857                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1858                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
1859                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1860         }
1861         else if (r_textureunits.integer >= 4)
1862         {
1863                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1864                 memset(&m, 0, sizeof(m));
1865                 m.tex[0] = R_GetTexture(normalmaptexture);
1866                 m.texcombinergb[0] = GL_REPLACE;
1867                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1868                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1869                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1870                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1871                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1872                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1873                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1874                 m.pointer_texcoord_bufferobject[1] = 0;
1875                 m.pointer_texcoord_bufferoffset[1] = 0;
1876                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1877                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1878                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1879                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1880                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1881                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1882                 m.pointer_texcoord3f[3] = rsurface.vertex3f;
1883                 m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1884                 m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1885                 m.texmatrix[3] = rsurface.entitytoattenuationz;
1886                 R_Mesh_TextureState(&m);
1887                 GL_ColorMask(0,0,0,1);
1888                 GL_BlendFunc(GL_ONE, GL_ZERO);
1889                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1890
1891                 // second pass
1892                 memset(&m, 0, sizeof(m));
1893                 m.tex[0] = R_GetTexture(basetexture);
1894                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1895                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1896                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1897                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1898                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1899                 {
1900                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1901                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1902                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1903                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1904                         m.texmatrix[1] = rsurface.entitytolight;
1905                 }
1906                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1907         }
1908         else
1909         {
1910                 // 2/2/2 2D combine path (any dot3 card)
1911                 memset(&m, 0, sizeof(m));
1912                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1913                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1914                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1915                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1916                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1917                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1918                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1919                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1920                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1921                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1922                 R_Mesh_TextureState(&m);
1923                 GL_ColorMask(0,0,0,1);
1924                 GL_BlendFunc(GL_ONE, GL_ZERO);
1925                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1926
1927                 // second pass
1928                 memset(&m, 0, sizeof(m));
1929                 m.tex[0] = R_GetTexture(normalmaptexture);
1930                 m.texcombinergb[0] = GL_REPLACE;
1931                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1932                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1933                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1934                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1935                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1936                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1937                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1938                 m.pointer_texcoord_bufferobject[1] = 0;
1939                 m.pointer_texcoord_bufferoffset[1] = 0;
1940                 R_Mesh_TextureState(&m);
1941                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1942                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1943
1944                 // second pass
1945                 memset(&m, 0, sizeof(m));
1946                 m.tex[0] = R_GetTexture(basetexture);
1947                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1948                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1949                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1950                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1951                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1952                 {
1953                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1954                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1955                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1956                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1957                         m.texmatrix[1] = rsurface.entitytolight;
1958                 }
1959                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1960         }
1961         // this final code is shared
1962         R_Mesh_TextureState(&m);
1963         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1964 }
1965
1966 static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1967 {
1968         float glossexponent;
1969         rmeshstate_t m;
1970         // FIXME: detect blendsquare!
1971         //if (!gl_support_blendsquare)
1972         //      return;
1973         GL_Color(1,1,1,1);
1974         // generate normalization cubemap texcoords
1975         R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1976         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1977         {
1978                 // 2/0/0/1/2 3D combine blendsquare path
1979                 memset(&m, 0, sizeof(m));
1980                 m.tex[0] = R_GetTexture(normalmaptexture);
1981                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1982                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1983                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1984                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1985                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1986                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1987                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1988                 m.pointer_texcoord_bufferobject[1] = 0;
1989                 m.pointer_texcoord_bufferoffset[1] = 0;
1990                 R_Mesh_TextureState(&m);
1991                 GL_ColorMask(0,0,0,1);
1992                 // this squares the result
1993                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1994                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1995
1996                 // second and third pass
1997                 R_Mesh_ResetTextureState();
1998                 // square alpha in framebuffer a few times to make it shiny
1999                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2000                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2001                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2002
2003                 // fourth pass
2004                 memset(&m, 0, sizeof(m));
2005                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
2006                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2007                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2008                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2009                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2010                 R_Mesh_TextureState(&m);
2011                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2012                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2013
2014                 // fifth pass
2015                 memset(&m, 0, sizeof(m));
2016                 m.tex[0] = R_GetTexture(glosstexture);
2017                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2018                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2019                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2020                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2021                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2022                 {
2023                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2024                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2025                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2026                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2027                         m.texmatrix[1] = rsurface.entitytolight;
2028                 }
2029                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2030         }
2031         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2032         {
2033                 // 2/0/0/2 3D combine blendsquare path
2034                 memset(&m, 0, sizeof(m));
2035                 m.tex[0] = R_GetTexture(normalmaptexture);
2036                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2037                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2038                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2039                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2040                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2041                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2042                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2043                 m.pointer_texcoord_bufferobject[1] = 0;
2044                 m.pointer_texcoord_bufferoffset[1] = 0;
2045                 R_Mesh_TextureState(&m);
2046                 GL_ColorMask(0,0,0,1);
2047                 // this squares the result
2048                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2049                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2050
2051                 // second and third pass
2052                 R_Mesh_ResetTextureState();
2053                 // square alpha in framebuffer a few times to make it shiny
2054                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2055                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2056                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2057
2058                 // fourth pass
2059                 memset(&m, 0, sizeof(m));
2060                 m.tex[0] = R_GetTexture(glosstexture);
2061                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2062                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2063                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2064                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2065                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2066                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2067                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2068                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2069                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2070                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2071         }
2072         else
2073         {
2074                 // 2/0/0/2/2 2D combine blendsquare path
2075                 memset(&m, 0, sizeof(m));
2076                 m.tex[0] = R_GetTexture(normalmaptexture);
2077                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2078                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2079                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2080                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2081                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2082                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2083                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2084                 m.pointer_texcoord_bufferobject[1] = 0;
2085                 m.pointer_texcoord_bufferoffset[1] = 0;
2086                 R_Mesh_TextureState(&m);
2087                 GL_ColorMask(0,0,0,1);
2088                 // this squares the result
2089                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2090                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2091
2092                 // second and third pass
2093                 R_Mesh_ResetTextureState();
2094                 // square alpha in framebuffer a few times to make it shiny
2095                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2096                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2097                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2098
2099                 // fourth pass
2100                 memset(&m, 0, sizeof(m));
2101                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2102                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2103                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2104                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2105                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2106                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2107                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2108                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2109                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2110                 m.texmatrix[1] = rsurface.entitytoattenuationz;
2111                 R_Mesh_TextureState(&m);
2112                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2113                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2114
2115                 // fifth pass
2116                 memset(&m, 0, sizeof(m));
2117                 m.tex[0] = R_GetTexture(glosstexture);
2118                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2119                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2120                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2121                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2122                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2123                 {
2124                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2125                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2126                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2127                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2128                         m.texmatrix[1] = rsurface.entitytolight;
2129                 }
2130                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2131         }
2132         // this final code is shared
2133         R_Mesh_TextureState(&m);
2134         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
2135 }
2136
2137 static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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)
2138 {
2139         // ARB path (any Geforce, any Radeon)
2140         qboolean doambient = ambientscale > 0;
2141         qboolean dodiffuse = diffusescale > 0;
2142         qboolean dospecular = specularscale > 0;
2143         if (!doambient && !dodiffuse && !dospecular)
2144                 return;
2145         R_Mesh_ColorPointer(NULL, 0, 0);
2146         if (doambient)
2147                 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, ambientscale * r_view.colorscale);
2148         if (dodiffuse)
2149                 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_view.colorscale);
2150         if (dopants)
2151         {
2152                 if (doambient)
2153                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, ambientscale * r_view.colorscale);
2154                 if (dodiffuse)
2155                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_view.colorscale);
2156         }
2157         if (doshirt)
2158         {
2159                 if (doambient)
2160                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, ambientscale * r_view.colorscale);
2161                 if (dodiffuse)
2162                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_view.colorscale);
2163         }
2164         if (dospecular)
2165                 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_view.colorscale);
2166 }
2167
2168 void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, vec3_t diffusecolor2, vec3_t ambientcolor2)
2169 {
2170         int renders;
2171         int i;
2172         int stop;
2173         int newfirstvertex;
2174         int newlastvertex;
2175         int newnumtriangles;
2176         int *newe;
2177         const int *e;
2178         float *c;
2179         int newelements[4096*3];
2180         R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
2181         for (renders = 0;renders < 64;renders++)
2182         {
2183                 stop = true;
2184                 newfirstvertex = 0;
2185                 newlastvertex = 0;
2186                 newnumtriangles = 0;
2187                 newe = newelements;
2188                 // due to low fillrate on the cards this vertex lighting path is
2189                 // designed for, we manually cull all triangles that do not
2190                 // contain a lit vertex
2191                 // this builds batches of triangles from multiple surfaces and
2192                 // renders them at once
2193                 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
2194                 {
2195                         if (VectorLength2(rsurface.array_color4f + e[0] * 4) + VectorLength2(rsurface.array_color4f + e[1] * 4) + VectorLength2(rsurface.array_color4f + e[2] * 4) >= 0.01)
2196                         {
2197                                 if (newnumtriangles)
2198                                 {
2199                                         newfirstvertex = min(newfirstvertex, e[0]);
2200                                         newlastvertex  = max(newlastvertex, e[0]);
2201                                 }
2202                                 else
2203                                 {
2204                                         newfirstvertex = e[0];
2205                                         newlastvertex = e[0];
2206                                 }
2207                                 newfirstvertex = min(newfirstvertex, e[1]);
2208                                 newlastvertex  = max(newlastvertex, e[1]);
2209                                 newfirstvertex = min(newfirstvertex, e[2]);
2210                                 newlastvertex  = max(newlastvertex, e[2]);
2211                                 newe[0] = e[0];
2212                                 newe[1] = e[1];
2213                                 newe[2] = e[2];
2214                                 newnumtriangles++;
2215                                 newe += 3;
2216                                 if (newnumtriangles >= (int)(sizeof(newelements)/sizeof(float[3])))
2217                                 {
2218                                         R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2219                                         newnumtriangles = 0;
2220                                         newe = newelements;
2221                                         stop = false;
2222                                 }
2223                         }
2224                 }
2225                 if (newnumtriangles >= 1)
2226                 {
2227                         // if all triangles are included, use the original array to take advantage of the bufferobject if possible
2228                         if (newnumtriangles == numtriangles)
2229                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2230                         else
2231                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2232                         stop = false;
2233                 }
2234                 // if we couldn't find any lit triangles, exit early
2235                 if (stop)
2236                         break;
2237                 // now reduce the intensity for the next overbright pass
2238                 // we have to clamp to 0 here incase the drivers have improper
2239                 // handling of negative colors
2240                 // (some old drivers even have improper handling of >1 color)
2241                 stop = true;
2242                 for (i = 0, c = rsurface.array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
2243                 {
2244                         if (c[0] > 1 || c[1] > 1 || c[2] > 1)
2245                         {
2246                                 c[0] = max(0, c[0] - 1);
2247                                 c[1] = max(0, c[1] - 1);
2248                                 c[2] = max(0, c[2] - 1);
2249                                 stop = false;
2250                         }
2251                         else
2252                                 VectorClear(c);
2253                 }
2254                 // another check...
2255                 if (stop)
2256                         break;
2257         }
2258 }
2259
2260 static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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)
2261 {
2262         // OpenGL 1.1 path (anything)
2263         float ambientcolorbase[3], diffusecolorbase[3];
2264         float ambientcolorpants[3], diffusecolorpants[3];
2265         float ambientcolorshirt[3], diffusecolorshirt[3];
2266         rmeshstate_t m;
2267         VectorScale(lightcolorbase, ambientscale * 2 * r_view.colorscale, ambientcolorbase);
2268         VectorScale(lightcolorbase, diffusescale * 2 * r_view.colorscale, diffusecolorbase);
2269         VectorScale(lightcolorpants, ambientscale * 2 * r_view.colorscale, ambientcolorpants);
2270         VectorScale(lightcolorpants, diffusescale * 2 * r_view.colorscale, diffusecolorpants);
2271         VectorScale(lightcolorshirt, ambientscale * 2 * r_view.colorscale, ambientcolorshirt);
2272         VectorScale(lightcolorshirt, diffusescale * 2 * r_view.colorscale, diffusecolorshirt);
2273         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2274         R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
2275         memset(&m, 0, sizeof(m));
2276         m.tex[0] = R_GetTexture(basetexture);
2277         m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2278         m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2279         m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2280         m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2281         if (r_textureunits.integer >= 2)
2282         {
2283                 // voodoo2 or TNT
2284                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2285                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2286                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2287                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2288                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2289                 if (r_textureunits.integer >= 3)
2290                 {
2291                         // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2292                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2293                         m.texmatrix[2] = rsurface.entitytoattenuationz;
2294                         m.pointer_texcoord3f[2] = rsurface.vertex3f;
2295                         m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
2296                         m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
2297                 }
2298         }
2299         R_Mesh_TextureState(&m);
2300         //R_Mesh_TexBind(0, R_GetTexture(basetexture));
2301         R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorbase, ambientcolorbase);
2302         if (dopants)
2303         {
2304                 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2305                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorpants, ambientcolorpants);
2306         }
2307         if (doshirt)
2308         {
2309                 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2310                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorshirt, ambientcolorshirt);
2311         }
2312 }
2313
2314 extern cvar_t gl_lightmaps;
2315 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset)
2316 {
2317         float ambientscale, diffusescale, specularscale;
2318         vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2319         rtexture_t *nmap;
2320         // calculate colors to render this texture with
2321         lightcolorbase[0] = rsurface.rtlight->currentcolor[0] * rsurface.texture->dlightcolor[0];
2322         lightcolorbase[1] = rsurface.rtlight->currentcolor[1] * rsurface.texture->dlightcolor[1];
2323         lightcolorbase[2] = rsurface.rtlight->currentcolor[2] * rsurface.texture->dlightcolor[2];
2324         ambientscale = rsurface.rtlight->ambientscale;
2325         diffusescale = rsurface.rtlight->diffusescale;
2326         specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
2327         if (!r_shadow_usenormalmap.integer)
2328         {
2329                 ambientscale += 1.0f * diffusescale;
2330                 diffusescale = 0;
2331                 specularscale = 0;
2332         }
2333         if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2334                 return;
2335         GL_DepthRange(0, (rsurface.texture->currentmaterialflags & MATERIALFLAG_SHORTDEPTHRANGE) ? 0.0625 : 1);
2336         GL_PolygonOffset(rsurface.texture->currentpolygonfactor, rsurface.texture->currentpolygonoffset);
2337         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST));
2338         GL_CullFace((rsurface.texture->currentmaterialflags & MATERIALFLAG_NOCULLFACE) ? GL_NONE : r_view.cullface_back);
2339         nmap = rsurface.texture->currentskinframe->nmap;
2340         if (gl_lightmaps.integer)
2341                 nmap = r_texture_blanknormalmap;
2342         if (rsurface.texture->colormapping && !gl_lightmaps.integer)
2343         {
2344                 qboolean dopants = rsurface.texture->currentskinframe->pants != NULL && VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f);
2345                 qboolean doshirt = rsurface.texture->currentskinframe->shirt != NULL && VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
2346                 if (dopants)
2347                 {
2348                         lightcolorpants[0] = lightcolorbase[0] * rsurface.colormap_pantscolor[0];
2349                         lightcolorpants[1] = lightcolorbase[1] * rsurface.colormap_pantscolor[1];
2350                         lightcolorpants[2] = lightcolorbase[2] * rsurface.colormap_pantscolor[2];
2351                 }
2352                 else
2353                         VectorClear(lightcolorpants);
2354                 if (doshirt)
2355                 {
2356                         lightcolorshirt[0] = lightcolorbase[0] * rsurface.colormap_shirtcolor[0];
2357                         lightcolorshirt[1] = lightcolorbase[1] * rsurface.colormap_shirtcolor[1];
2358                         lightcolorshirt[2] = lightcolorbase[2] * rsurface.colormap_shirtcolor[2];
2359                 }
2360                 else
2361                         VectorClear(lightcolorshirt);
2362                 switch (r_shadow_rendermode)
2363                 {
2364                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2365                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2366                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2367                         break;
2368                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2369                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2370                         break;
2371                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2372                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2373                         break;
2374                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2375                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2376                         break;
2377                 default:
2378                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2379                         break;
2380                 }
2381         }
2382         else
2383         {
2384                 switch (r_shadow_rendermode)
2385                 {
2386                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2387                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2388                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2389                         break;
2390                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2391                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2392                         break;
2393                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2394                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2395                         break;
2396                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2397                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2398                         break;
2399                 default:
2400                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2401                         break;
2402                 }
2403         }
2404 }
2405
2406 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)
2407 {
2408         matrix4x4_t tempmatrix = *matrix;
2409         Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2410
2411         // if this light has been compiled before, free the associated data
2412         R_RTLight_Uncompile(rtlight);
2413
2414         // clear it completely to avoid any lingering data
2415         memset(rtlight, 0, sizeof(*rtlight));
2416
2417         // copy the properties
2418         rtlight->matrix_lighttoworld = tempmatrix;
2419         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2420         Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2421         rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2422         VectorCopy(color, rtlight->color);
2423         rtlight->cubemapname[0] = 0;
2424         if (cubemapname && cubemapname[0])
2425                 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2426         rtlight->shadow = shadow;
2427         rtlight->corona = corona;
2428         rtlight->style = style;
2429         rtlight->isstatic = isstatic;
2430         rtlight->coronasizescale = coronasizescale;
2431         rtlight->ambientscale = ambientscale;
2432         rtlight->diffusescale = diffusescale;
2433         rtlight->specularscale = specularscale;
2434         rtlight->flags = flags;
2435
2436         // compute derived data
2437         //rtlight->cullradius = rtlight->radius;
2438         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2439         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2440         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2441         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2442         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2443         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2444         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2445 }
2446
2447 // compiles rtlight geometry
2448 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2449 void R_RTLight_Compile(rtlight_t *rtlight)
2450 {
2451         int i;
2452         int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2453         int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2454         entity_render_t *ent = r_refdef.worldentity;
2455         model_t *model = r_refdef.worldmodel;
2456         unsigned char *data;
2457
2458         // compile the light
2459         rtlight->compiled = true;
2460         rtlight->static_numleafs = 0;
2461         rtlight->static_numleafpvsbytes = 0;
2462         rtlight->static_leaflist = NULL;
2463         rtlight->static_leafpvs = NULL;
2464         rtlight->static_numsurfaces = 0;
2465         rtlight->static_surfacelist = NULL;
2466         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2467         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2468         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2469         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2470         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2471         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2472
2473         if (model && model->GetLightInfo)
2474         {
2475                 // this variable must be set for the CompileShadowVolume code
2476                 r_shadow_compilingrtlight = rtlight;
2477                 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);
2478                 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);
2479                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2480                 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2481                 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2482                 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2483                 rtlight->static_numsurfaces = numsurfaces;
2484                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2485                 rtlight->static_numleafs = numleafs;
2486                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2487                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2488                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2489                 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2490                 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2491                 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2492                 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2493                 if (rtlight->static_numsurfaces)
2494                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2495                 if (rtlight->static_numleafs)
2496                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2497                 if (rtlight->static_numleafpvsbytes)
2498                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2499                 if (rtlight->static_numshadowtrispvsbytes)
2500                         memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2501                 if (rtlight->static_numlighttrispvsbytes)
2502                         memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2503                 if (model->CompileShadowVolume && rtlight->shadow)
2504                         model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2505                 // now we're done compiling the rtlight
2506                 r_shadow_compilingrtlight = NULL;
2507         }
2508
2509
2510         // use smallest available cullradius - box radius or light radius
2511         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2512         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2513
2514         shadowmeshes = 0;
2515         shadowmeshtris = 0;
2516         if (rtlight->static_meshchain_shadow)
2517         {
2518                 shadowmesh_t *mesh;
2519                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2520                 {
2521                         shadowmeshes++;
2522                         shadowmeshtris += mesh->numtriangles;
2523                 }
2524         }
2525
2526         lighttris = 0;
2527         if (rtlight->static_numlighttrispvsbytes)
2528                 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2529                         if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2530                                 lighttris++;
2531
2532         shadowtris = 0;
2533         if (rtlight->static_numlighttrispvsbytes)
2534                 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2535                         if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2536                                 shadowtris++;
2537
2538         if (developer.integer >= 10)
2539                 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);
2540 }
2541
2542 void R_RTLight_Uncompile(rtlight_t *rtlight)
2543 {
2544         if (rtlight->compiled)
2545         {
2546                 if (rtlight->static_meshchain_shadow)
2547                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2548                 rtlight->static_meshchain_shadow = NULL;
2549                 // these allocations are grouped
2550                 if (rtlight->static_surfacelist)
2551                         Mem_Free(rtlight->static_surfacelist);
2552                 rtlight->static_numleafs = 0;
2553                 rtlight->static_numleafpvsbytes = 0;
2554                 rtlight->static_leaflist = NULL;
2555                 rtlight->static_leafpvs = NULL;
2556                 rtlight->static_numsurfaces = 0;
2557                 rtlight->static_surfacelist = NULL;
2558                 rtlight->static_numshadowtrispvsbytes = 0;
2559                 rtlight->static_shadowtrispvs = NULL;
2560                 rtlight->static_numlighttrispvsbytes = 0;
2561                 rtlight->static_lighttrispvs = NULL;
2562                 rtlight->compiled = false;
2563         }
2564 }
2565
2566 void R_Shadow_UncompileWorldLights(void)
2567 {
2568         size_t lightindex;
2569         dlight_t *light;
2570         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
2571         {
2572                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
2573                 if (!light)
2574                         continue;
2575                 R_RTLight_Uncompile(&light->rtlight);
2576         }
2577 }
2578
2579 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
2580 {
2581         int i, j;
2582         mplane_t plane;
2583         // reset the count of frustum planes
2584         // see rsurface.rtlight_frustumplanes definition for how much this array
2585         // can hold
2586         rsurface.rtlight_numfrustumplanes = 0;
2587
2588         // haven't implemented a culling path for ortho rendering
2589         if (!r_view.useperspective)
2590         {
2591                 // check if the light is on screen and copy the 4 planes if it is
2592                 for (i = 0;i < 4;i++)
2593                         if (PlaneDiff(rtlight->shadoworigin, &r_view.frustum[i]) < -0.03125)
2594                                 break;
2595                 if (i == 4)
2596                         for (i = 0;i < 4;i++)
2597                                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_view.frustum[i];
2598                 return;
2599         }
2600
2601 #if 1
2602         // generate a deformed frustum that includes the light origin, this is
2603         // used to cull shadow casting surfaces that can not possibly cast a
2604         // shadow onto the visible light-receiving surfaces, which can be a
2605         // performance gain
2606         //
2607         // if the light origin is onscreen the result will be 4 planes exactly
2608         // if the light origin is offscreen on only one axis the result will
2609         // be exactly 5 planes (split-side case)
2610         // if the light origin is offscreen on two axes the result will be
2611         // exactly 4 planes (stretched corner case)
2612         for (i = 0;i < 4;i++)
2613         {
2614                 // quickly reject standard frustum planes that put the light
2615                 // origin outside the frustum
2616                 if (PlaneDiff(rtlight->shadoworigin, &r_view.frustum[i]) < -0.03125)
2617                         continue;
2618                 // copy the plane
2619                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_view.frustum[i];
2620         }
2621         // if all the standard frustum planes were accepted, the light is onscreen
2622         // otherwise we need to generate some more planes below...
2623         if (rsurface.rtlight_numfrustumplanes < 4)
2624         {
2625                 // at least one of the stock frustum planes failed, so we need to
2626                 // create one or two custom planes to enclose the light origin
2627                 for (i = 0;i < 4;i++)
2628                 {
2629                         // create a plane using the view origin and light origin, and a
2630                         // single point from the frustum corner set
2631                         TriangleNormal(r_view.origin, r_view.frustumcorner[i], rtlight->shadoworigin, plane.normal);
2632                         VectorNormalize(plane.normal);
2633                         plane.dist = DotProduct(r_view.origin, plane.normal);
2634                         // see if this plane is backwards and flip it if so
2635                         for (j = 0;j < 4;j++)
2636                                 if (j != i && DotProduct(r_view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2637                                         break;
2638                         if (j < 4)
2639                         {
2640                                 VectorNegate(plane.normal, plane.normal);
2641                                 plane.dist *= -1;
2642                                 // flipped plane, test again to see if it is now valid
2643                                 for (j = 0;j < 4;j++)
2644                                         if (j != i && DotProduct(r_view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2645                                                 break;
2646                                 // if the plane is still not valid, then it is dividing the
2647                                 // frustum and has to be rejected
2648                                 if (j < 4)
2649                                         continue;
2650                         }
2651                         // we have created a valid plane, compute extra info
2652                         PlaneClassify(&plane);
2653                         // copy the plane
2654                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2655 #if 1
2656                         // if we've found 5 frustum planes then we have constructed a
2657                         // proper split-side case and do not need to keep searching for
2658                         // planes to enclose the light origin
2659                         if (rsurface.rtlight_numfrustumplanes == 5)
2660                                 break;
2661 #endif
2662                 }
2663         }
2664 #endif
2665
2666 #if 0
2667         for (i = 0;i < rsurface.rtlight_numfrustumplanes;i++)
2668         {
2669                 plane = rsurface.rtlight_frustumplanes[i];
2670                 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));
2671         }
2672 #endif
2673
2674 #if 0
2675         // now add the light-space box planes if the light box is rotated, as any
2676         // caster outside the oriented light box is irrelevant (even if it passed
2677         // the worldspace light box, which is axial)
2678         if (rtlight->matrix_lighttoworld.m[0][0] != 1 || rtlight->matrix_lighttoworld.m[1][1] != 1 || rtlight->matrix_lighttoworld.m[2][2] != 1)
2679         {
2680                 for (i = 0;i < 6;i++)
2681                 {
2682                         vec3_t v;
2683                         VectorClear(v);
2684                         v[i >> 1] = (i & 1) ? -1 : 1;
2685                         Matrix4x4_Transform(&rtlight->matrix_lighttoworld, v, plane.normal);
2686                         VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
2687                         plane.dist = VectorNormalizeLength(plane.normal);
2688                         plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
2689                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2690                 }
2691         }
2692 #endif
2693
2694 #if 0
2695         // add the world-space reduced box planes
2696         for (i = 0;i < 6;i++)
2697         {
2698                 VectorClear(plane.normal);
2699                 plane.normal[i >> 1] = (i & 1) ? -1 : 1;
2700                 plane.dist = (i & 1) ? -rsurface.rtlight_cullmaxs[i >> 1] : rsurface.rtlight_cullmins[i >> 1];
2701                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2702         }
2703 #endif
2704
2705 #if 0
2706         {
2707         int j, oldnum;
2708         vec3_t points[8];
2709         vec_t bestdist;
2710         // reduce all plane distances to tightly fit the rtlight cull box, which
2711         // is in worldspace
2712         VectorSet(points[0], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2713         VectorSet(points[1], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2714         VectorSet(points[2], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2715         VectorSet(points[3], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2716         VectorSet(points[4], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2717         VectorSet(points[5], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2718         VectorSet(points[6], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2719         VectorSet(points[7], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2720         oldnum = rsurface.rtlight_numfrustumplanes;
2721         rsurface.rtlight_numfrustumplanes = 0;
2722         for (j = 0;j < oldnum;j++)
2723         {
2724                 // find the nearest point on the box to this plane
2725                 bestdist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[0]);
2726                 for (i = 1;i < 8;i++)
2727                 {
2728                         dist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[i]);
2729                         if (bestdist > dist)
2730                                 bestdist = dist;
2731                 }
2732                 Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, rsurface.rtlight_frustumplanes[j].normal[0], rsurface.rtlight_frustumplanes[j].normal[1], rsurface.rtlight_frustumplanes[j].normal[2], rsurface.rtlight_frustumplanes[j].dist, bestdist);
2733                 // if the nearest point is near or behind the plane, we want this
2734                 // plane, otherwise the plane is useless as it won't cull anything
2735                 if (rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125)
2736                 {
2737                         PlaneClassify(&rsurface.rtlight_frustumplanes[j]);
2738                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = rsurface.rtlight_frustumplanes[j];
2739                 }
2740         }
2741         }
2742 #endif
2743 }
2744
2745 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2746 {
2747         RSurf_ActiveWorldEntity();
2748         if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2749         {
2750                 shadowmesh_t *mesh;
2751                 CHECKGLERROR
2752                 for (mesh = rsurface.rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2753                 {
2754                         r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2755                         R_Mesh_VertexPointer(mesh->vertex3f, mesh->vbo, mesh->vbooffset_vertex3f);
2756                         GL_LockArrays(0, mesh->numverts);
2757                         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2758                         {
2759                                 // decrement stencil if backface is behind depthbuffer
2760                                 GL_CullFace(r_view.cullface_front);
2761                                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2762                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2763                                 // increment stencil if frontface is behind depthbuffer
2764                                 GL_CullFace(r_view.cullface_back);
2765                                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2766                         }
2767                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2768                         GL_LockArrays(0, 0);
2769                 }
2770                 CHECKGLERROR
2771         }
2772         else if (numsurfaces && r_refdef.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2773         {
2774                 int t, tend;
2775                 int surfacelistindex;
2776                 msurface_t *surface;
2777                 R_Shadow_PrepareShadowMark(r_refdef.worldmodel->brush.shadowmesh->numtriangles);
2778                 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2779                 {
2780                         surface = r_refdef.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2781                         for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2782                                 if (CHECKPVSBIT(trispvs, t))
2783                                         shadowmarklist[numshadowmark++] = t;
2784                 }
2785                 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, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
2786         }
2787         else if (numsurfaces)
2788                 r_refdef.worldmodel->DrawShadowVolume(r_refdef.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
2789 }
2790
2791 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2792 {
2793         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2794         vec_t relativeshadowradius;
2795         RSurf_ActiveModelEntity(ent, false, false);
2796         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, relativeshadoworigin);
2797         relativeshadowradius = rsurface.rtlight->radius / ent->scale;
2798         relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2799         relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2800         relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2801         relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2802         relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2803         relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2804         ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2805 }
2806
2807 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2808 {
2809         // set up properties for rendering light onto this entity
2810         RSurf_ActiveModelEntity(ent, true, true);
2811         Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
2812         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2813         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2814         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2815         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2816                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2817 }
2818
2819 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2820 {
2821         if (!r_refdef.worldmodel->DrawLight)
2822                 return;
2823
2824         // set up properties for rendering light onto this entity
2825         RSurf_ActiveWorldEntity();
2826         rsurface.entitytolight = rsurface.rtlight->matrix_worldtolight;
2827         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2828         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2829         VectorCopy(rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2830         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2831                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2832
2833         r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
2834 }
2835
2836 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2837 {
2838         model_t *model = ent->model;
2839         if (!model->DrawLight)
2840                 return;
2841
2842         R_Shadow_SetupEntityLight(ent);
2843
2844         model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2845 }
2846
2847 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2848 {
2849         int i;
2850         float f;
2851         int numleafs, numsurfaces;
2852         int *leaflist, *surfacelist;
2853         unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2854         int numlightentities;
2855         int numlightentities_noselfshadow;
2856         int numshadowentities;
2857         int numshadowentities_noselfshadow;
2858         entity_render_t *lightentities[MAX_EDICTS];
2859         entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
2860         entity_render_t *shadowentities[MAX_EDICTS];
2861         entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
2862
2863         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2864         // skip lights that are basically invisible (color 0 0 0)
2865         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2866                 return;
2867
2868         // loading is done before visibility checks because loading should happen
2869         // all at once at the start of a level, not when it stalls gameplay.
2870         // (especially important to benchmarks)
2871         // compile light
2872         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2873                 R_RTLight_Compile(rtlight);
2874         // load cubemap
2875         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2876
2877         // look up the light style value at this time
2878         f = (rtlight->style >= 0 ? r_refdef.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
2879         VectorScale(rtlight->color, f, rtlight->currentcolor);
2880         /*
2881         if (rtlight->selected)
2882         {
2883                 f = 2 + sin(realtime * M_PI * 4.0);
2884                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2885         }
2886         */
2887
2888         // if lightstyle is currently off, don't draw the light
2889         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2890                 return;
2891
2892         // if the light box is offscreen, skip it
2893         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2894                 return;
2895
2896         VectorCopy(rtlight->cullmins, rsurface.rtlight_cullmins);
2897         VectorCopy(rtlight->cullmaxs, rsurface.rtlight_cullmaxs);
2898
2899         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2900         {
2901                 // compiled light, world available and can receive realtime lighting
2902                 // retrieve leaf information
2903                 numleafs = rtlight->static_numleafs;
2904                 leaflist = rtlight->static_leaflist;
2905                 leafpvs = rtlight->static_leafpvs;
2906                 numsurfaces = rtlight->static_numsurfaces;
2907                 surfacelist = rtlight->static_surfacelist;
2908                 shadowtrispvs = rtlight->static_shadowtrispvs;
2909                 lighttrispvs = rtlight->static_lighttrispvs;
2910         }
2911         else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2912         {
2913                 // dynamic light, world available and can receive realtime lighting
2914                 // calculate lit surfaces and leafs
2915                 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);
2916                 r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rsurface.rtlight_cullmins, rsurface.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);
2917                 leaflist = r_shadow_buffer_leaflist;
2918                 leafpvs = r_shadow_buffer_leafpvs;
2919                 surfacelist = r_shadow_buffer_surfacelist;
2920                 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2921                 lighttrispvs = r_shadow_buffer_lighttrispvs;
2922                 // if the reduced leaf bounds are offscreen, skip it
2923                 if (R_CullBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2924                         return;
2925         }
2926         else
2927         {
2928                 // no world
2929                 numleafs = 0;
2930                 leaflist = NULL;
2931                 leafpvs = NULL;
2932                 numsurfaces = 0;
2933                 surfacelist = NULL;
2934                 shadowtrispvs = NULL;
2935                 lighttrispvs = NULL;
2936         }
2937         // check if light is illuminating any visible leafs
2938         if (numleafs)
2939         {
2940                 for (i = 0;i < numleafs;i++)
2941                         if (r_viewcache.world_leafvisible[leaflist[i]])
2942                                 break;
2943                 if (i == numleafs)
2944                         return;
2945         }
2946         // set up a scissor rectangle for this light
2947         if (R_Shadow_ScissorForBBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2948                 return;
2949
2950         R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
2951
2952         // make a list of lit entities and shadow casting entities
2953         numlightentities = 0;
2954         numlightentities_noselfshadow = 0;
2955         numshadowentities = 0;
2956         numshadowentities_noselfshadow = 0;
2957         // add dynamic entities that are lit by the light
2958         if (r_drawentities.integer)
2959         {
2960                 for (i = 0;i < r_refdef.numentities;i++)
2961                 {
2962                         model_t *model;
2963                         entity_render_t *ent = r_refdef.entities[i];
2964                         vec3_t org;
2965                         if (!BoxesOverlap(ent->mins, ent->maxs, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2966                                 continue;
2967                         // skip the object entirely if it is not within the valid
2968                         // shadow-casting region (which includes the lit region)
2969                         if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rsurface.rtlight_numfrustumplanes, rsurface.rtlight_frustumplanes))
2970                                 continue;
2971                         if (!(model = ent->model))
2972                                 continue;
2973                         if (r_viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2974                         {
2975                                 // this entity wants to receive light, is visible, and is
2976                                 // inside the light box
2977                                 // TODO: check if the surfaces in the model can receive light
2978                                 // so now check if it's in a leaf seen by the light
2979                                 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2980                                         continue;
2981                                 if (ent->flags & RENDER_NOSELFSHADOW)
2982                                         lightentities_noselfshadow[numlightentities_noselfshadow++] = ent;
2983                                 else
2984                                         lightentities[numlightentities++] = ent;
2985                                 // since it is lit, it probably also casts a shadow...
2986                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2987                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2988                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2989                                 {
2990                                         // note: exterior models without the RENDER_NOSELFSHADOW
2991                                         // flag still create a RENDER_NOSELFSHADOW shadow but
2992                                         // are lit normally, this means that they are
2993                                         // self-shadowing but do not shadow other
2994                                         // RENDER_NOSELFSHADOW entities such as the gun
2995                                         // (very weird, but keeps the player shadow off the gun)
2996                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
2997                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
2998                                         else
2999                                                 shadowentities[numshadowentities++] = ent;
3000                                 }
3001                         }
3002                         else if (ent->flags & RENDER_SHADOW)
3003                         {
3004                                 // this entity is not receiving light, but may still need to
3005                                 // cast a shadow...
3006                                 // TODO: check if the surfaces in the model can cast shadow
3007                                 // now check if it is in a leaf seen by the light
3008                                 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
3009                                         continue;
3010                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
3011                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
3012                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
3013                                 {
3014                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
3015                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
3016                                         else
3017                                                 shadowentities[numshadowentities++] = ent;
3018                                 }
3019                         }
3020                 }
3021         }
3022
3023         // return if there's nothing at all to light
3024         if (!numlightentities && !numsurfaces)
3025                 return;
3026
3027         // don't let sound skip if going slow
3028         if (r_refdef.extraupdate)
3029                 S_ExtraUpdate ();
3030
3031         // make this the active rtlight for rendering purposes
3032         R_Shadow_RenderMode_ActiveLight(rtlight);
3033         // count this light in the r_speeds
3034         r_refdef.stats.lights++;
3035
3036         if (r_showshadowvolumes.integer && r_view.showdebug && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3037         {
3038                 // optionally draw visible shape of the shadow volumes
3039                 // for performance analysis by level designers
3040                 R_Shadow_RenderMode_VisibleShadowVolumes();
3041                 if (numsurfaces)
3042                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3043                 for (i = 0;i < numshadowentities;i++)
3044                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3045                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3046                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3047         }
3048
3049         if (gl_stencil && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3050         {
3051                 // draw stencil shadow volumes to mask off pixels that are in shadow
3052                 // so that they won't receive lighting
3053                 R_Shadow_RenderMode_StencilShadowVolumes(true);
3054                 if (numsurfaces)
3055                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3056                 for (i = 0;i < numshadowentities;i++)
3057                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3058                 if (numlightentities_noselfshadow)
3059                 {
3060                         // draw lighting in the unmasked areas
3061                         R_Shadow_RenderMode_Lighting(true, false);
3062                         for (i = 0;i < numlightentities_noselfshadow;i++)
3063                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
3064
3065                         // optionally draw the illuminated areas
3066                         // for performance analysis by level designers
3067                         if (r_showlighting.integer && r_view.showdebug)
3068                         {
3069                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3070                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3071                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
3072                         }
3073
3074                         R_Shadow_RenderMode_StencilShadowVolumes(false);
3075                 }
3076                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3077                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3078
3079                 if (numsurfaces + numlightentities)
3080                 {
3081                         // draw lighting in the unmasked areas
3082                         R_Shadow_RenderMode_Lighting(true, false);
3083                         if (numsurfaces)
3084                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3085                         for (i = 0;i < numlightentities;i++)
3086                                 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
3087
3088                         // optionally draw the illuminated areas
3089                         // for performance analysis by level designers
3090                         if (r_showlighting.integer && r_view.showdebug)
3091                         {
3092                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3093                                 if (numsurfaces)
3094                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3095                                 for (i = 0;i < numlightentities;i++)
3096                                         R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
3097                         }
3098                 }
3099         }
3100         else
3101         {
3102                 if (numsurfaces + numlightentities)
3103                 {
3104                         // draw lighting in the unmasked areas
3105                         R_Shadow_RenderMode_Lighting(false, false);
3106                         if (numsurfaces)
3107                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3108                         for (i = 0;i < numlightentities;i++)
3109                                 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
3110                         for (i = 0;i < numlightentities_noselfshadow;i++)
3111                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
3112
3113                         // optionally draw the illuminated areas
3114                         // for performance analysis by level designers
3115                         if (r_showlighting.integer && r_view.showdebug)
3116                         {
3117                                 R_Shadow_RenderMode_VisibleLighting(false, false);
3118                                 if (numsurfaces)
3119                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3120                                 for (i = 0;i < numlightentities;i++)
3121                                         R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
3122                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3123                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i], numsurfaces, surfacelist);
3124                         }
3125                 }
3126         }
3127 }
3128
3129 void R_Shadow_DrawLightSprites(void);
3130 void R_ShadowVolumeLighting(qboolean visible)
3131 {
3132         int flag;
3133         int lnum;
3134         size_t lightindex;
3135         dlight_t *light;
3136
3137         if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
3138                 R_Shadow_EditLights_Reload_f();
3139
3140         if (r_editlights.integer)
3141                 R_Shadow_DrawLightSprites();
3142
3143         R_Shadow_RenderMode_Begin();
3144
3145         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3146         if (r_shadow_debuglight.integer >= 0)
3147         {
3148                 lightindex = r_shadow_debuglight.integer;
3149                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3150                 if (light && (light->flags & flag))
3151                         R_DrawRTLight(&light->rtlight, visible);
3152         }
3153         else
3154         {
3155                 for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3156                 {
3157                         light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3158                         if (light && (light->flags & flag))
3159                                 R_DrawRTLight(&light->rtlight, visible);
3160                 }
3161         }
3162         if (r_refdef.rtdlight)
3163                 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
3164                         R_DrawRTLight(&r_refdef.lights[lnum], visible);
3165
3166         R_Shadow_RenderMode_End();
3167 }
3168
3169 extern void R_SetupView(void);
3170 extern cvar_t r_shadows_throwdistance;
3171 void R_DrawModelShadows(void)
3172 {
3173         int i;
3174         float relativethrowdistance;
3175         entity_render_t *ent;
3176         vec3_t relativelightorigin;
3177         vec3_t relativelightdirection;
3178         vec3_t relativeshadowmins, relativeshadowmaxs;
3179         float vertex3f[12];
3180
3181         if (!r_drawentities.integer || !gl_stencil)
3182                 return;
3183
3184         CHECKGLERROR
3185         GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
3186
3187         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
3188
3189         if (gl_ext_separatestencil.integer)
3190                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
3191         else if (gl_ext_stenciltwoside.integer)
3192                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
3193         else
3194                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
3195
3196         R_Shadow_RenderMode_StencilShadowVolumes(true);
3197
3198         for (i = 0;i < r_refdef.numentities;i++)
3199         {
3200                 ent = r_refdef.entities[i];
3201                 // cast shadows from anything that is not a submodel of the map
3202                 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
3203                 {
3204                         relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
3205                         VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
3206                         VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
3207                         VectorNegate(ent->modellight_lightdir, relativelightdirection);
3208                         VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
3209                         RSurf_ActiveModelEntity(ent, false, false);
3210                         ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
3211                 }
3212         }
3213
3214         // not really the right mode, but this will disable any silly stencil features
3215         R_Shadow_RenderMode_VisibleLighting(true, true);
3216
3217         // vertex coordinates for a quad that covers the screen exactly
3218         vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
3219         vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
3220         vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
3221         vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
3222
3223         // set up ortho view for rendering this pass
3224         GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
3225         GL_Scissor(r_view.x, r_view.y, r_view.width, r_view.height);
3226         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
3227         GL_ScissorTest(true);
3228         R_Mesh_Matrix(&identitymatrix);
3229         R_Mesh_ResetTextureState();
3230         R_Mesh_VertexPointer(vertex3f, 0, 0);
3231         R_Mesh_ColorPointer(NULL, 0, 0);
3232
3233         // set up a 50% darkening blend on shadowed areas
3234         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3235         GL_DepthRange(0, 1);
3236         GL_DepthTest(false);
3237         GL_DepthMask(false);
3238         GL_PolygonOffset(0, 0);CHECKGLERROR
3239         GL_Color(0, 0, 0, 0.5);
3240         GL_ColorMask(r_view.colormask[0], r_view.colormask[1], r_view.colormask[2], 1);
3241         qglDepthFunc(GL_ALWAYS);CHECKGLERROR
3242         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
3243         qglStencilMask(~0);CHECKGLERROR
3244         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
3245         qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
3246
3247         // apply the blend to the shadowed areas
3248         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
3249
3250         // restoring the perspective view is done by R_RenderScene
3251         //R_SetupView();
3252
3253         // restore other state to normal
3254         R_Shadow_RenderMode_End();
3255 }
3256
3257 void R_DrawCoronas(void)
3258 {
3259         int i, flag;
3260         float cscale, scale;
3261         size_t lightindex;
3262         dlight_t *light;
3263         rtlight_t *rtlight;
3264         if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
3265                 return;
3266         R_Mesh_Matrix(&identitymatrix);
3267         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3268         // FIXME: these traces should scan all render entities instead of cl.world
3269         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3270         {
3271                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3272                 if (!light)
3273                         continue;
3274                 rtlight = &light->rtlight;
3275                 if (!(rtlight->flags & flag))
3276                         continue;
3277                 if (rtlight->corona * r_coronas.value <= 0)
3278                         continue;
3279                 if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex)
3280                         continue;
3281                 cscale = rtlight->corona * r_coronas.value* 0.25f;
3282                 scale = rtlight->radius * rtlight->coronasizescale;
3283                 if (VectorDistance2(rtlight->shadoworigin, r_view.origin) < 16.0f * 16.0f)
3284                         continue;
3285                 if (CL_Move(r_view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3286                         continue;
3287                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_view.right, r_view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3288         }
3289         for (i = 0;i < r_refdef.numlights;i++)
3290         {
3291                 rtlight = &r_refdef.lights[i];
3292                 if (!(rtlight->flags & flag))
3293                         continue;
3294                 if (rtlight->corona <= 0)
3295                         continue;
3296                 if (VectorDistance2(rtlight->shadoworigin, r_view.origin) < 32.0f * 32.0f)
3297                         continue;
3298                 if (gl_flashblend.integer)
3299                 {
3300                         cscale = rtlight->corona * 1.0f;
3301                         scale = rtlight->radius * rtlight->coronasizescale * 2.0f;
3302                 }
3303                 else
3304                 {
3305                         cscale = rtlight->corona * r_coronas.value* 0.25f;
3306                         scale = rtlight->radius * rtlight->coronasizescale;
3307                 }
3308                 if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f))
3309                         continue;
3310                 if (CL_Move(r_view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3311                         continue;
3312                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_view.right, r_view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3313         }
3314 }
3315
3316
3317
3318 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
3319 typedef struct suffixinfo_s
3320 {
3321         char *suffix;
3322         qboolean flipx, flipy, flipdiagonal;
3323 }
3324 suffixinfo_t;
3325 static suffixinfo_t suffix[3][6] =
3326 {
3327         {
3328                 {"px",   false, false, false},
3329                 {"nx",   false, false, false},
3330                 {"py",   false, false, false},
3331                 {"ny",   false, false, false},
3332                 {"pz",   false, false, false},
3333                 {"nz",   false, false, false}
3334         },
3335         {
3336                 {"posx", false, false, false},
3337                 {"negx", false, false, false},
3338                 {"posy", false, false, false},
3339                 {"negy", false, false, false},
3340                 {"posz", false, false, false},
3341                 {"negz", false, false, false}
3342         },
3343         {
3344                 {"rt",    true, false,  true},
3345                 {"lf",   false,  true,  true},
3346                 {"ft",    true,  true, false},
3347                 {"bk",   false, false, false},
3348                 {"up",    true, false,  true},
3349                 {"dn",    true, false,  true}
3350         }
3351 };
3352
3353 static int componentorder[4] = {0, 1, 2, 3};
3354
3355 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
3356 {
3357         int i, j, cubemapsize;
3358         unsigned char *cubemappixels, *image_buffer;
3359         rtexture_t *cubemaptexture;
3360         char name[256];
3361         // must start 0 so the first loadimagepixels has no requested width/height
3362         cubemapsize = 0;
3363         cubemappixels = NULL;
3364         cubemaptexture = NULL;
3365         // keep trying different suffix groups (posx, px, rt) until one loads
3366         for (j = 0;j < 3 && !cubemappixels;j++)
3367         {
3368                 // load the 6 images in the suffix group
3369                 for (i = 0;i < 6;i++)
3370                 {
3371                         // generate an image name based on the base and and suffix
3372                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3373                         // load it
3374                         if ((image_buffer = loadimagepixelsbgra(name, false, false)))
3375                         {
3376                                 // an image loaded, make sure width and height are equal
3377                                 if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
3378                                 {
3379                                         // if this is the first image to load successfully, allocate the cubemap memory
3380                                         if (!cubemappixels && image_width >= 1)
3381                                         {
3382                                                 cubemapsize = image_width;
3383                                                 // note this clears to black, so unavailable sides are black
3384                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3385                                         }
3386                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3387                                         if (cubemappixels)
3388                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_buffer, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
3389                                 }
3390                                 else
3391                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3392                                 // free the image
3393                                 Mem_Free(image_buffer);
3394                         }
3395                 }
3396         }
3397         // if a cubemap loaded, upload it
3398         if (cubemappixels)
3399         {
3400                 if (!r_shadow_filters_texturepool)
3401                         r_shadow_filters_texturepool = R_AllocTexturePool();
3402                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
3403                 Mem_Free(cubemappixels);
3404         }
3405         else
3406         {
3407                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3408                 for (j = 0;j < 3;j++)
3409                         for (i = 0;i < 6;i++)
3410                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3411                 Con_Print(" and was unable to find any of them.\n");
3412         }
3413         return cubemaptexture;
3414 }
3415
3416 rtexture_t *R_Shadow_Cubemap(const char *basename)
3417 {
3418         int i;
3419         for (i = 0;i < numcubemaps;i++)
3420                 if (!strcasecmp(cubemaps[i].basename, basename))
3421                         return cubemaps[i].texture;
3422         if (i >= MAX_CUBEMAPS)
3423                 return r_texture_whitecube;
3424         numcubemaps++;
3425         strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
3426         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3427         if (!cubemaps[i].texture)
3428                 cubemaps[i].texture = r_texture_whitecube;
3429         return cubemaps[i].texture;
3430 }
3431
3432 void R_Shadow_FreeCubemaps(void)
3433 {
3434         numcubemaps = 0;
3435         R_FreeTexturePool(&r_shadow_filters_texturepool);
3436 }
3437
3438 dlight_t *R_Shadow_NewWorldLight(void)
3439 {
3440         return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
3441 }
3442
3443 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)
3444 {
3445         matrix4x4_t matrix;
3446         // validate parameters
3447         if (style < 0 || style >= MAX_LIGHTSTYLES)
3448         {
3449                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3450                 style = 0;
3451         }
3452         if (!cubemapname)
3453                 cubemapname = "";
3454
3455         // copy to light properties
3456         VectorCopy(origin, light->origin);
3457         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3458         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3459         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3460         light->color[0] = max(color[0], 0);
3461         light->color[1] = max(color[1], 0);
3462         light->color[2] = max(color[2], 0);
3463         light->radius = max(radius, 0);
3464         light->style = style;
3465         light->shadow = shadowenable;
3466         light->corona = corona;
3467         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3468         light->coronasizescale = coronasizescale;
3469         light->ambientscale = ambientscale;
3470         light->diffusescale = diffusescale;
3471         light->specularscale = specularscale;
3472         light->flags = flags;
3473
3474         // update renderable light data
3475         Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
3476         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);
3477 }
3478
3479 void R_Shadow_FreeWorldLight(dlight_t *light)
3480 {
3481         if (r_shadow_selectedlight == light)
3482                 r_shadow_selectedlight = NULL;
3483         R_RTLight_Uncompile(&light->rtlight);
3484         Mem_ExpandableArray_FreeRecord(&r_shadow_worldlightsarray, light);
3485 }
3486
3487 void R_Shadow_ClearWorldLights(void)
3488 {
3489         size_t lightindex;
3490         dlight_t *light;
3491         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3492         {
3493                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3494                 if (light)
3495                         R_Shadow_FreeWorldLight(light);
3496         }
3497         r_shadow_selectedlight = NULL;
3498         R_Shadow_FreeCubemaps();
3499 }
3500
3501 void R_Shadow_SelectLight(dlight_t *light)
3502 {
3503         if (r_shadow_selectedlight)
3504                 r_shadow_selectedlight->selected = false;
3505         r_shadow_selectedlight = light;
3506         if (r_shadow_selectedlight)
3507                 r_shadow_selectedlight->selected = true;
3508 }
3509
3510 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3511 {
3512         // this is never batched (there can be only one)
3513         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprcursor->tex, r_editlights_sprcursor->tex, false, false, r_editlights_cursorlocation, r_view.right, r_view.up, EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, EDLIGHTSPRSIZE, 1, 1, 1, 1);
3514 }
3515
3516 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3517 {
3518         float intensity;
3519         float s;
3520         vec3_t spritecolor;
3521         cachepic_t *pic;
3522
3523         // this is never batched (due to the ent parameter changing every time)
3524         // so numsurfaces == 1 and surfacelist[0] == lightnumber
3525         const dlight_t *light = (dlight_t *)ent;
3526         s = EDLIGHTSPRSIZE;
3527         intensity = 0.5f;
3528         VectorScale(light->color, intensity, spritecolor);
3529         if (VectorLength(spritecolor) < 0.1732f)
3530                 VectorSet(spritecolor, 0.1f, 0.1f, 0.1f);
3531         if (VectorLength(spritecolor) > 1.0f)
3532                 VectorNormalize(spritecolor);
3533
3534         // draw light sprite
3535         if (light->cubemapname[0] && !light->shadow)
3536                 pic = r_editlights_sprcubemapnoshadowlight;
3537         else if (light->cubemapname[0])
3538                 pic = r_editlights_sprcubemaplight;
3539         else if (!light->shadow)
3540                 pic = r_editlights_sprnoshadowlight;
3541         else
3542                 pic = r_editlights_sprlight;
3543         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, pic->tex, pic->tex, false, false, light->origin, r_view.right, r_view.up, s, -s, -s, s, spritecolor[0], spritecolor[1], spritecolor[2], 1);
3544         // draw selection sprite if light is selected
3545         if (light->selected)
3546                 R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprselection->tex, r_editlights_sprselection->tex, false, false, light->origin, r_view.right, r_view.up, s, -s, -s, s, 1, 1, 1, 1);
3547         // VorteX todo: add normalmode/realtime mode light overlay sprites?
3548 }
3549
3550 void R_Shadow_DrawLightSprites(void)
3551 {
3552         size_t lightindex;
3553         dlight_t *light;
3554         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3555         {
3556                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3557                 if (light)
3558                         R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
3559         }
3560         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
3561 }
3562
3563 void R_Shadow_SelectLightInView(void)
3564 {
3565         float bestrating, rating, temp[3];
3566         dlight_t *best;
3567         size_t lightindex;
3568         dlight_t *light;
3569         best = NULL;
3570         bestrating = 0;
3571         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3572         {
3573                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3574                 if (!light)
3575                         continue;
3576                 VectorSubtract(light->origin, r_view.origin, temp);
3577                 rating = (DotProduct(temp, r_view.forward) / sqrt(DotProduct(temp, temp)));
3578                 if (rating >= 0.95)
3579                 {
3580                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3581                         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)
3582                         {
3583                                 bestrating = rating;
3584                                 best = light;
3585                         }
3586                 }
3587         }
3588         R_Shadow_SelectLight(best);
3589 }
3590
3591 void R_Shadow_LoadWorldLights(void)
3592 {
3593         int n, a, style, shadow, flags;
3594         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3595         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3596         if (r_refdef.worldmodel == NULL)
3597         {
3598                 Con_Print("No map loaded.\n");
3599                 return;
3600         }
3601         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3602         strlcat (name, ".rtlights", sizeof (name));
3603         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3604         if (lightsstring)
3605         {
3606                 s = lightsstring;
3607                 n = 0;
3608                 while (*s)
3609                 {
3610                         t = s;
3611                         /*
3612                         shadow = true;
3613                         for (;COM_Parse(t, true) && strcmp(
3614                         if (COM_Parse(t, true))
3615                         {
3616                                 if (com_token[0] == '!')
3617                                 {
3618                                         shadow = false;
3619                                         origin[0] = atof(com_token+1);
3620                                 }
3621                                 else
3622                                         origin[0] = atof(com_token);
3623                                 if (Com_Parse(t
3624                         }
3625                         */
3626                         t = s;
3627                         while (*s && *s != '\n' && *s != '\r')
3628                                 s++;
3629                         if (!*s)
3630                                 break;
3631                         tempchar = *s;
3632                         shadow = true;
3633                         // check for modifier flags
3634                         if (*t == '!')
3635                         {
3636                                 shadow = false;
3637                                 t++;
3638                         }
3639                         *s = 0;
3640                         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);
3641                         *s = tempchar;
3642                         if (a < 18)
3643                                 flags = LIGHTFLAG_REALTIMEMODE;
3644                         if (a < 17)
3645                                 specularscale = 1;
3646                         if (a < 16)
3647                                 diffusescale = 1;
3648                         if (a < 15)
3649                                 ambientscale = 0;
3650                         if (a < 14)
3651                                 coronasizescale = 0.25f;
3652                         if (a < 13)
3653                                 VectorClear(angles);
3654                         if (a < 10)
3655                                 corona = 0;
3656                         if (a < 9 || !strcmp(cubemapname, "\"\""))
3657                                 cubemapname[0] = 0;
3658                         // remove quotes on cubemapname
3659                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3660                         {
3661                                 size_t namelen;
3662                                 namelen = strlen(cubemapname) - 2;
3663                                 memmove(cubemapname, cubemapname + 1, namelen);
3664                                 cubemapname[namelen] = '\0';
3665                         }
3666                         if (a < 8)
3667                         {
3668                                 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);
3669                                 break;
3670                         }
3671                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3672                         if (*s == '\r')
3673                                 s++;
3674                         if (*s == '\n')
3675                                 s++;
3676                         n++;
3677                 }
3678                 if (*s)
3679                         Con_Printf("invalid rtlights file \"%s\"\n", name);
3680                 Mem_Free(lightsstring);
3681         }
3682 }
3683
3684 void R_Shadow_SaveWorldLights(void)
3685 {
3686         size_t lightindex;
3687         dlight_t *light;
3688         size_t bufchars, bufmaxchars;
3689         char *buf, *oldbuf;
3690         char name[MAX_QPATH];
3691         char line[MAX_INPUTLINE];
3692         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
3693                 return;
3694         if (r_refdef.worldmodel == NULL)
3695         {
3696                 Con_Print("No map loaded.\n");
3697                 return;
3698         }
3699         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3700         strlcat (name, ".rtlights", sizeof (name));
3701         bufchars = bufmaxchars = 0;
3702         buf = NULL;
3703         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3704         {
3705                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3706                 if (!light)
3707                         continue;
3708                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3709                         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);
3710                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3711                         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]);
3712                 else
3713                         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);
3714                 if (bufchars + strlen(line) > bufmaxchars)
3715                 {
3716                         bufmaxchars = bufchars + strlen(line) + 2048;
3717                         oldbuf = buf;
3718                         buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3719                         if (oldbuf)
3720                         {
3721                                 if (bufchars)
3722                                         memcpy(buf, oldbuf, bufchars);
3723                                 Mem_Free(oldbuf);
3724                         }
3725                 }
3726                 if (strlen(line))
3727                 {
3728                         memcpy(buf + bufchars, line, strlen(line));
3729                         bufchars += strlen(line);
3730                 }
3731         }
3732         if (bufchars)
3733                 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3734         if (buf)
3735                 Mem_Free(buf);
3736 }
3737
3738 void R_Shadow_LoadLightsFile(void)
3739 {
3740         int n, a, style;
3741         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3742         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3743         if (r_refdef.worldmodel == NULL)
3744         {
3745                 Con_Print("No map loaded.\n");
3746                 return;
3747         }
3748         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3749         strlcat (name, ".lights", sizeof (name));
3750         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3751         if (lightsstring)
3752         {
3753                 s = lightsstring;
3754                 n = 0;
3755                 while (*s)
3756                 {
3757                         t = s;
3758                         while (*s && *s != '\n' && *s != '\r')
3759                                 s++;
3760                         if (!*s)
3761                                 break;
3762                         tempchar = *s;
3763                         *s = 0;
3764                         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);
3765                         *s = tempchar;
3766                         if (a < 14)
3767                         {
3768                                 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);
3769                                 break;
3770                         }
3771                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3772                         radius = bound(15, radius, 4096);
3773                         VectorScale(color, (2.0f / (8388608.0f)), color);
3774                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3775                         if (*s == '\r')
3776                                 s++;
3777                         if (*s == '\n')
3778                                 s++;
3779                         n++;
3780                 }
3781                 if (*s)
3782                         Con_Printf("invalid lights file \"%s\"\n", name);
3783                 Mem_Free(lightsstring);
3784         }
3785 }
3786
3787 // tyrlite/hmap2 light types in the delay field
3788 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3789
3790 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3791 {
3792         int entnum, style, islight, skin, pflags, effects, type, n;
3793         char *entfiledata;
3794         const char *data;
3795         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3796         char key[256], value[MAX_INPUTLINE];
3797
3798         if (r_refdef.worldmodel == NULL)
3799         {
3800                 Con_Print("No map loaded.\n");
3801                 return;
3802         }
3803         // try to load a .ent file first
3804         FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3805         strlcat (key, ".ent", sizeof (key));
3806         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3807         // and if that is not found, fall back to the bsp file entity string
3808         if (!data)
3809                 data = r_refdef.worldmodel->brush.entities;
3810         if (!data)
3811                 return;
3812         for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
3813         {
3814                 type = LIGHTTYPE_MINUSX;
3815                 origin[0] = origin[1] = origin[2] = 0;
3816                 originhack[0] = originhack[1] = originhack[2] = 0;
3817                 angles[0] = angles[1] = angles[2] = 0;
3818                 color[0] = color[1] = color[2] = 1;
3819                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3820                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3821                 fadescale = 1;
3822                 lightscale = 1;
3823                 style = 0;
3824                 skin = 0;
3825                 pflags = 0;
3826                 effects = 0;
3827                 islight = false;
3828                 while (1)
3829                 {
3830                         if (!COM_ParseToken_Simple(&data, false, false))
3831                                 break; // error
3832                         if (com_token[0] == '}')
3833                                 break; // end of entity
3834                         if (com_token[0] == '_')
3835                                 strlcpy(key, com_token + 1, sizeof(key));
3836                         else
3837                                 strlcpy(key, com_token, sizeof(key));
3838                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3839                                 key[strlen(key)-1] = 0;
3840                         if (!COM_ParseToken_Simple(&data, false, false))
3841                                 break; // error
3842                         strlcpy(value, com_token, sizeof(value));
3843
3844                         // now that we have the key pair worked out...
3845                         if (!strcmp("light", key))
3846                         {
3847                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3848                                 if (n == 1)
3849                                 {
3850                                         // quake
3851                                         light[0] = vec[0] * (1.0f / 256.0f);
3852                                         light[1] = vec[0] * (1.0f / 256.0f);
3853                                         light[2] = vec[0] * (1.0f / 256.0f);
3854                                         light[3] = vec[0];
3855                                 }
3856                                 else if (n == 4)
3857                                 {
3858                                         // halflife
3859                                         light[0] = vec[0] * (1.0f / 255.0f);
3860                                         light[1] = vec[1] * (1.0f / 255.0f);
3861                                         light[2] = vec[2] * (1.0f / 255.0f);
3862                                         light[3] = vec[3];
3863                                 }
3864                         }
3865                         else if (!strcmp("delay", key))
3866                                 type = atoi(value);
3867                         else if (!strcmp("origin", key))
3868                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3869                         else if (!strcmp("angle", key))
3870                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3871                         else if (!strcmp("angles", key))
3872                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3873                         else if (!strcmp("color", key))
3874                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3875                         else if (!strcmp("wait", key))
3876                                 fadescale = atof(value);
3877                         else if (!strcmp("classname", key))
3878                         {
3879                                 if (!strncmp(value, "light", 5))
3880                                 {
3881                                         islight = true;
3882                                         if (!strcmp(value, "light_fluoro"))
3883                                         {
3884                                                 originhack[0] = 0;
3885                                                 originhack[1] = 0;
3886                                                 originhack[2] = 0;
3887                                                 overridecolor[0] = 1;
3888                                                 overridecolor[1] = 1;
3889                                                 overridecolor[2] = 1;
3890                                         }
3891                                         if (!strcmp(value, "light_fluorospark"))
3892                                         {
3893                                                 originhack[0] = 0;
3894                                                 originhack[1] = 0;
3895                                                 originhack[2] = 0;
3896                                                 overridecolor[0] = 1;
3897                                                 overridecolor[1] = 1;
3898                                                 overridecolor[2] = 1;
3899                                         }
3900                                         if (!strcmp(value, "light_globe"))
3901                                         {
3902                                                 originhack[0] = 0;
3903                                                 originhack[1] = 0;
3904                                                 originhack[2] = 0;
3905                                                 overridecolor[0] = 1;
3906                                                 overridecolor[1] = 0.8;
3907                                                 overridecolor[2] = 0.4;
3908                                         }
3909                                         if (!strcmp(value, "light_flame_large_yellow"))
3910                                         {
3911                                                 originhack[0] = 0;
3912                                                 originhack[1] = 0;
3913                                                 originhack[2] = 0;
3914                                                 overridecolor[0] = 1;
3915                                                 overridecolor[1] = 0.5;
3916                                                 overridecolor[2] = 0.1;
3917                                         }
3918                                         if (!strcmp(value, "light_flame_small_yellow"))
3919                                         {
3920                                                 originhack[0] = 0;
3921                                                 originhack[1] = 0;
3922                                                 originhack[2] = 0;
3923                                                 overridecolor[0] = 1;
3924                                                 overridecolor[1] = 0.5;
3925                                                 overridecolor[2] = 0.1;
3926                                         }
3927                                         if (!strcmp(value, "light_torch_small_white"))
3928                                         {
3929                                                 originhack[0] = 0;
3930                                                 originhack[1] = 0;
3931                                                 originhack[2] = 0;
3932                                                 overridecolor[0] = 1;
3933                                                 overridecolor[1] = 0.5;
3934                                                 overridecolor[2] = 0.1;
3935                                         }
3936                                         if (!strcmp(value, "light_torch_small_walltorch"))
3937                                         {
3938                                                 originhack[0] = 0;
3939                                                 originhack[1] = 0;
3940                                                 originhack[2] = 0;
3941                                                 overridecolor[0] = 1;
3942                                                 overridecolor[1] = 0.5;
3943                                                 overridecolor[2] = 0.1;
3944                                         }
3945                                 }
3946                         }
3947                         else if (!strcmp("style", key))
3948                                 style = atoi(value);
3949                         else if (!strcmp("skin", key))
3950                                 skin = (int)atof(value);
3951                         else if (!strcmp("pflags", key))
3952                                 pflags = (int)atof(value);
3953                         else if (!strcmp("effects", key))
3954                                 effects = (int)atof(value);
3955                         else if (r_refdef.worldmodel->type == mod_brushq3)
3956                         {
3957                                 if (!strcmp("scale", key))
3958                                         lightscale = atof(value);
3959                                 if (!strcmp("fade", key))
3960                                         fadescale = atof(value);
3961                         }
3962                 }
3963                 if (!islight)
3964                         continue;
3965                 if (lightscale <= 0)
3966                         lightscale = 1;
3967                 if (fadescale <= 0)
3968                         fadescale = 1;
3969                 if (color[0] == color[1] && color[0] == color[2])
3970                 {
3971                         color[0] *= overridecolor[0];
3972                         color[1] *= overridecolor[1];
3973                         color[2] *= overridecolor[2];
3974                 }
3975                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3976                 color[0] = color[0] * light[0];
3977                 color[1] = color[1] * light[1];
3978                 color[2] = color[2] * light[2];
3979                 switch (type)
3980                 {
3981                 case LIGHTTYPE_MINUSX:
3982                         break;
3983                 case LIGHTTYPE_RECIPX:
3984                         radius *= 2;
3985                         VectorScale(color, (1.0f / 16.0f), color);
3986                         break;
3987                 case LIGHTTYPE_RECIPXX:
3988                         radius *= 2;
3989                         VectorScale(color, (1.0f / 16.0f), color);
3990                         break;
3991                 default:
3992                 case LIGHTTYPE_NONE:
3993                         break;
3994                 case LIGHTTYPE_SUN:
3995                         break;
3996                 case LIGHTTYPE_MINUSXX:
3997                         break;
3998                 }
3999                 VectorAdd(origin, originhack, origin);
4000                 if (radius >= 1)
4001                         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);
4002         }
4003         if (entfiledata)
4004                 Mem_Free(entfiledata);
4005 }
4006
4007
4008 void R_Shadow_SetCursorLocationForView(void)
4009 {
4010         vec_t dist, push;
4011         vec3_t dest, endpos;
4012         trace_t trace;
4013         VectorMA(r_view.origin, r_editlights_cursordistance.value, r_view.forward, dest);
4014         trace = CL_Move(r_view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
4015         if (trace.fraction < 1)
4016         {
4017                 dist = trace.fraction * r_editlights_cursordistance.value;
4018                 push = r_editlights_cursorpushback.value;
4019                 if (push > dist)
4020                         push = dist;
4021                 push = -push;
4022                 VectorMA(trace.endpos, push, r_view.forward, endpos);
4023                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
4024         }
4025         else
4026         {
4027                 VectorClear( endpos );
4028         }
4029         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4030         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4031         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4032 }
4033
4034 void R_Shadow_UpdateWorldLightSelection(void)
4035 {
4036         if (r_editlights.integer)
4037         {
4038                 R_Shadow_SetCursorLocationForView();
4039                 R_Shadow_SelectLightInView();
4040         }
4041         else
4042                 R_Shadow_SelectLight(NULL);
4043 }
4044
4045 void R_Shadow_EditLights_Clear_f(void)
4046 {
4047         R_Shadow_ClearWorldLights();
4048 }
4049
4050 void R_Shadow_EditLights_Reload_f(void)
4051 {
4052         if (!r_refdef.worldmodel)
4053                 return;
4054         strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
4055         R_Shadow_ClearWorldLights();
4056         R_Shadow_LoadWorldLights();
4057         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4058         {
4059                 R_Shadow_LoadLightsFile();
4060                 if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4061                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4062         }
4063 }
4064
4065 void R_Shadow_EditLights_Save_f(void)
4066 {
4067         if (!r_refdef.worldmodel)
4068                 return;
4069         R_Shadow_SaveWorldLights();
4070 }
4071
4072 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
4073 {
4074         R_Shadow_ClearWorldLights();
4075         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4076 }
4077
4078 void R_Shadow_EditLights_ImportLightsFile_f(void)
4079 {
4080         R_Shadow_ClearWorldLights();
4081         R_Shadow_LoadLightsFile();
4082 }
4083
4084 void R_Shadow_EditLights_Spawn_f(void)
4085 {
4086         vec3_t color;
4087         if (!r_editlights.integer)
4088         {
4089                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4090                 return;
4091         }
4092         if (Cmd_Argc() != 1)
4093         {
4094                 Con_Print("r_editlights_spawn does not take parameters\n");
4095                 return;
4096         }
4097         color[0] = color[1] = color[2] = 1;
4098         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
4099 }
4100
4101 void R_Shadow_EditLights_Edit_f(void)
4102 {
4103         vec3_t origin, angles, color;
4104         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
4105         int style, shadows, flags, normalmode, realtimemode;
4106         char cubemapname[MAX_INPUTLINE];
4107         if (!r_editlights.integer)
4108         {
4109                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4110                 return;
4111         }
4112         if (!r_shadow_selectedlight)
4113         {
4114                 Con_Print("No selected light.\n");
4115                 return;
4116         }
4117         VectorCopy(r_shadow_selectedlight->origin, origin);
4118         VectorCopy(r_shadow_selectedlight->angles, angles);
4119         VectorCopy(r_shadow_selectedlight->color, color);
4120         radius = r_shadow_selectedlight->radius;
4121         style = r_shadow_selectedlight->style;
4122         if (r_shadow_selectedlight->cubemapname)
4123                 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
4124         else
4125                 cubemapname[0] = 0;
4126         shadows = r_shadow_selectedlight->shadow;
4127         corona = r_shadow_selectedlight->corona;
4128         coronasizescale = r_shadow_selectedlight->coronasizescale;
4129         ambientscale = r_shadow_selectedlight->ambientscale;
4130         diffusescale = r_shadow_selectedlight->diffusescale;
4131         specularscale = r_shadow_selectedlight->specularscale;
4132         flags = r_shadow_selectedlight->flags;
4133         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
4134         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
4135         if (!strcmp(Cmd_Argv(1), "origin"))
4136         {
4137                 if (Cmd_Argc() != 5)
4138                 {
4139                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4140                         return;
4141                 }
4142                 origin[0] = atof(Cmd_Argv(2));
4143                 origin[1] = atof(Cmd_Argv(3));
4144                 origin[2] = atof(Cmd_Argv(4));
4145         }
4146         else if (!strcmp(Cmd_Argv(1), "originx"))
4147         {
4148                 if (Cmd_Argc() != 3)
4149                 {
4150                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4151                         return;
4152                 }
4153                 origin[0] = atof(Cmd_Argv(2));
4154         }
4155         else if (!strcmp(Cmd_Argv(1), "originy"))
4156         {
4157                 if (Cmd_Argc() != 3)
4158                 {
4159                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4160                         return;
4161                 }
4162                 origin[1] = atof(Cmd_Argv(2));
4163         }
4164         else if (!strcmp(Cmd_Argv(1), "originz"))
4165         {
4166                 if (Cmd_Argc() != 3)
4167                 {
4168                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4169                         return;
4170                 }
4171                 origin[2] = atof(Cmd_Argv(2));
4172         }
4173         else if (!strcmp(Cmd_Argv(1), "move"))
4174         {
4175                 if (Cmd_Argc() != 5)
4176                 {
4177                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4178                         return;
4179                 }
4180                 origin[0] += atof(Cmd_Argv(2));
4181                 origin[1] += atof(Cmd_Argv(3));
4182                 origin[2] += atof(Cmd_Argv(4));
4183         }
4184         else if (!strcmp(Cmd_Argv(1), "movex"))
4185         {
4186                 if (Cmd_Argc() != 3)
4187                 {
4188                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4189                         return;
4190                 }
4191                 origin[0] += atof(Cmd_Argv(2));
4192         }
4193         else if (!strcmp(Cmd_Argv(1), "movey"))
4194         {
4195                 if (Cmd_Argc() != 3)
4196                 {
4197                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4198                         return;
4199                 }
4200                 origin[1] += atof(Cmd_Argv(2));
4201         }
4202         else if (!strcmp(Cmd_Argv(1), "movez"))
4203         {
4204                 if (Cmd_Argc() != 3)
4205                 {
4206                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4207                         return;
4208                 }
4209                 origin[2] += atof(Cmd_Argv(2));
4210         }
4211         else if (!strcmp(Cmd_Argv(1), "angles"))
4212         {
4213                 if (Cmd_Argc() != 5)
4214                 {
4215                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4216                         return;
4217                 }
4218                 angles[0] = atof(Cmd_Argv(2));
4219                 angles[1] = atof(Cmd_Argv(3));
4220                 angles[2] = atof(Cmd_Argv(4));
4221         }
4222         else if (!strcmp(Cmd_Argv(1), "anglesx"))
4223         {
4224                 if (Cmd_Argc() != 3)
4225                 {
4226                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4227                         return;
4228                 }
4229                 angles[0] = atof(Cmd_Argv(2));
4230         }
4231         else if (!strcmp(Cmd_Argv(1), "anglesy"))
4232         {
4233                 if (Cmd_Argc() != 3)
4234                 {
4235                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4236                         return;
4237                 }
4238                 angles[1] = atof(Cmd_Argv(2));
4239         }
4240         else if (!strcmp(Cmd_Argv(1), "anglesz"))
4241         {
4242                 if (Cmd_Argc() != 3)
4243                 {
4244                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4245                         return;
4246                 }
4247                 angles[2] = atof(Cmd_Argv(2));
4248         }
4249         else if (!strcmp(Cmd_Argv(1), "color"))
4250         {
4251                 if (Cmd_Argc() != 5)
4252                 {
4253                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
4254                         return;
4255                 }
4256                 color[0] = atof(Cmd_Argv(2));
4257                 color[1] = atof(Cmd_Argv(3));
4258                 color[2] = atof(Cmd_Argv(4));
4259         }
4260         else if (!strcmp(Cmd_Argv(1), "radius"))
4261         {
4262                 if (Cmd_Argc() != 3)
4263                 {
4264                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4265                         return;
4266                 }
4267                 radius = atof(Cmd_Argv(2));
4268         }
4269         else if (!strcmp(Cmd_Argv(1), "colorscale"))
4270         {
4271                 if (Cmd_Argc() == 3)
4272                 {
4273                         double scale = atof(Cmd_Argv(2));
4274                         color[0] *= scale;
4275                         color[1] *= scale;
4276                         color[2] *= scale;
4277                 }
4278                 else
4279                 {
4280                         if (Cmd_Argc() != 5)
4281                         {
4282                                 Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
4283                                 return;
4284                         }
4285                         color[0] *= atof(Cmd_Argv(2));
4286                         color[1] *= atof(Cmd_Argv(3));
4287                         color[2] *= atof(Cmd_Argv(4));
4288                 }
4289         }
4290         else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
4291         {
4292                 if (Cmd_Argc() != 3)
4293                 {
4294                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4295                         return;
4296                 }
4297                 radius *= atof(Cmd_Argv(2));
4298         }
4299         else if (!strcmp(Cmd_Argv(1), "style"))
4300         {
4301                 if (Cmd_Argc() != 3)
4302                 {
4303                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4304                         return;
4305                 }
4306                 style = atoi(Cmd_Argv(2));
4307         }
4308         else if (!strcmp(Cmd_Argv(1), "cubemap"))
4309         {
4310                 if (Cmd_Argc() > 3)
4311                 {
4312                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4313                         return;
4314                 }
4315                 if (Cmd_Argc() == 3)
4316                         strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
4317                 else
4318                         cubemapname[0] = 0;
4319         }
4320         else if (!strcmp(Cmd_Argv(1), "shadows"))
4321         {
4322                 if (Cmd_Argc() != 3)
4323                 {
4324                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4325                         return;
4326                 }
4327                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4328         }
4329         else if (!strcmp(Cmd_Argv(1), "corona"))
4330         {
4331                 if (Cmd_Argc() != 3)
4332                 {
4333                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4334                         return;
4335                 }
4336                 corona = atof(Cmd_Argv(2));
4337         }
4338         else if (!strcmp(Cmd_Argv(1), "coronasize"))
4339         {
4340                 if (Cmd_Argc() != 3)
4341                 {
4342                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4343                         return;
4344                 }
4345                 coronasizescale = atof(Cmd_Argv(2));
4346         }
4347         else if (!strcmp(Cmd_Argv(1), "ambient"))
4348         {
4349                 if (Cmd_Argc() != 3)
4350                 {
4351                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4352                         return;
4353                 }
4354                 ambientscale = atof(Cmd_Argv(2));
4355         }
4356         else if (!strcmp(Cmd_Argv(1), "diffuse"))
4357         {
4358                 if (Cmd_Argc() != 3)
4359                 {
4360                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4361                         return;
4362                 }
4363                 diffusescale = atof(Cmd_Argv(2));
4364         }
4365         else if (!strcmp(Cmd_Argv(1), "specular"))
4366         {
4367                 if (Cmd_Argc() != 3)
4368                 {
4369                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4370                         return;
4371                 }
4372                 specularscale = atof(Cmd_Argv(2));
4373         }
4374         else if (!strcmp(Cmd_Argv(1), "normalmode"))
4375         {
4376                 if (Cmd_Argc() != 3)
4377                 {
4378                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4379                         return;
4380                 }
4381                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4382         }
4383         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
4384         {
4385                 if (Cmd_Argc() != 3)
4386                 {
4387                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4388                         return;
4389                 }
4390                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4391         }
4392         else
4393         {
4394                 Con_Print("usage: r_editlights_edit [property] [value]\n");
4395                 Con_Print("Selected light's properties:\n");
4396                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4397                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4398                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4399                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
4400                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
4401                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
4402                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4403                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
4404                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
4405                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
4406                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
4407                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
4408                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4409                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4410                 return;
4411         }
4412         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4413         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4414 }
4415
4416 void R_Shadow_EditLights_EditAll_f(void)
4417 {
4418         size_t lightindex;
4419         dlight_t *light;
4420
4421         if (!r_editlights.integer)
4422         {
4423                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4424                 return;
4425         }
4426
4427         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4428         {
4429                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4430                 if (!light)
4431                         continue;
4432                 R_Shadow_SelectLight(light);
4433                 R_Shadow_EditLights_Edit_f();
4434         }
4435 }
4436
4437 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4438 {
4439         int lightnumber, lightcount;
4440         size_t lightindex;
4441         dlight_t *light;
4442         float x, y;
4443         char temp[256];
4444         if (!r_editlights.integer)
4445                 return;
4446         x = vid_conwidth.value - 240;
4447         y = 5;
4448         DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
4449         lightnumber = -1;
4450         lightcount = 0;
4451         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4452         {
4453                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4454                 if (!light)
4455                         continue;
4456                 if (light == r_shadow_selectedlight)
4457                         lightnumber = lightindex;
4458                 lightcount++;
4459         }
4460         sprintf(temp, "Cursor origin: %.0f %.0f %.0f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4461         sprintf(temp, "Total lights : %i active (%i total)", lightcount, (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4462         y += 8;
4463         if (r_shadow_selectedlight == NULL)
4464                 return;
4465         sprintf(temp, "Light #%i properties:", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4466         sprintf(temp, "Origin       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4467         sprintf(temp, "Angles       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4468         sprintf(temp, "Color        : %.2f %.2f %.2f\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, NULL, true);y += 8;
4469         sprintf(temp, "Radius       : %.0f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4470         sprintf(temp, "Corona       : %.0f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4471         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4472         sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4473         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4474         sprintf(temp, "CoronaSize   : %.2f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4475         sprintf(temp, "Ambient      : %.2f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4476         sprintf(temp, "Diffuse      : %.2f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4477         sprintf(temp, "Specular     : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4478         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, NULL, true);y += 8;
4479         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, NULL, true);y += 8;
4480 }
4481
4482 void R_Shadow_EditLights_ToggleShadow_f(void)
4483 {
4484         if (!r_editlights.integer)
4485         {
4486                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4487                 return;
4488         }
4489         if (!r_shadow_selectedlight)
4490         {
4491                 Con_Print("No selected light.\n");
4492                 return;
4493         }
4494         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);
4495 }
4496
4497 void R_Shadow_EditLights_ToggleCorona_f(void)
4498 {
4499         if (!r_editlights.integer)
4500         {
4501                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4502                 return;
4503         }
4504         if (!r_shadow_selectedlight)
4505         {
4506                 Con_Print("No selected light.\n");
4507                 return;
4508         }
4509         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);
4510 }
4511
4512 void R_Shadow_EditLights_Remove_f(void)
4513 {
4514         if (!r_editlights.integer)
4515         {
4516                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
4517                 return;
4518         }
4519         if (!r_shadow_selectedlight)
4520         {
4521                 Con_Print("No selected light.\n");
4522                 return;
4523         }
4524         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4525         r_shadow_selectedlight = NULL;
4526 }
4527
4528 void R_Shadow_EditLights_Help_f(void)
4529 {
4530         Con_Print(
4531 "Documentation on r_editlights system:\n"
4532 "Settings:\n"
4533 "r_editlights : enable/disable editing mode\n"
4534 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4535 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4536 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4537 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4538 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4539 "Commands:\n"
4540 "r_editlights_help : this help\n"
4541 "r_editlights_clear : remove all lights\n"
4542 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4543 "r_editlights_save : save to .rtlights file\n"
4544 "r_editlights_spawn : create a light with default settings\n"
4545 "r_editlights_edit command : edit selected light - more documentation below\n"
4546 "r_editlights_remove : remove selected light\n"
4547 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4548 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4549 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4550 "Edit commands:\n"
4551 "origin x y z : set light location\n"
4552 "originx x: set x component of light location\n"
4553 "originy y: set y component of light location\n"
4554 "originz z: set z component of light location\n"
4555 "move x y z : adjust light location\n"
4556 "movex x: adjust x component of light location\n"
4557 "movey y: adjust y component of light location\n"
4558 "movez z: adjust z component of light location\n"
4559 "angles x y z : set light angles\n"
4560 "anglesx x: set x component of light angles\n"
4561 "anglesy y: set y component of light angles\n"
4562 "anglesz z: set z component of light angles\n"
4563 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4564 "radius radius : set radius (size) of light\n"
4565 "colorscale grey : multiply color of light (1 does nothing)\n"
4566 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
4567 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
4568 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
4569 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4570 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4571 "shadows 1/0 : turn on/off shadows\n"
4572 "corona n : set corona intensity\n"
4573 "coronasize n : set corona size (0-1)\n"
4574 "ambient n : set ambient intensity (0-1)\n"
4575 "diffuse n : set diffuse intensity (0-1)\n"
4576 "specular n : set specular intensity (0-1)\n"
4577 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4578 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4579 "<nothing> : print light properties to console\n"
4580         );
4581 }
4582
4583 void R_Shadow_EditLights_CopyInfo_f(void)
4584 {
4585         if (!r_editlights.integer)
4586         {
4587                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
4588                 return;
4589         }
4590         if (!r_shadow_selectedlight)
4591         {
4592                 Con_Print("No selected light.\n");
4593                 return;
4594         }
4595         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4596         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4597         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4598         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4599         if (r_shadow_selectedlight->cubemapname)
4600                 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
4601         else
4602                 r_shadow_bufferlight.cubemapname[0] = 0;
4603         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4604         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4605         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4606         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4607         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4608         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4609         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4610 }
4611
4612 void R_Shadow_EditLights_PasteInfo_f(void)
4613 {
4614         if (!r_editlights.integer)
4615         {
4616                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
4617                 return;
4618         }
4619         if (!r_shadow_selectedlight)
4620         {
4621                 Con_Print("No selected light.\n");
4622                 return;
4623         }
4624         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);
4625 }
4626
4627 void R_Shadow_EditLights_Init(void)
4628 {
4629         Cvar_RegisterVariable(&r_editlights);
4630         Cvar_RegisterVariable(&r_editlights_cursordistance);
4631         Cvar_RegisterVariable(&r_editlights_cursorpushback);
4632         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4633         Cvar_RegisterVariable(&r_editlights_cursorgrid);
4634         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4635         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
4636         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
4637         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)");
4638         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
4639         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
4640         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
4641         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)");
4642         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
4643         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
4644         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
4645         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
4646         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
4647         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
4648         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)");
4649 }
4650
4651
4652
4653 /*
4654 =============================================================================
4655
4656 LIGHT SAMPLING
4657
4658 =============================================================================
4659 */
4660
4661 void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic)
4662 {
4663         VectorClear(diffusecolor);
4664         VectorClear(diffusenormal);
4665
4666         if (!r_fullbright.integer && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint)
4667         {
4668                 ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_ambient.value * (2.0f / 128.0f);
4669                 r_refdef.worldmodel->brush.LightPoint(r_refdef.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
4670         }
4671         else
4672                 VectorSet(ambientcolor, 1, 1, 1);
4673
4674         if (dynamic)
4675         {
4676                 int i;
4677                 float f, v[3];
4678                 rtlight_t *light;
4679                 for (i = 0;i < r_refdef.numlights;i++)
4680                 {
4681                         light = &r_refdef.lights[i];
4682                         Matrix4x4_Transform(&light->matrix_worldtolight, p, v);
4683                         f = 1 - VectorLength2(v);
4684                         if (f > 0 && CL_Move(p, vec3_origin, vec3_origin, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
4685                                 VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
4686                 }
4687         }
4688 }