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