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