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