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