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