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