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