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