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