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