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