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