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