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