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