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