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