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