]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
implemented r_glsl_usegeneric cvar, this causes generic GLSL shaders to
[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 int r_shadow_buffer_numshadowtrispvsbytes;
189 unsigned char *r_shadow_buffer_shadowtrispvs;
190 int r_shadow_buffer_numlighttrispvsbytes;
191 unsigned char *r_shadow_buffer_lighttrispvs;
192
193 rtexturepool_t *r_shadow_texturepool;
194 rtexture_t *r_shadow_attenuationgradienttexture;
195 rtexture_t *r_shadow_attenuation2dtexture;
196 rtexture_t *r_shadow_attenuation3dtexture;
197 rtexture_t *r_shadow_lightcorona;
198
199 // lights are reloaded when this changes
200 char r_shadow_mapname[MAX_QPATH];
201
202 // used only for light filters (cubemaps)
203 rtexturepool_t *r_shadow_filters_texturepool;
204
205 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"};
206 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"};
207 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
208 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
209 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)"};
210 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"};
211 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
212 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
213 cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
214 cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
215 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
216 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
217 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
218 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
219 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)"};
220 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
221 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
222 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
223 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
224 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)"};
225 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"};
226 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
227 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
228 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"};
229 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
230 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
231 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)"};
232 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
233 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
234 cvar_t r_shadow_polygonoffset = {0, "r_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)"};
235 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)"};
236 cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
237 cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
238 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
239 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
240 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
241 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
242 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
243 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
244 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
245 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
246
247 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
248 #define ATTENTABLESIZE 256
249 // 1D gradient, 2D circle and 3D sphere attenuation textures
250 #define ATTEN1DSIZE 32
251 #define ATTEN2DSIZE 64
252 #define ATTEN3DSIZE 32
253
254 static float r_shadow_attendividebias; // r_shadow_lightattenuationdividebias
255 static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
256 static float r_shadow_attentable[ATTENTABLESIZE+1];
257
258 rtlight_t *r_shadow_compilingrtlight;
259 static memexpandablearray_t r_shadow_worldlightsarray;
260 dlight_t *r_shadow_selectedlight;
261 dlight_t r_shadow_bufferlight;
262 vec3_t r_editlights_cursorlocation;
263
264 extern int con_vislines;
265
266 typedef struct cubemapinfo_s
267 {
268         char basename[64];
269         rtexture_t *texture;
270 }
271 cubemapinfo_t;
272
273 #define MAX_CUBEMAPS 256
274 static int numcubemaps;
275 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
276
277 void R_Shadow_UncompileWorldLights(void);
278 void R_Shadow_ClearWorldLights(void);
279 void R_Shadow_SaveWorldLights(void);
280 void R_Shadow_LoadWorldLights(void);
281 void R_Shadow_LoadLightsFile(void);
282 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
283 void R_Shadow_EditLights_Reload_f(void);
284 void R_Shadow_ValidateCvars(void);
285 static void R_Shadow_MakeTextures(void);
286
287 // VorteX: custom editor light sprites
288 #define EDLIGHTSPRSIZE                  8
289 cachepic_t *r_editlights_sprcursor;
290 cachepic_t *r_editlights_sprlight;
291 cachepic_t *r_editlights_sprnoshadowlight;
292 cachepic_t *r_editlights_sprcubemaplight;
293 cachepic_t *r_editlights_sprcubemapnoshadowlight;
294 cachepic_t *r_editlights_sprselection;
295
296 void r_shadow_start(void)
297 {
298         // allocate vertex processing arrays
299         numcubemaps = 0;
300         r_shadow_attenuationgradienttexture = NULL;
301         r_shadow_attenuation2dtexture = NULL;
302         r_shadow_attenuation3dtexture = NULL;
303         r_shadow_texturepool = NULL;
304         r_shadow_filters_texturepool = NULL;
305         R_Shadow_ValidateCvars();
306         R_Shadow_MakeTextures();
307         maxshadowtriangles = 0;
308         shadowelements = NULL;
309         maxshadowvertices = 0;
310         shadowvertex3f = NULL;
311         maxvertexupdate = 0;
312         vertexupdate = NULL;
313         vertexremap = NULL;
314         vertexupdatenum = 0;
315         maxshadowmark = 0;
316         numshadowmark = 0;
317         shadowmark = NULL;
318         shadowmarklist = NULL;
319         shadowmarkcount = 0;
320         r_shadow_buffer_numleafpvsbytes = 0;
321         r_shadow_buffer_leafpvs = NULL;
322         r_shadow_buffer_leaflist = NULL;
323         r_shadow_buffer_numsurfacepvsbytes = 0;
324         r_shadow_buffer_surfacepvs = NULL;
325         r_shadow_buffer_surfacelist = NULL;
326         r_shadow_buffer_numshadowtrispvsbytes = 0;
327         r_shadow_buffer_shadowtrispvs = NULL;
328         r_shadow_buffer_numlighttrispvsbytes = 0;
329         r_shadow_buffer_lighttrispvs = NULL;
330 }
331
332 void r_shadow_shutdown(void)
333 {
334         R_Shadow_UncompileWorldLights();
335         numcubemaps = 0;
336         r_shadow_attenuationgradienttexture = NULL;
337         r_shadow_attenuation2dtexture = NULL;
338         r_shadow_attenuation3dtexture = NULL;
339         R_FreeTexturePool(&r_shadow_texturepool);
340         R_FreeTexturePool(&r_shadow_filters_texturepool);
341         maxshadowtriangles = 0;
342         if (shadowelements)
343                 Mem_Free(shadowelements);
344         shadowelements = NULL;
345         if (shadowvertex3f)
346                 Mem_Free(shadowvertex3f);
347         shadowvertex3f = NULL;
348         maxvertexupdate = 0;
349         if (vertexupdate)
350                 Mem_Free(vertexupdate);
351         vertexupdate = NULL;
352         if (vertexremap)
353                 Mem_Free(vertexremap);
354         vertexremap = NULL;
355         vertexupdatenum = 0;
356         maxshadowmark = 0;
357         numshadowmark = 0;
358         if (shadowmark)
359                 Mem_Free(shadowmark);
360         shadowmark = NULL;
361         if (shadowmarklist)
362                 Mem_Free(shadowmarklist);
363         shadowmarklist = NULL;
364         shadowmarkcount = 0;
365         r_shadow_buffer_numleafpvsbytes = 0;
366         if (r_shadow_buffer_leafpvs)
367                 Mem_Free(r_shadow_buffer_leafpvs);
368         r_shadow_buffer_leafpvs = NULL;
369         if (r_shadow_buffer_leaflist)
370                 Mem_Free(r_shadow_buffer_leaflist);
371         r_shadow_buffer_leaflist = NULL;
372         r_shadow_buffer_numsurfacepvsbytes = 0;
373         if (r_shadow_buffer_surfacepvs)
374                 Mem_Free(r_shadow_buffer_surfacepvs);
375         r_shadow_buffer_surfacepvs = NULL;
376         if (r_shadow_buffer_surfacelist)
377                 Mem_Free(r_shadow_buffer_surfacelist);
378         r_shadow_buffer_surfacelist = NULL;
379         r_shadow_buffer_numshadowtrispvsbytes = 0;
380         if (r_shadow_buffer_shadowtrispvs)
381                 Mem_Free(r_shadow_buffer_shadowtrispvs);
382         r_shadow_buffer_numlighttrispvsbytes = 0;
383         if (r_shadow_buffer_lighttrispvs)
384                 Mem_Free(r_shadow_buffer_lighttrispvs);
385 }
386
387 void r_shadow_newmap(void)
388 {
389         if (cl.worldmodel && strncmp(cl.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
390                 R_Shadow_EditLights_Reload_f();
391 }
392
393 void R_Shadow_Help_f(void)
394 {
395         Con_Printf(
396 "Documentation on r_shadow system:\n"
397 "Settings:\n"
398 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
399 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
400 "r_shadow_debuglight : render only this light number (-1 = all)\n"
401 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
402 "r_shadow_gloss2intensity : brightness of forced gloss\n"
403 "r_shadow_glossintensity : brightness of textured gloss\n"
404 "r_shadow_lightattenuationlinearscale : used to generate attenuation texture\n"
405 "r_shadow_lightattenuationdividebias : used to generate attenuation texture\n"
406 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
407 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
408 "r_shadow_portallight : use portal visibility for static light precomputation\n"
409 "r_shadow_projectdistance : shadow volume projection distance\n"
410 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
411 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
412 "r_shadow_realtime_world : use high quality world lighting mode\n"
413 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
414 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
415 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
416 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
417 "r_shadow_scissor : use scissor optimization\n"
418 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
419 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
420 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
421 "r_showlighting : useful for performance testing; bright = slow!\n"
422 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
423 "Commands:\n"
424 "r_shadow_help : this help\n"
425         );
426 }
427
428 void R_Shadow_Init(void)
429 {
430         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
431         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
432         Cvar_RegisterVariable(&r_shadow_usenormalmap);
433         Cvar_RegisterVariable(&r_shadow_debuglight);
434         Cvar_RegisterVariable(&r_shadow_gloss);
435         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
436         Cvar_RegisterVariable(&r_shadow_glossintensity);
437         Cvar_RegisterVariable(&r_shadow_glossexponent);
438         Cvar_RegisterVariable(&r_shadow_lightattenuationdividebias);
439         Cvar_RegisterVariable(&r_shadow_lightattenuationlinearscale);
440         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
441         Cvar_RegisterVariable(&r_shadow_lightradiusscale);
442         Cvar_RegisterVariable(&r_shadow_portallight);
443         Cvar_RegisterVariable(&r_shadow_projectdistance);
444         Cvar_RegisterVariable(&r_shadow_frontsidecasting);
445         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
446         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
447         Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
448         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
449         Cvar_RegisterVariable(&r_shadow_realtime_world);
450         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
451         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
452         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
453         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
454         Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
455         Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
456         Cvar_RegisterVariable(&r_shadow_scissor);
457         Cvar_RegisterVariable(&r_shadow_culltriangles);
458         Cvar_RegisterVariable(&r_shadow_polygonfactor);
459         Cvar_RegisterVariable(&r_shadow_polygonoffset);
460         Cvar_RegisterVariable(&r_shadow_texture3d);
461         Cvar_RegisterVariable(&r_coronas);
462         Cvar_RegisterVariable(&gl_flashblend);
463         Cvar_RegisterVariable(&gl_ext_separatestencil);
464         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
465         if (gamemode == GAME_TENEBRAE)
466         {
467                 Cvar_SetValue("r_shadow_gloss", 2);
468                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
469         }
470         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
471         R_Shadow_EditLights_Init();
472         Mem_ExpandableArray_NewArray(&r_shadow_worldlightsarray, r_main_mempool, sizeof(dlight_t), 128);
473         maxshadowtriangles = 0;
474         shadowelements = NULL;
475         maxshadowvertices = 0;
476         shadowvertex3f = NULL;
477         maxvertexupdate = 0;
478         vertexupdate = NULL;
479         vertexremap = NULL;
480         vertexupdatenum = 0;
481         maxshadowmark = 0;
482         numshadowmark = 0;
483         shadowmark = NULL;
484         shadowmarklist = NULL;
485         shadowmarkcount = 0;
486         r_shadow_buffer_numleafpvsbytes = 0;
487         r_shadow_buffer_leafpvs = NULL;
488         r_shadow_buffer_leaflist = NULL;
489         r_shadow_buffer_numsurfacepvsbytes = 0;
490         r_shadow_buffer_surfacepvs = NULL;
491         r_shadow_buffer_surfacelist = NULL;
492         r_shadow_buffer_shadowtrispvs = NULL;
493         r_shadow_buffer_lighttrispvs = NULL;
494         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
495 }
496
497 matrix4x4_t matrix_attenuationxyz =
498 {
499         {
500                 {0.5, 0.0, 0.0, 0.5},
501                 {0.0, 0.5, 0.0, 0.5},
502                 {0.0, 0.0, 0.5, 0.5},
503                 {0.0, 0.0, 0.0, 1.0}
504         }
505 };
506
507 matrix4x4_t matrix_attenuationz =
508 {
509         {
510                 {0.0, 0.0, 0.5, 0.5},
511                 {0.0, 0.0, 0.0, 0.5},
512                 {0.0, 0.0, 0.0, 0.5},
513                 {0.0, 0.0, 0.0, 1.0}
514         }
515 };
516
517 void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
518 {
519         // make sure shadowelements is big enough for this volume
520         if (maxshadowtriangles < numtriangles)
521         {
522                 maxshadowtriangles = numtriangles;
523                 if (shadowelements)
524                         Mem_Free(shadowelements);
525                 shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
526         }
527         // make sure shadowvertex3f is big enough for this volume
528         if (maxshadowvertices < numvertices)
529         {
530                 maxshadowvertices = numvertices;
531                 if (shadowvertex3f)
532                         Mem_Free(shadowvertex3f);
533                 shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
534         }
535 }
536
537 static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
538 {
539         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
540         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
541         int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
542         int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
543         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
544         {
545                 if (r_shadow_buffer_leafpvs)
546                         Mem_Free(r_shadow_buffer_leafpvs);
547                 if (r_shadow_buffer_leaflist)
548                         Mem_Free(r_shadow_buffer_leaflist);
549                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
550                 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
551                 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
552         }
553         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
554         {
555                 if (r_shadow_buffer_surfacepvs)
556                         Mem_Free(r_shadow_buffer_surfacepvs);
557                 if (r_shadow_buffer_surfacelist)
558                         Mem_Free(r_shadow_buffer_surfacelist);
559                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
560                 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
561                 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
562         }
563         if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
564         {
565                 if (r_shadow_buffer_shadowtrispvs)
566                         Mem_Free(r_shadow_buffer_shadowtrispvs);
567                 r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
568                 r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
569         }
570         if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
571         {
572                 if (r_shadow_buffer_lighttrispvs)
573                         Mem_Free(r_shadow_buffer_lighttrispvs);
574                 r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
575                 r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
576         }
577 }
578
579 void R_Shadow_PrepareShadowMark(int numtris)
580 {
581         // make sure shadowmark is big enough for this volume
582         if (maxshadowmark < numtris)
583         {
584                 maxshadowmark = numtris;
585                 if (shadowmark)
586                         Mem_Free(shadowmark);
587                 if (shadowmarklist)
588                         Mem_Free(shadowmarklist);
589                 shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
590                 shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
591                 shadowmarkcount = 0;
592         }
593         shadowmarkcount++;
594         // if shadowmarkcount wrapped we clear the array and adjust accordingly
595         if (shadowmarkcount == 0)
596         {
597                 shadowmarkcount = 1;
598                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
599         }
600         numshadowmark = 0;
601 }
602
603 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)
604 {
605         int i, j;
606         int outtriangles = 0, outvertices = 0;
607         const int *element;
608         const float *vertex;
609         float ratio, direction[3], projectvector[3];
610
611         if (projectdirection)
612                 VectorScale(projectdirection, projectdistance, projectvector);
613         else
614                 VectorClear(projectvector);
615
616         if (maxvertexupdate < innumvertices)
617         {
618                 maxvertexupdate = innumvertices;
619                 if (vertexupdate)
620                         Mem_Free(vertexupdate);
621                 if (vertexremap)
622                         Mem_Free(vertexremap);
623                 vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
624                 vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
625                 vertexupdatenum = 0;
626         }
627         vertexupdatenum++;
628         if (vertexupdatenum == 0)
629         {
630                 vertexupdatenum = 1;
631                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
632                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
633         }
634
635         for (i = 0;i < numshadowmarktris;i++)
636                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
637
638         // create the vertices
639         if (projectdirection)
640         {
641                 for (i = 0;i < numshadowmarktris;i++)
642                 {
643                         element = inelement3i + shadowmarktris[i] * 3;
644                         for (j = 0;j < 3;j++)
645                         {
646                                 if (vertexupdate[element[j]] != vertexupdatenum)
647                                 {
648                                         vertexupdate[element[j]] = vertexupdatenum;
649                                         vertexremap[element[j]] = outvertices;
650                                         vertex = invertex3f + element[j] * 3;
651                                         // project one copy of the vertex according to projectvector
652                                         VectorCopy(vertex, outvertex3f);
653                                         VectorAdd(vertex, projectvector, (outvertex3f + 3));
654                                         outvertex3f += 6;
655                                         outvertices += 2;
656                                 }
657                         }
658                 }
659         }
660         else
661         {
662                 for (i = 0;i < numshadowmarktris;i++)
663                 {
664                         element = inelement3i + shadowmarktris[i] * 3;
665                         for (j = 0;j < 3;j++)
666                         {
667                                 if (vertexupdate[element[j]] != vertexupdatenum)
668                                 {
669                                         vertexupdate[element[j]] = vertexupdatenum;
670                                         vertexremap[element[j]] = outvertices;
671                                         vertex = invertex3f + element[j] * 3;
672                                         // project one copy of the vertex to the sphere radius of the light
673                                         // (FIXME: would projecting it to the light box be better?)
674                                         VectorSubtract(vertex, projectorigin, direction);
675                                         ratio = projectdistance / VectorLength(direction);
676                                         VectorCopy(vertex, outvertex3f);
677                                         VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
678                                         outvertex3f += 6;
679                                         outvertices += 2;
680                                 }
681                         }
682                 }
683         }
684
685         if (r_shadow_frontsidecasting.integer)
686         {
687                 for (i = 0;i < numshadowmarktris;i++)
688                 {
689                         int remappedelement[3];
690                         int markindex;
691                         const int *neighbortriangle;
692
693                         markindex = shadowmarktris[i] * 3;
694                         element = inelement3i + markindex;
695                         neighbortriangle = inneighbor3i + markindex;
696                         // output the front and back triangles
697                         outelement3i[0] = vertexremap[element[0]];
698                         outelement3i[1] = vertexremap[element[1]];
699                         outelement3i[2] = vertexremap[element[2]];
700                         outelement3i[3] = vertexremap[element[2]] + 1;
701                         outelement3i[4] = vertexremap[element[1]] + 1;
702                         outelement3i[5] = vertexremap[element[0]] + 1;
703
704                         outelement3i += 6;
705                         outtriangles += 2;
706                         // output the sides (facing outward from this triangle)
707                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
708                         {
709                                 remappedelement[0] = vertexremap[element[0]];
710                                 remappedelement[1] = vertexremap[element[1]];
711                                 outelement3i[0] = remappedelement[1];
712                                 outelement3i[1] = remappedelement[0];
713                                 outelement3i[2] = remappedelement[0] + 1;
714                                 outelement3i[3] = remappedelement[1];
715                                 outelement3i[4] = remappedelement[0] + 1;
716                                 outelement3i[5] = remappedelement[1] + 1;
717
718                                 outelement3i += 6;
719                                 outtriangles += 2;
720                         }
721                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
722                         {
723                                 remappedelement[1] = vertexremap[element[1]];
724                                 remappedelement[2] = vertexremap[element[2]];
725                                 outelement3i[0] = remappedelement[2];
726                                 outelement3i[1] = remappedelement[1];
727                                 outelement3i[2] = remappedelement[1] + 1;
728                                 outelement3i[3] = remappedelement[2];
729                                 outelement3i[4] = remappedelement[1] + 1;
730                                 outelement3i[5] = remappedelement[2] + 1;
731
732                                 outelement3i += 6;
733                                 outtriangles += 2;
734                         }
735                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
736                         {
737                                 remappedelement[0] = vertexremap[element[0]];
738                                 remappedelement[2] = vertexremap[element[2]];
739                                 outelement3i[0] = remappedelement[0];
740                                 outelement3i[1] = remappedelement[2];
741                                 outelement3i[2] = remappedelement[2] + 1;
742                                 outelement3i[3] = remappedelement[0];
743                                 outelement3i[4] = remappedelement[2] + 1;
744                                 outelement3i[5] = remappedelement[0] + 1;
745
746                                 outelement3i += 6;
747                                 outtriangles += 2;
748                         }
749                 }
750         }
751         else
752         {
753                 for (i = 0;i < numshadowmarktris;i++)
754                 {
755                         int remappedelement[3];
756                         int markindex;
757                         const int *neighbortriangle;
758
759                         markindex = shadowmarktris[i] * 3;
760                         element = inelement3i + markindex;
761                         neighbortriangle = inneighbor3i + markindex;
762                         // output the front and back triangles
763                         outelement3i[0] = vertexremap[element[2]];
764                         outelement3i[1] = vertexremap[element[1]];
765                         outelement3i[2] = vertexremap[element[0]];
766                         outelement3i[3] = vertexremap[element[0]] + 1;
767                         outelement3i[4] = vertexremap[element[1]] + 1;
768                         outelement3i[5] = vertexremap[element[2]] + 1;
769
770                         outelement3i += 6;
771                         outtriangles += 2;
772                         // output the sides (facing outward from this triangle)
773                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
774                         {
775                                 remappedelement[0] = vertexremap[element[0]];
776                                 remappedelement[1] = vertexremap[element[1]];
777                                 outelement3i[0] = remappedelement[0];
778                                 outelement3i[1] = remappedelement[1];
779                                 outelement3i[2] = remappedelement[1] + 1;
780                                 outelement3i[3] = remappedelement[0];
781                                 outelement3i[4] = remappedelement[1] + 1;
782                                 outelement3i[5] = remappedelement[0] + 1;
783
784                                 outelement3i += 6;
785                                 outtriangles += 2;
786                         }
787                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
788                         {
789                                 remappedelement[1] = vertexremap[element[1]];
790                                 remappedelement[2] = vertexremap[element[2]];
791                                 outelement3i[0] = remappedelement[1];
792                                 outelement3i[1] = remappedelement[2];
793                                 outelement3i[2] = remappedelement[2] + 1;
794                                 outelement3i[3] = remappedelement[1];
795                                 outelement3i[4] = remappedelement[2] + 1;
796                                 outelement3i[5] = remappedelement[1] + 1;
797
798                                 outelement3i += 6;
799                                 outtriangles += 2;
800                         }
801                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
802                         {
803                                 remappedelement[0] = vertexremap[element[0]];
804                                 remappedelement[2] = vertexremap[element[2]];
805                                 outelement3i[0] = remappedelement[2];
806                                 outelement3i[1] = remappedelement[0];
807                                 outelement3i[2] = remappedelement[0] + 1;
808                                 outelement3i[3] = remappedelement[2];
809                                 outelement3i[4] = remappedelement[0] + 1;
810                                 outelement3i[5] = remappedelement[2] + 1;
811
812                                 outelement3i += 6;
813                                 outtriangles += 2;
814                         }
815                 }
816         }
817         if (outnumvertices)
818                 *outnumvertices = outvertices;
819         return outtriangles;
820 }
821
822 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)
823 {
824         int tris, outverts;
825         if (projectdistance < 0.1)
826         {
827                 Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
828                 return;
829         }
830         if (!numverts || !nummarktris)
831                 return;
832         // make sure shadowelements is big enough for this volume
833         if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
834                 R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
835         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
836         r_refdef.stats.lights_dynamicshadowtriangles += tris;
837         R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
838 }
839
840 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)
841 {
842         int t, tend;
843         const int *e;
844         const float *v[3];
845         float normal[3];
846         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
847                 return;
848         tend = firsttriangle + numtris;
849         if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
850         {
851                 // surface box entirely inside light box, no box cull
852                 if (projectdirection)
853                 {
854                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
855                         {
856                                 TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
857                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
858                                         shadowmarklist[numshadowmark++] = t;
859                         }
860                 }
861                 else
862                 {
863                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
864                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
865                                         shadowmarklist[numshadowmark++] = t;
866                 }
867         }
868         else
869         {
870                 // surface box not entirely inside light box, cull each triangle
871                 if (projectdirection)
872                 {
873                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
874                         {
875                                 v[0] = invertex3f + e[0] * 3;
876                                 v[1] = invertex3f + e[1] * 3;
877                                 v[2] = invertex3f + e[2] * 3;
878                                 TriangleNormal(v[0], v[1], v[2], normal);
879                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
880                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
881                                         shadowmarklist[numshadowmark++] = t;
882                         }
883                 }
884                 else
885                 {
886                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
887                         {
888                                 v[0] = invertex3f + e[0] * 3;
889                                 v[1] = invertex3f + e[1] * 3;
890                                 v[2] = invertex3f + e[2] * 3;
891                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
892                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
893                                         shadowmarklist[numshadowmark++] = t;
894                         }
895                 }
896         }
897 }
898
899 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
900 {
901         if (r_shadow_compilingrtlight)
902         {
903                 // if we're compiling an rtlight, capture the mesh
904                 Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
905                 return;
906         }
907         r_refdef.stats.lights_shadowtriangles += numtriangles;
908         CHECKGLERROR
909         R_Mesh_VertexPointer(vertex3f, 0, 0);
910         GL_LockArrays(0, numvertices);
911         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
912         {
913                 // decrement stencil if backface is behind depthbuffer
914                 GL_CullFace(r_refdef.view.cullface_front);
915                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
916                 R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
917                 // increment stencil if frontface is behind depthbuffer
918                 GL_CullFace(r_refdef.view.cullface_back);
919                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
920         }
921         R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
922         GL_LockArrays(0, 0);
923         CHECKGLERROR
924 }
925
926 static void R_Shadow_MakeTextures_MakeCorona(void)
927 {
928         float dx, dy;
929         int x, y, a;
930         unsigned char pixels[32][32][4];
931         for (y = 0;y < 32;y++)
932         {
933                 dy = (y - 15.5f) * (1.0f / 16.0f);
934                 for (x = 0;x < 32;x++)
935                 {
936                         dx = (x - 15.5f) * (1.0f / 16.0f);
937                         a = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));
938                         a = bound(0, a, 255);
939                         pixels[y][x][0] = a;
940                         pixels[y][x][1] = a;
941                         pixels[y][x][2] = a;
942                         pixels[y][x][3] = 255;
943                 }
944         }
945         r_shadow_lightcorona = R_LoadTexture2D(r_shadow_texturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE, NULL);
946 }
947
948 static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
949 {
950         float dist = sqrt(x*x+y*y+z*z);
951         float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
952         // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
953         return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
954 }
955
956 static void R_Shadow_MakeTextures(void)
957 {
958         int x, y, z;
959         float intensity, dist;
960         unsigned int *data;
961         R_FreeTexturePool(&r_shadow_texturepool);
962         r_shadow_texturepool = R_AllocTexturePool();
963         r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
964         r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
965         data = (unsigned int *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE) * 4);
966         // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
967         for (x = 0;x <= ATTENTABLESIZE;x++)
968         {
969                 dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
970                 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
971                 r_shadow_attentable[x] = bound(0, intensity, 1);
972         }
973         // 1D gradient texture
974         for (x = 0;x < ATTEN1DSIZE;x++)
975                 data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
976         r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
977         // 2D circle texture
978         for (y = 0;y < ATTEN2DSIZE;y++)
979                 for (x = 0;x < ATTEN2DSIZE;x++)
980                         data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
981         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
982         // 3D sphere texture
983         if (r_shadow_texture3d.integer && gl_texture3d)
984         {
985                 for (z = 0;z < ATTEN3DSIZE;z++)
986                         for (y = 0;y < ATTEN3DSIZE;y++)
987                                 for (x = 0;x < ATTEN3DSIZE;x++)
988                                         data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
989                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
990         }
991         else
992                 r_shadow_attenuation3dtexture = NULL;
993         Mem_Free(data);
994
995         R_Shadow_MakeTextures_MakeCorona();
996
997         // Editor light sprites
998         r_editlights_sprcursor = Draw_CachePic("gfx/editlights/cursor", true);
999         r_editlights_sprlight = Draw_CachePic("gfx/editlights/light", true);
1000         r_editlights_sprnoshadowlight = Draw_CachePic("gfx/editlights/noshadow", true);
1001         r_editlights_sprcubemaplight = Draw_CachePic("gfx/editlights/cubemaplight", true);
1002         r_editlights_sprcubemapnoshadowlight = Draw_CachePic("gfx/editlights/cubemapnoshadowlight", true);
1003         r_editlights_sprselection = Draw_CachePic("gfx/editlights/selection", true);
1004 }
1005
1006 void R_Shadow_ValidateCvars(void)
1007 {
1008         if (r_shadow_texture3d.integer && !gl_texture3d)
1009                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
1010         if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
1011                 Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
1012         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
1013                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
1014 }
1015
1016 void R_Shadow_RenderMode_Begin(void)
1017 {
1018         R_Shadow_ValidateCvars();
1019
1020         if (!r_shadow_attenuation2dtexture
1021          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
1022          || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
1023          || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
1024                 R_Shadow_MakeTextures();
1025
1026         CHECKGLERROR
1027         R_Mesh_ColorPointer(NULL, 0, 0);
1028         R_Mesh_ResetTextureState();
1029         GL_BlendFunc(GL_ONE, GL_ZERO);
1030         GL_DepthRange(0, 1);
1031         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
1032         GL_DepthTest(true);
1033         GL_DepthMask(false);
1034         GL_Color(0, 0, 0, 1);
1035         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1036
1037         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1038
1039         if (gl_ext_separatestencil.integer)
1040                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
1041         else if (gl_ext_stenciltwoside.integer)
1042                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
1043         else
1044                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
1045
1046         if (r_glsl.integer && gl_support_fragment_shader)
1047                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
1048         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1049                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
1050         else
1051                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
1052 }
1053
1054 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
1055 {
1056         rsurface.rtlight = rtlight;
1057 }
1058
1059 void R_Shadow_RenderMode_Reset(void)
1060 {
1061         CHECKGLERROR
1062         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1063         {
1064                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1065         }
1066         R_Mesh_ColorPointer(NULL, 0, 0);
1067         R_Mesh_ResetTextureState();
1068         GL_DepthRange(0, 1);
1069         GL_DepthTest(true);
1070         GL_DepthMask(false);
1071         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1072         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
1073         qglDisable(GL_STENCIL_TEST);CHECKGLERROR
1074         qglStencilMask(~0);CHECKGLERROR
1075         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
1076         qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
1077         GL_CullFace(r_refdef.view.cullface_back);
1078         GL_Color(1, 1, 1, 1);
1079         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1080         GL_BlendFunc(GL_ONE, GL_ZERO);
1081         R_SetupGenericShader(false);
1082 }
1083
1084 void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil)
1085 {
1086         CHECKGLERROR
1087         R_Shadow_RenderMode_Reset();
1088         GL_ColorMask(0, 0, 0, 0);
1089         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1090         R_SetupDepthOrShadowShader();
1091         qglDepthFunc(GL_LESS);CHECKGLERROR
1092         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1093         r_shadow_rendermode = r_shadow_shadowingrendermode;
1094         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
1095         {
1096                 GL_CullFace(GL_NONE);
1097                 qglStencilOpSeparate(r_refdef.view.cullface_front, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1098                 qglStencilOpSeparate(r_refdef.view.cullface_back, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1099         }
1100         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1101         {
1102                 GL_CullFace(GL_NONE);
1103                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1104                 qglActiveStencilFaceEXT(r_refdef.view.cullface_front);CHECKGLERROR
1105                 qglStencilMask(~0);CHECKGLERROR
1106                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1107                 qglActiveStencilFaceEXT(r_refdef.view.cullface_back);CHECKGLERROR
1108                 qglStencilMask(~0);CHECKGLERROR
1109                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1110         }
1111         if (clearstencil)
1112                 GL_Clear(GL_STENCIL_BUFFER_BIT);
1113         r_refdef.stats.lights_clears++;
1114 }
1115
1116 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
1117 {
1118         CHECKGLERROR
1119         R_Shadow_RenderMode_Reset();
1120         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1121         if (!transparent)
1122         {
1123                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1124         }
1125         if (stenciltest)
1126         {
1127                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1128                 // only draw light where this geometry was already rendered AND the
1129                 // stencil is 128 (values other than this mean shadow)
1130                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1131         }
1132         r_shadow_rendermode = r_shadow_lightingrendermode;
1133         // do global setup needed for the chosen lighting mode
1134         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1135         {
1136                 R_Mesh_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap)); // light filter
1137                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1138                 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1139                 CHECKGLERROR
1140         }
1141 }
1142
1143 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1144 {
1145         CHECKGLERROR
1146         R_Shadow_RenderMode_Reset();
1147         GL_BlendFunc(GL_ONE, GL_ONE);
1148         GL_DepthRange(0, 1);
1149         GL_DepthTest(r_showshadowvolumes.integer < 2);
1150         GL_Color(0.0, 0.0125 * r_refdef.view.colorscale, 0.1 * r_refdef.view.colorscale, 1);
1151         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1152         GL_CullFace(GL_NONE);
1153         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1154 }
1155
1156 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1157 {
1158         CHECKGLERROR
1159         R_Shadow_RenderMode_Reset();
1160         GL_BlendFunc(GL_ONE, GL_ONE);
1161         GL_DepthRange(0, 1);
1162         GL_DepthTest(r_showlighting.integer < 2);
1163         GL_Color(0.1 * r_refdef.view.colorscale, 0.0125 * r_refdef.view.colorscale, 0, 1);
1164         if (!transparent)
1165         {
1166                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1167         }
1168         if (stenciltest)
1169         {
1170                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1171                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1172         }
1173         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1174 }
1175
1176 void R_Shadow_RenderMode_End(void)
1177 {
1178         CHECKGLERROR
1179         R_Shadow_RenderMode_Reset();
1180         R_Shadow_RenderMode_ActiveLight(NULL);
1181         GL_DepthMask(true);
1182         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1183         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1184 }
1185
1186 int bboxedges[12][2] =
1187 {
1188         // top
1189         {0, 1}, // +X
1190         {0, 2}, // +Y
1191         {1, 3}, // Y, +X
1192         {2, 3}, // X, +Y
1193         // bottom
1194         {4, 5}, // +X
1195         {4, 6}, // +Y
1196         {5, 7}, // Y, +X
1197         {6, 7}, // X, +Y
1198         // verticals
1199         {0, 4}, // +Z
1200         {1, 5}, // X, +Z
1201         {2, 6}, // Y, +Z
1202         {3, 7}, // XY, +Z
1203 };
1204
1205 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1206 {
1207         int i, ix1, iy1, ix2, iy2;
1208         float x1, y1, x2, y2;
1209         vec4_t v, v2;
1210         float vertex[20][3];
1211         int j, k;
1212         vec4_t plane4f;
1213         int numvertices;
1214         float corner[8][4];
1215         float dist[8];
1216         int sign[8];
1217         float f;
1218
1219         if (!r_shadow_scissor.integer)
1220                 return false;
1221
1222         // if view is inside the light box, just say yes it's visible
1223         if (BoxesOverlap(r_refdef.view.origin, r_refdef.view.origin, mins, maxs))
1224         {
1225                 GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1226                 return false;
1227         }
1228
1229         x1 = y1 = x2 = y2 = 0;
1230
1231         // transform all corners that are infront of the nearclip plane
1232         VectorNegate(r_refdef.view.frustum[4].normal, plane4f);
1233         plane4f[3] = r_refdef.view.frustum[4].dist;
1234         numvertices = 0;
1235         for (i = 0;i < 8;i++)
1236         {
1237                 Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1);
1238                 dist[i] = DotProduct4(corner[i], plane4f);
1239                 sign[i] = dist[i] > 0;
1240                 if (!sign[i])
1241                 {
1242                         VectorCopy(corner[i], vertex[numvertices]);
1243                         numvertices++;
1244                 }
1245         }
1246         // if some points are behind the nearclip, add clipped edge points to make
1247         // sure that the scissor boundary is complete
1248         if (numvertices > 0 && numvertices < 8)
1249         {
1250                 // add clipped edge points
1251                 for (i = 0;i < 12;i++)
1252                 {
1253                         j = bboxedges[i][0];
1254                         k = bboxedges[i][1];
1255                         if (sign[j] != sign[k])
1256                         {
1257                                 f = dist[j] / (dist[j] - dist[k]);
1258                                 VectorLerp(corner[j], f, corner[k], vertex[numvertices]);
1259                                 numvertices++;
1260                         }
1261                 }
1262         }
1263
1264         // if we have no points to check, the light is behind the view plane
1265         if (!numvertices)
1266                 return true;
1267
1268         // if we have some points to transform, check what screen area is covered
1269         x1 = y1 = x2 = y2 = 0;
1270         v[3] = 1.0f;
1271         //Con_Printf("%i vertices to transform...\n", numvertices);
1272         for (i = 0;i < numvertices;i++)
1273         {
1274                 VectorCopy(vertex[i], v);
1275                 GL_TransformToScreen(v, v2);
1276                 //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]);
1277                 if (i)
1278                 {
1279                         if (x1 > v2[0]) x1 = v2[0];
1280                         if (x2 < v2[0]) x2 = v2[0];
1281                         if (y1 > v2[1]) y1 = v2[1];
1282                         if (y2 < v2[1]) y2 = v2[1];
1283                 }
1284                 else
1285                 {
1286                         x1 = x2 = v2[0];
1287                         y1 = y2 = v2[1];
1288                 }
1289         }
1290
1291         // now convert the scissor rectangle to integer screen coordinates
1292         ix1 = (int)(x1 - 1.0f);
1293         iy1 = (int)(y1 - 1.0f);
1294         ix2 = (int)(x2 + 1.0f);
1295         iy2 = (int)(y2 + 1.0f);
1296         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1297
1298         // clamp it to the screen
1299         if (ix1 < r_refdef.view.x) ix1 = r_refdef.view.x;
1300         if (iy1 < r_refdef.view.y) iy1 = r_refdef.view.y;
1301         if (ix2 > r_refdef.view.x + r_refdef.view.width) ix2 = r_refdef.view.x + r_refdef.view.width;
1302         if (iy2 > r_refdef.view.y + r_refdef.view.height) iy2 = r_refdef.view.y + r_refdef.view.height;
1303
1304         // if it is inside out, it's not visible
1305         if (ix2 <= ix1 || iy2 <= iy1)
1306                 return true;
1307
1308         // the light area is visible, set up the scissor rectangle
1309         GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1310         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1311         //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1312         r_refdef.stats.lights_scissored++;
1313         return false;
1314 }
1315
1316 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1317 {
1318         float *vertex3f = rsurface.vertex3f + 3 * firstvertex;
1319         float *normal3f = rsurface.normal3f + 3 * firstvertex;
1320         float *color4f = rsurface.array_color4f + 4 * firstvertex;
1321         float dist, dot, distintensity, shadeintensity, v[3], n[3];
1322         if (r_textureunits.integer >= 3)
1323         {
1324                 if (VectorLength2(diffusecolor) > 0)
1325                 {
1326                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1327                         {
1328                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1329                                 Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1330                                 if ((dot = DotProduct(n, v)) < 0)
1331                                 {
1332                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1333                                         VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
1334                                 }
1335                                 else
1336                                         VectorCopy(ambientcolor, color4f);
1337                                 if (r_refdef.fogenabled)
1338                                 {
1339                                         float f;
1340                                         f = FogPoint_Model(vertex3f);
1341                                         VectorScale(color4f, f, color4f);
1342                                 }
1343                                 color4f[3] = 1;
1344                         }
1345                 }
1346                 else
1347                 {
1348                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1349                         {
1350                                 VectorCopy(ambientcolor, color4f);
1351                                 if (r_refdef.fogenabled)
1352                                 {
1353                                         float f;
1354                                         Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1355                                         f = FogPoint_Model(vertex3f);
1356                                         VectorScale(color4f, f, color4f);
1357                                 }
1358                                 color4f[3] = 1;
1359                         }
1360                 }
1361         }
1362         else if (r_textureunits.integer >= 2)
1363         {
1364                 if (VectorLength2(diffusecolor) > 0)
1365                 {
1366                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1367                         {
1368                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1369                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1370                                 {
1371                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1372                                         if ((dot = DotProduct(n, v)) < 0)
1373                                         {
1374                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1375                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1376                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1377                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1378                                         }
1379                                         else
1380                                         {
1381                                                 color4f[0] = ambientcolor[0] * distintensity;
1382                                                 color4f[1] = ambientcolor[1] * distintensity;
1383                                                 color4f[2] = ambientcolor[2] * distintensity;
1384                                         }
1385                                         if (r_refdef.fogenabled)
1386                                         {
1387                                                 float f;
1388                                                 f = FogPoint_Model(vertex3f);
1389                                                 VectorScale(color4f, f, color4f);
1390                                         }
1391                                 }
1392                                 else
1393                                         VectorClear(color4f);
1394                                 color4f[3] = 1;
1395                         }
1396                 }
1397                 else
1398                 {
1399                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1400                         {
1401                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1402                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1403                                 {
1404                                         color4f[0] = ambientcolor[0] * distintensity;
1405                                         color4f[1] = ambientcolor[1] * distintensity;
1406                                         color4f[2] = ambientcolor[2] * distintensity;
1407                                         if (r_refdef.fogenabled)
1408                                         {
1409                                                 float f;
1410                                                 f = FogPoint_Model(vertex3f);
1411                                                 VectorScale(color4f, f, color4f);
1412                                         }
1413                                 }
1414                                 else
1415                                         VectorClear(color4f);
1416                                 color4f[3] = 1;
1417                         }
1418                 }
1419         }
1420         else
1421         {
1422                 if (VectorLength2(diffusecolor) > 0)
1423                 {
1424                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1425                         {
1426                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1427                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1428                                 {
1429                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1430                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1431                                         if ((dot = DotProduct(n, v)) < 0)
1432                                         {
1433                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1434                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1435                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1436                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1437                                         }
1438                                         else
1439                                         {
1440                                                 color4f[0] = ambientcolor[0] * distintensity;
1441                                                 color4f[1] = ambientcolor[1] * distintensity;
1442                                                 color4f[2] = ambientcolor[2] * distintensity;
1443                                         }
1444                                         if (r_refdef.fogenabled)
1445                                         {
1446                                                 float f;
1447                                                 f = FogPoint_Model(vertex3f);
1448                                                 VectorScale(color4f, f, color4f);
1449                                         }
1450                                 }
1451                                 else
1452                                         VectorClear(color4f);
1453                                 color4f[3] = 1;
1454                         }
1455                 }
1456                 else
1457                 {
1458                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1459                         {
1460                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1461                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1462                                 {
1463                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1464                                         color4f[0] = ambientcolor[0] * distintensity;
1465                                         color4f[1] = ambientcolor[1] * distintensity;
1466                                         color4f[2] = ambientcolor[2] * distintensity;
1467                                         if (r_refdef.fogenabled)
1468                                         {
1469                                                 float f;
1470                                                 f = FogPoint_Model(vertex3f);
1471                                                 VectorScale(color4f, f, color4f);
1472                                         }
1473                                 }
1474                                 else
1475                                         VectorClear(color4f);
1476                                 color4f[3] = 1;
1477                         }
1478                 }
1479         }
1480 }
1481
1482 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1483
1484 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1485 {
1486         int i;
1487         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1488         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1489         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1490         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1491         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1492         float lightdir[3];
1493         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1494         {
1495                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1496                 // the cubemap normalizes this for us
1497                 out3f[0] = DotProduct(svector3f, lightdir);
1498                 out3f[1] = DotProduct(tvector3f, lightdir);
1499                 out3f[2] = DotProduct(normal3f, lightdir);
1500         }
1501 }
1502
1503 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1504 {
1505         int i;
1506         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1507         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1508         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1509         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1510         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1511         float lightdir[3], eyedir[3], halfdir[3];
1512         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1513         {
1514                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1515                 VectorNormalize(lightdir);
1516                 VectorSubtract(rsurface.modelorg, vertex3f, eyedir);
1517                 VectorNormalize(eyedir);
1518                 VectorAdd(lightdir, eyedir, halfdir);
1519                 // the cubemap normalizes this for us
1520                 out3f[0] = DotProduct(svector3f, halfdir);
1521                 out3f[1] = DotProduct(tvector3f, halfdir);
1522                 out3f[2] = DotProduct(normal3f, halfdir);
1523         }
1524 }
1525
1526 static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1527 {
1528         // used to display how many times a surface is lit for level design purposes
1529         GL_Color(0.1 * r_refdef.view.colorscale, 0.025 * r_refdef.view.colorscale, 0, 1);
1530         R_Mesh_ColorPointer(NULL, 0, 0);
1531         R_Mesh_ResetTextureState();
1532         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1533 }
1534
1535 static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1536 {
1537         // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1538         R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
1539         if ((rsurface.texture->currentmaterialflags & MATERIALFLAG_VERTEXTEXTUREBLEND))
1540                 R_Mesh_ColorPointer(rsurface.modellightmapcolor4f, rsurface.modellightmapcolor4f_bufferobject, rsurface.modellightmapcolor4f_bufferoffset);
1541         else
1542                 R_Mesh_ColorPointer(NULL, 0, 0);
1543         R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
1544         R_Mesh_TexBind(GL20TU_NORMAL, R_GetTexture(rsurface.texture->currentskinframe->nmap));
1545         R_Mesh_TexBind(GL20TU_COLOR, R_GetTexture(rsurface.texture->basetexture));
1546         R_Mesh_TexBind(GL20TU_GLOSS, R_GetTexture(rsurface.texture->glosstexture));
1547         if (rsurface.texture->backgroundcurrentskinframe)
1548         {
1549                 R_Mesh_TexBind(GL20TU_SECONDARY_NORMAL, R_GetTexture(rsurface.texture->backgroundcurrentskinframe->nmap));
1550                 R_Mesh_TexBind(GL20TU_SECONDARY_COLOR, R_GetTexture(rsurface.texture->backgroundbasetexture));
1551                 R_Mesh_TexBind(GL20TU_SECONDARY_GLOSS, R_GetTexture(rsurface.texture->backgroundglosstexture));
1552         }
1553         //R_Mesh_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap));
1554         R_Mesh_TexBind(GL20TU_FOGMASK, R_GetTexture(r_texture_fogattenuation));
1555         R_Mesh_TexBind(GL20TU_PANTS, R_GetTexture(rsurface.texture->currentskinframe->pants));
1556         R_Mesh_TexBind(GL20TU_SHIRT, R_GetTexture(rsurface.texture->currentskinframe->shirt));
1557         R_Mesh_TexBind(GL20TU_ATTENUATION, R_GetTexture(r_shadow_attenuationgradienttexture));
1558         R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
1559         R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
1560         R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
1561         R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
1562         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1563         {
1564                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1565         }
1566         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1567         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1568         {
1569                 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1570         }
1571 }
1572
1573 static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, float r, float g, float b)
1574 {
1575         // shared final code for all the dot3 layers
1576         int renders;
1577         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1578         for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1579         {
1580                 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1581                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1582         }
1583 }
1584
1585 static void R_Shadow_RenderLighting_Light_Dot3_AmbientPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1586 {
1587         rmeshstate_t m;
1588         // colorscale accounts for how much we multiply the brightness
1589         // during combine.
1590         //
1591         // mult is how many times the final pass of the lighting will be
1592         // performed to get more brightness than otherwise possible.
1593         //
1594         // Limit mult to 64 for sanity sake.
1595         GL_Color(1,1,1,1);
1596         if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1597         {
1598                 // 3 3D combine path (Geforce3, Radeon 8500)
1599                 memset(&m, 0, sizeof(m));
1600                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1601                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1602                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1603                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1604                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1605                 m.tex[1] = R_GetTexture(basetexture);
1606                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1607                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1608                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1609                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1610                 m.texcubemap[2] = R_GetTexture(rsurface.rtlight->currentcubemap);
1611                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1612                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1613                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1614                 m.texmatrix[2] = rsurface.entitytolight;
1615                 GL_BlendFunc(GL_ONE, GL_ONE);
1616         }
1617         else if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1618         {
1619                 // 2 3D combine path (Geforce3, original Radeon)
1620                 memset(&m, 0, sizeof(m));
1621                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1622                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1623                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1624                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1625                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1626                 m.tex[1] = R_GetTexture(basetexture);
1627                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1628                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1629                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1630                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1631                 GL_BlendFunc(GL_ONE, GL_ONE);
1632         }
1633         else if (r_textureunits.integer >= 4 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1634         {
1635                 // 4 2D combine path (Geforce3, Radeon 8500)
1636                 memset(&m, 0, sizeof(m));
1637                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1638                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1639                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1640                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1641                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1642                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1643                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1644                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1645                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1646                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1647                 m.tex[2] = R_GetTexture(basetexture);
1648                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1649                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1650                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1651                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1652                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1653                 {
1654                         m.texcubemap[3] = R_GetTexture(rsurface.rtlight->currentcubemap);
1655                         m.pointer_texcoord3f[3] = rsurface.vertex3f;
1656                         m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1657                         m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1658                         m.texmatrix[3] = rsurface.entitytolight;
1659                 }
1660                 GL_BlendFunc(GL_ONE, GL_ONE);
1661         }
1662         else if (r_textureunits.integer >= 3 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1663         {
1664                 // 3 2D combine path (Geforce3, original Radeon)
1665                 memset(&m, 0, sizeof(m));
1666                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1667                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1668                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1669                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1670                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1671                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1672                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1673                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1674                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1675                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1676                 m.tex[2] = R_GetTexture(basetexture);
1677                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1678                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1679                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1680                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1681                 GL_BlendFunc(GL_ONE, GL_ONE);
1682         }
1683         else
1684         {
1685                 // 2/2/2 2D combine path (any dot3 card)
1686                 memset(&m, 0, sizeof(m));
1687                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1688                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1689                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1690                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1691                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1692                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1693                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1694                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1695                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1696                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1697                 R_Mesh_TextureState(&m);
1698                 GL_ColorMask(0,0,0,1);
1699                 GL_BlendFunc(GL_ONE, GL_ZERO);
1700                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1701
1702                 // second pass
1703                 memset(&m, 0, sizeof(m));
1704                 m.tex[0] = R_GetTexture(basetexture);
1705                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1706                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1707                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1708                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1709                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1710                 {
1711                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1712                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1713                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1714                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1715                         m.texmatrix[1] = rsurface.entitytolight;
1716                 }
1717                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1718         }
1719         // this final code is shared
1720         R_Mesh_TextureState(&m);
1721         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1722 }
1723
1724 static void R_Shadow_RenderLighting_Light_Dot3_DiffusePass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1725 {
1726         rmeshstate_t m;
1727         // colorscale accounts for how much we multiply the brightness
1728         // during combine.
1729         //
1730         // mult is how many times the final pass of the lighting will be
1731         // performed to get more brightness than otherwise possible.
1732         //
1733         // Limit mult to 64 for sanity sake.
1734         GL_Color(1,1,1,1);
1735         // generate normalization cubemap texcoords
1736         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1737         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1738         {
1739                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1740                 memset(&m, 0, sizeof(m));
1741                 m.tex[0] = R_GetTexture(normalmaptexture);
1742                 m.texcombinergb[0] = GL_REPLACE;
1743                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1744                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1745                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1746                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1747                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1748                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1749                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1750                 m.pointer_texcoord_bufferobject[1] = 0;
1751                 m.pointer_texcoord_bufferoffset[1] = 0;
1752                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1753                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1754                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1755                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1756                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1757                 R_Mesh_TextureState(&m);
1758                 GL_ColorMask(0,0,0,1);
1759                 GL_BlendFunc(GL_ONE, GL_ZERO);
1760                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1761
1762                 // second pass
1763                 memset(&m, 0, sizeof(m));
1764                 m.tex[0] = R_GetTexture(basetexture);
1765                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1766                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1767                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1768                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1769                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1770                 {
1771                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1772                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1773                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1774                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1775                         m.texmatrix[1] = rsurface.entitytolight;
1776                 }
1777                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1778         }
1779         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1780         {
1781                 // 1/2/2 3D combine path (original Radeon)
1782                 memset(&m, 0, sizeof(m));
1783                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1784                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1785                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1786                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1787                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1788                 R_Mesh_TextureState(&m);
1789                 GL_ColorMask(0,0,0,1);
1790                 GL_BlendFunc(GL_ONE, GL_ZERO);
1791                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1792
1793                 // second pass
1794                 memset(&m, 0, sizeof(m));
1795                 m.tex[0] = R_GetTexture(normalmaptexture);
1796                 m.texcombinergb[0] = GL_REPLACE;
1797                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1798                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1799                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1800                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1801                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1802                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1803                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1804                 m.pointer_texcoord_bufferobject[1] = 0;
1805                 m.pointer_texcoord_bufferoffset[1] = 0;
1806                 R_Mesh_TextureState(&m);
1807                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1808                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1809
1810                 // second pass
1811                 memset(&m, 0, sizeof(m));
1812                 m.tex[0] = R_GetTexture(basetexture);
1813                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1814                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1815                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1816                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1817                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1818                 {
1819                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1820                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1821                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1822                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1823                         m.texmatrix[1] = rsurface.entitytolight;
1824                 }
1825                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1826         }
1827         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1828         {
1829                 // 2/2 3D combine path (original Radeon)
1830                 memset(&m, 0, sizeof(m));
1831                 m.tex[0] = R_GetTexture(normalmaptexture);
1832                 m.texcombinergb[0] = GL_REPLACE;
1833                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1834                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1835                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1836                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1837                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1838                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1839                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1840                 m.pointer_texcoord_bufferobject[1] = 0;
1841                 m.pointer_texcoord_bufferoffset[1] = 0;
1842                 R_Mesh_TextureState(&m);
1843                 GL_ColorMask(0,0,0,1);
1844                 GL_BlendFunc(GL_ONE, GL_ZERO);
1845                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1846
1847                 // second pass
1848                 memset(&m, 0, sizeof(m));
1849                 m.tex[0] = R_GetTexture(basetexture);
1850                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1851                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1852                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1853                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1854                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1855                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1856                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1857                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1858                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
1859                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1860         }
1861         else if (r_textureunits.integer >= 4)
1862         {
1863                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1864                 memset(&m, 0, sizeof(m));
1865                 m.tex[0] = R_GetTexture(normalmaptexture);
1866                 m.texcombinergb[0] = GL_REPLACE;
1867                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1868                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1869                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1870                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1871                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1872                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1873                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1874                 m.pointer_texcoord_bufferobject[1] = 0;
1875                 m.pointer_texcoord_bufferoffset[1] = 0;
1876                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1877                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1878                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1879                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1880                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1881                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1882                 m.pointer_texcoord3f[3] = rsurface.vertex3f;
1883                 m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1884                 m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1885                 m.texmatrix[3] = rsurface.entitytoattenuationz;
1886                 R_Mesh_TextureState(&m);
1887                 GL_ColorMask(0,0,0,1);
1888                 GL_BlendFunc(GL_ONE, GL_ZERO);
1889                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1890
1891                 // second pass
1892                 memset(&m, 0, sizeof(m));
1893                 m.tex[0] = R_GetTexture(basetexture);
1894                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1895                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1896                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1897                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1898                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1899                 {
1900                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1901                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1902                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1903                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1904                         m.texmatrix[1] = rsurface.entitytolight;
1905                 }
1906                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1907         }
1908         else
1909         {
1910                 // 2/2/2 2D combine path (any dot3 card)
1911                 memset(&m, 0, sizeof(m));
1912                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1913                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1914                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1915                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1916                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1917                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1918                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1919                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1920                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1921                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1922                 R_Mesh_TextureState(&m);
1923                 GL_ColorMask(0,0,0,1);
1924                 GL_BlendFunc(GL_ONE, GL_ZERO);
1925                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1926
1927                 // second pass
1928                 memset(&m, 0, sizeof(m));
1929                 m.tex[0] = R_GetTexture(normalmaptexture);
1930                 m.texcombinergb[0] = GL_REPLACE;
1931                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1932                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1933                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1934                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1935                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1936                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1937                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1938                 m.pointer_texcoord_bufferobject[1] = 0;
1939                 m.pointer_texcoord_bufferoffset[1] = 0;
1940                 R_Mesh_TextureState(&m);
1941                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1942                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1943
1944                 // second pass
1945                 memset(&m, 0, sizeof(m));
1946                 m.tex[0] = R_GetTexture(basetexture);
1947                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1948                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1949                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1950                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1951                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1952                 {
1953                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1954                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1955                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1956                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1957                         m.texmatrix[1] = rsurface.entitytolight;
1958                 }
1959                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1960         }
1961         // this final code is shared
1962         R_Mesh_TextureState(&m);
1963         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1964 }
1965
1966 static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1967 {
1968         float glossexponent;
1969         rmeshstate_t m;
1970         // FIXME: detect blendsquare!
1971         //if (!gl_support_blendsquare)
1972         //      return;
1973         GL_Color(1,1,1,1);
1974         // generate normalization cubemap texcoords
1975         R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1976         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1977         {
1978                 // 2/0/0/1/2 3D combine blendsquare path
1979                 memset(&m, 0, sizeof(m));
1980                 m.tex[0] = R_GetTexture(normalmaptexture);
1981                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1982                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1983                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1984                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1985                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1986                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1987                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1988                 m.pointer_texcoord_bufferobject[1] = 0;
1989                 m.pointer_texcoord_bufferoffset[1] = 0;
1990                 R_Mesh_TextureState(&m);
1991                 GL_ColorMask(0,0,0,1);
1992                 // this squares the result
1993                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1994                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1995
1996                 // second and third pass
1997                 R_Mesh_ResetTextureState();
1998                 // square alpha in framebuffer a few times to make it shiny
1999                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2000                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2001                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2002
2003                 // fourth pass
2004                 memset(&m, 0, sizeof(m));
2005                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
2006                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2007                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2008                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2009                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2010                 R_Mesh_TextureState(&m);
2011                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2012                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2013
2014                 // fifth pass
2015                 memset(&m, 0, sizeof(m));
2016                 m.tex[0] = R_GetTexture(glosstexture);
2017                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2018                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2019                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2020                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2021                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2022                 {
2023                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2024                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2025                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2026                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2027                         m.texmatrix[1] = rsurface.entitytolight;
2028                 }
2029                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2030         }
2031         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2032         {
2033                 // 2/0/0/2 3D combine blendsquare path
2034                 memset(&m, 0, sizeof(m));
2035                 m.tex[0] = R_GetTexture(normalmaptexture);
2036                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2037                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2038                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2039                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2040                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2041                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2042                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2043                 m.pointer_texcoord_bufferobject[1] = 0;
2044                 m.pointer_texcoord_bufferoffset[1] = 0;
2045                 R_Mesh_TextureState(&m);
2046                 GL_ColorMask(0,0,0,1);
2047                 // this squares the result
2048                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2049                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2050
2051                 // second and third pass
2052                 R_Mesh_ResetTextureState();
2053                 // square alpha in framebuffer a few times to make it shiny
2054                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2055                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2056                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2057
2058                 // fourth pass
2059                 memset(&m, 0, sizeof(m));
2060                 m.tex[0] = R_GetTexture(glosstexture);
2061                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2062                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2063                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2064                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2065                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2066                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2067                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2068                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2069                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2070                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2071         }
2072         else
2073         {
2074                 // 2/0/0/2/2 2D combine blendsquare path
2075                 memset(&m, 0, sizeof(m));
2076                 m.tex[0] = R_GetTexture(normalmaptexture);
2077                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2078                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2079                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2080                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2081                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2082                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2083                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2084                 m.pointer_texcoord_bufferobject[1] = 0;
2085                 m.pointer_texcoord_bufferoffset[1] = 0;
2086                 R_Mesh_TextureState(&m);
2087                 GL_ColorMask(0,0,0,1);
2088                 // this squares the result
2089                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2090                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2091
2092                 // second and third pass
2093                 R_Mesh_ResetTextureState();
2094                 // square alpha in framebuffer a few times to make it shiny
2095                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2096                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2097                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2098
2099                 // fourth pass
2100                 memset(&m, 0, sizeof(m));
2101                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2102                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2103                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2104                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2105                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2106                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2107                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2108                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2109                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2110                 m.texmatrix[1] = rsurface.entitytoattenuationz;
2111                 R_Mesh_TextureState(&m);
2112                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2113                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2114
2115                 // fifth pass
2116                 memset(&m, 0, sizeof(m));
2117                 m.tex[0] = R_GetTexture(glosstexture);
2118                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2119                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2120                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2121                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2122                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2123                 {
2124                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2125                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2126                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2127                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2128                         m.texmatrix[1] = rsurface.entitytolight;
2129                 }
2130                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2131         }
2132         // this final code is shared
2133         R_Mesh_TextureState(&m);
2134         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
2135 }
2136
2137 static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
2138 {
2139         // ARB path (any Geforce, any Radeon)
2140         qboolean doambient = ambientscale > 0;
2141         qboolean dodiffuse = diffusescale > 0;
2142         qboolean dospecular = specularscale > 0;
2143         if (!doambient && !dodiffuse && !dospecular)
2144                 return;
2145         R_Mesh_ColorPointer(NULL, 0, 0);
2146         if (doambient)
2147                 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, ambientscale * r_refdef.view.colorscale);
2148         if (dodiffuse)
2149                 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2150         if (dopants)
2151         {
2152                 if (doambient)
2153                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, ambientscale * r_refdef.view.colorscale);
2154                 if (dodiffuse)
2155                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2156         }
2157         if (doshirt)
2158         {
2159                 if (doambient)
2160                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, ambientscale * r_refdef.view.colorscale);
2161                 if (dodiffuse)
2162                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2163         }
2164         if (dospecular)
2165                 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_refdef.view.colorscale);
2166 }
2167
2168 void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, vec3_t diffusecolor2, vec3_t ambientcolor2)
2169 {
2170         int renders;
2171         int i;
2172         int stop;
2173         int newfirstvertex;
2174         int newlastvertex;
2175         int newnumtriangles;
2176         int *newe;
2177         const int *e;
2178         float *c;
2179         int newelements[4096*3];
2180         R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
2181         for (renders = 0;renders < 64;renders++)
2182         {
2183                 stop = true;
2184                 newfirstvertex = 0;
2185                 newlastvertex = 0;
2186                 newnumtriangles = 0;
2187                 newe = newelements;
2188                 // due to low fillrate on the cards this vertex lighting path is
2189                 // designed for, we manually cull all triangles that do not
2190                 // contain a lit vertex
2191                 // this builds batches of triangles from multiple surfaces and
2192                 // renders them at once
2193                 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
2194                 {
2195                         if (VectorLength2(rsurface.array_color4f + e[0] * 4) + VectorLength2(rsurface.array_color4f + e[1] * 4) + VectorLength2(rsurface.array_color4f + e[2] * 4) >= 0.01)
2196                         {
2197                                 if (newnumtriangles)
2198                                 {
2199                                         newfirstvertex = min(newfirstvertex, e[0]);
2200                                         newlastvertex  = max(newlastvertex, e[0]);
2201                                 }
2202                                 else
2203                                 {
2204                                         newfirstvertex = e[0];
2205                                         newlastvertex = e[0];
2206                                 }
2207                                 newfirstvertex = min(newfirstvertex, e[1]);
2208                                 newlastvertex  = max(newlastvertex, e[1]);
2209                                 newfirstvertex = min(newfirstvertex, e[2]);
2210                                 newlastvertex  = max(newlastvertex, e[2]);
2211                                 newe[0] = e[0];
2212                                 newe[1] = e[1];
2213                                 newe[2] = e[2];
2214                                 newnumtriangles++;
2215                                 newe += 3;
2216                                 if (newnumtriangles >= (int)(sizeof(newelements)/sizeof(float[3])))
2217                                 {
2218                                         R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2219                                         newnumtriangles = 0;
2220                                         newe = newelements;
2221                                         stop = false;
2222                                 }
2223                         }
2224                 }
2225                 if (newnumtriangles >= 1)
2226                 {
2227                         // if all triangles are included, use the original array to take advantage of the bufferobject if possible
2228                         if (newnumtriangles == numtriangles)
2229                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2230                         else
2231                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2232                         stop = false;
2233                 }
2234                 // if we couldn't find any lit triangles, exit early
2235                 if (stop)
2236                         break;
2237                 // now reduce the intensity for the next overbright pass
2238                 // we have to clamp to 0 here incase the drivers have improper
2239                 // handling of negative colors
2240                 // (some old drivers even have improper handling of >1 color)
2241                 stop = true;
2242                 for (i = 0, c = rsurface.array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
2243                 {
2244                         if (c[0] > 1 || c[1] > 1 || c[2] > 1)
2245                         {
2246                                 c[0] = max(0, c[0] - 1);
2247                                 c[1] = max(0, c[1] - 1);
2248                                 c[2] = max(0, c[2] - 1);
2249                                 stop = false;
2250                         }
2251                         else
2252                                 VectorClear(c);
2253                 }
2254                 // another check...
2255                 if (stop)
2256                         break;
2257         }
2258 }
2259
2260 static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, 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 ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
2261 {
2262         // OpenGL 1.1 path (anything)
2263         float ambientcolorbase[3], diffusecolorbase[3];
2264         float ambientcolorpants[3], diffusecolorpants[3];
2265         float ambientcolorshirt[3], diffusecolorshirt[3];
2266         rmeshstate_t m;
2267         VectorScale(lightcolorbase, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorbase);
2268         VectorScale(lightcolorbase, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorbase);
2269         VectorScale(lightcolorpants, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorpants);
2270         VectorScale(lightcolorpants, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorpants);
2271         VectorScale(lightcolorshirt, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorshirt);
2272         VectorScale(lightcolorshirt, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorshirt);
2273         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2274         R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
2275         memset(&m, 0, sizeof(m));
2276         m.tex[0] = R_GetTexture(basetexture);
2277         m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2278         m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2279         m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2280         m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2281         if (r_textureunits.integer >= 2)
2282         {
2283                 // voodoo2 or TNT
2284                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2285                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2286                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2287                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2288                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2289                 if (r_textureunits.integer >= 3)
2290                 {
2291                         // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2292                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2293                         m.texmatrix[2] = rsurface.entitytoattenuationz;
2294                         m.pointer_texcoord3f[2] = rsurface.vertex3f;
2295                         m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
2296                         m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
2297                 }
2298         }
2299         R_Mesh_TextureState(&m);
2300         //R_Mesh_TexBind(0, R_GetTexture(basetexture));
2301         R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorbase, ambientcolorbase);
2302         if (dopants)
2303         {
2304                 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2305                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorpants, ambientcolorpants);
2306         }
2307         if (doshirt)
2308         {
2309                 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2310                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorshirt, ambientcolorshirt);
2311         }
2312 }
2313
2314 extern cvar_t gl_lightmaps;
2315 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset)
2316 {
2317         float ambientscale, diffusescale, specularscale;
2318         vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2319         rtexture_t *nmap;
2320         // calculate colors to render this texture with
2321         lightcolorbase[0] = rsurface.rtlight->currentcolor[0] * rsurface.texture->dlightcolor[0];
2322         lightcolorbase[1] = rsurface.rtlight->currentcolor[1] * rsurface.texture->dlightcolor[1];
2323         lightcolorbase[2] = rsurface.rtlight->currentcolor[2] * rsurface.texture->dlightcolor[2];
2324         ambientscale = rsurface.rtlight->ambientscale;
2325         diffusescale = rsurface.rtlight->diffusescale;
2326         specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
2327         if (!r_shadow_usenormalmap.integer)
2328         {
2329                 ambientscale += 1.0f * diffusescale;
2330                 diffusescale = 0;
2331                 specularscale = 0;
2332         }
2333         if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2334                 return;
2335         RSurf_SetupDepthAndCulling();
2336         nmap = rsurface.texture->currentskinframe->nmap;
2337         if (gl_lightmaps.integer)
2338                 nmap = r_texture_blanknormalmap;
2339         if (rsurface.texture->colormapping && !gl_lightmaps.integer)
2340         {
2341                 qboolean dopants = rsurface.texture->currentskinframe->pants != NULL && VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f);
2342                 qboolean doshirt = rsurface.texture->currentskinframe->shirt != NULL && VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
2343                 if (dopants)
2344                 {
2345                         lightcolorpants[0] = lightcolorbase[0] * rsurface.colormap_pantscolor[0];
2346                         lightcolorpants[1] = lightcolorbase[1] * rsurface.colormap_pantscolor[1];
2347                         lightcolorpants[2] = lightcolorbase[2] * rsurface.colormap_pantscolor[2];
2348                 }
2349                 else
2350                         VectorClear(lightcolorpants);
2351                 if (doshirt)
2352                 {
2353                         lightcolorshirt[0] = lightcolorbase[0] * rsurface.colormap_shirtcolor[0];
2354                         lightcolorshirt[1] = lightcolorbase[1] * rsurface.colormap_shirtcolor[1];
2355                         lightcolorshirt[2] = lightcolorbase[2] * rsurface.colormap_shirtcolor[2];
2356                 }
2357                 else
2358                         VectorClear(lightcolorshirt);
2359                 switch (r_shadow_rendermode)
2360                 {
2361                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2362                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2363                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2364                         break;
2365                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2366                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2367                         break;
2368                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2369                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2370                         break;
2371                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2372                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2373                         break;
2374                 default:
2375                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2376                         break;
2377                 }
2378         }
2379         else
2380         {
2381                 switch (r_shadow_rendermode)
2382                 {
2383                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2384                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2385                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2386                         break;
2387                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2388                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2389                         break;
2390                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2391                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2392                         break;
2393                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2394                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2395                         break;
2396                 default:
2397                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2398                         break;
2399                 }
2400         }
2401 }
2402
2403 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)
2404 {
2405         matrix4x4_t tempmatrix = *matrix;
2406         Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2407
2408         // if this light has been compiled before, free the associated data
2409         R_RTLight_Uncompile(rtlight);
2410
2411         // clear it completely to avoid any lingering data
2412         memset(rtlight, 0, sizeof(*rtlight));
2413
2414         // copy the properties
2415         rtlight->matrix_lighttoworld = tempmatrix;
2416         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2417         Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2418         rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2419         VectorCopy(color, rtlight->color);
2420         rtlight->cubemapname[0] = 0;
2421         if (cubemapname && cubemapname[0])
2422                 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2423         rtlight->shadow = shadow;
2424         rtlight->corona = corona;
2425         rtlight->style = style;
2426         rtlight->isstatic = isstatic;
2427         rtlight->coronasizescale = coronasizescale;
2428         rtlight->ambientscale = ambientscale;
2429         rtlight->diffusescale = diffusescale;
2430         rtlight->specularscale = specularscale;
2431         rtlight->flags = flags;
2432
2433         // compute derived data
2434         //rtlight->cullradius = rtlight->radius;
2435         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2436         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2437         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2438         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2439         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2440         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2441         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2442 }
2443
2444 // compiles rtlight geometry
2445 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2446 void R_RTLight_Compile(rtlight_t *rtlight)
2447 {
2448         int i;
2449         int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2450         int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2451         entity_render_t *ent = r_refdef.scene.worldentity;
2452         model_t *model = r_refdef.scene.worldmodel;
2453         unsigned char *data;
2454
2455         // compile the light
2456         rtlight->compiled = true;
2457         rtlight->static_numleafs = 0;
2458         rtlight->static_numleafpvsbytes = 0;
2459         rtlight->static_leaflist = NULL;
2460         rtlight->static_leafpvs = NULL;
2461         rtlight->static_numsurfaces = 0;
2462         rtlight->static_surfacelist = NULL;
2463         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2464         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2465         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2466         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2467         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2468         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2469
2470         if (model && model->GetLightInfo)
2471         {
2472                 // this variable must be set for the CompileShadowVolume code
2473                 r_shadow_compilingrtlight = rtlight;
2474                 R_Shadow_EnlargeLeafSurfaceTrisBuffer(model->brush.num_leafs, model->num_surfaces, model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles, model->surfmesh.num_triangles);
2475                 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, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2476                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2477                 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2478                 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2479                 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2480                 rtlight->static_numsurfaces = numsurfaces;
2481                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2482                 rtlight->static_numleafs = numleafs;
2483                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2484                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2485                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2486                 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2487                 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2488                 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2489                 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2490                 if (rtlight->static_numsurfaces)
2491                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2492                 if (rtlight->static_numleafs)
2493                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2494                 if (rtlight->static_numleafpvsbytes)
2495                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2496                 if (rtlight->static_numshadowtrispvsbytes)
2497                         memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2498                 if (rtlight->static_numlighttrispvsbytes)
2499                         memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2500                 if (model->CompileShadowVolume && rtlight->shadow)
2501                         model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2502                 // now we're done compiling the rtlight
2503                 r_shadow_compilingrtlight = NULL;
2504         }
2505
2506
2507         // use smallest available cullradius - box radius or light radius
2508         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2509         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2510
2511         shadowmeshes = 0;
2512         shadowmeshtris = 0;
2513         if (rtlight->static_meshchain_shadow)
2514         {
2515                 shadowmesh_t *mesh;
2516                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2517                 {
2518                         shadowmeshes++;
2519                         shadowmeshtris += mesh->numtriangles;
2520                 }
2521         }
2522
2523         lighttris = 0;
2524         if (rtlight->static_numlighttrispvsbytes)
2525                 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2526                         if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2527                                 lighttris++;
2528
2529         shadowtris = 0;
2530         if (rtlight->static_numlighttrispvsbytes)
2531                 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2532                         if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2533                                 shadowtris++;
2534
2535         if (developer.integer >= 10)
2536                 Con_Printf("static light built: %f %f %f : %f %f %f box, %i light triangles, %i shadow triangles, %i compiled 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], lighttris, shadowtris, shadowmeshtris, shadowmeshes);
2537 }
2538
2539 void R_RTLight_Uncompile(rtlight_t *rtlight)
2540 {
2541         if (rtlight->compiled)
2542         {
2543                 if (rtlight->static_meshchain_shadow)
2544                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2545                 rtlight->static_meshchain_shadow = NULL;
2546                 // these allocations are grouped
2547                 if (rtlight->static_surfacelist)
2548                         Mem_Free(rtlight->static_surfacelist);
2549                 rtlight->static_numleafs = 0;
2550                 rtlight->static_numleafpvsbytes = 0;
2551                 rtlight->static_leaflist = NULL;
2552                 rtlight->static_leafpvs = NULL;
2553                 rtlight->static_numsurfaces = 0;
2554                 rtlight->static_surfacelist = NULL;
2555                 rtlight->static_numshadowtrispvsbytes = 0;
2556                 rtlight->static_shadowtrispvs = NULL;
2557                 rtlight->static_numlighttrispvsbytes = 0;
2558                 rtlight->static_lighttrispvs = NULL;
2559                 rtlight->compiled = false;
2560         }
2561 }
2562
2563 void R_Shadow_UncompileWorldLights(void)
2564 {
2565         size_t lightindex;
2566         dlight_t *light;
2567         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
2568         {
2569                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
2570                 if (!light)
2571                         continue;
2572                 R_RTLight_Uncompile(&light->rtlight);
2573         }
2574 }
2575
2576 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
2577 {
2578         int i, j;
2579         mplane_t plane;
2580         // reset the count of frustum planes
2581         // see rsurface.rtlight_frustumplanes definition for how much this array
2582         // can hold
2583         rsurface.rtlight_numfrustumplanes = 0;
2584
2585         // haven't implemented a culling path for ortho rendering
2586         if (!r_refdef.view.useperspective)
2587         {
2588                 // check if the light is on screen and copy the 4 planes if it is
2589                 for (i = 0;i < 4;i++)
2590                         if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2591                                 break;
2592                 if (i == 4)
2593                         for (i = 0;i < 4;i++)
2594                                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2595                 return;
2596         }
2597
2598 #if 1
2599         // generate a deformed frustum that includes the light origin, this is
2600         // used to cull shadow casting surfaces that can not possibly cast a
2601         // shadow onto the visible light-receiving surfaces, which can be a
2602         // performance gain
2603         //
2604         // if the light origin is onscreen the result will be 4 planes exactly
2605         // if the light origin is offscreen on only one axis the result will
2606         // be exactly 5 planes (split-side case)
2607         // if the light origin is offscreen on two axes the result will be
2608         // exactly 4 planes (stretched corner case)
2609         for (i = 0;i < 4;i++)
2610         {
2611                 // quickly reject standard frustum planes that put the light
2612                 // origin outside the frustum
2613                 if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2614                         continue;
2615                 // copy the plane
2616                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2617         }
2618         // if all the standard frustum planes were accepted, the light is onscreen
2619         // otherwise we need to generate some more planes below...
2620         if (rsurface.rtlight_numfrustumplanes < 4)
2621         {
2622                 // at least one of the stock frustum planes failed, so we need to
2623                 // create one or two custom planes to enclose the light origin
2624                 for (i = 0;i < 4;i++)
2625                 {
2626                         // create a plane using the view origin and light origin, and a
2627                         // single point from the frustum corner set
2628                         TriangleNormal(r_refdef.view.origin, r_refdef.view.frustumcorner[i], rtlight->shadoworigin, plane.normal);
2629                         VectorNormalize(plane.normal);
2630                         plane.dist = DotProduct(r_refdef.view.origin, plane.normal);
2631                         // see if this plane is backwards and flip it if so
2632                         for (j = 0;j < 4;j++)
2633                                 if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2634                                         break;
2635                         if (j < 4)
2636                         {
2637                                 VectorNegate(plane.normal, plane.normal);
2638                                 plane.dist *= -1;
2639                                 // flipped plane, test again to see if it is now valid
2640                                 for (j = 0;j < 4;j++)
2641                                         if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2642                                                 break;
2643                                 // if the plane is still not valid, then it is dividing the
2644                                 // frustum and has to be rejected
2645                                 if (j < 4)
2646                                         continue;
2647                         }
2648                         // we have created a valid plane, compute extra info
2649                         PlaneClassify(&plane);
2650                         // copy the plane
2651                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2652 #if 1
2653                         // if we've found 5 frustum planes then we have constructed a
2654                         // proper split-side case and do not need to keep searching for
2655                         // planes to enclose the light origin
2656                         if (rsurface.rtlight_numfrustumplanes == 5)
2657                                 break;
2658 #endif
2659                 }
2660         }
2661 #endif
2662
2663 #if 0
2664         for (i = 0;i < rsurface.rtlight_numfrustumplanes;i++)
2665         {
2666                 plane = rsurface.rtlight_frustumplanes[i];
2667                 Con_Printf("light %p plane #%i %f %f %f : %f (%f %f %f %f %f)\n", rtlight, i, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist, PlaneDiff(r_refdef.view.frustumcorner[0], &plane), PlaneDiff(r_refdef.view.frustumcorner[1], &plane), PlaneDiff(r_refdef.view.frustumcorner[2], &plane), PlaneDiff(r_refdef.view.frustumcorner[3], &plane), PlaneDiff(rtlight->shadoworigin, &plane));
2668         }
2669 #endif
2670
2671 #if 0
2672         // now add the light-space box planes if the light box is rotated, as any
2673         // caster outside the oriented light box is irrelevant (even if it passed
2674         // the worldspace light box, which is axial)
2675         if (rtlight->matrix_lighttoworld.m[0][0] != 1 || rtlight->matrix_lighttoworld.m[1][1] != 1 || rtlight->matrix_lighttoworld.m[2][2] != 1)
2676         {
2677                 for (i = 0;i < 6;i++)
2678                 {
2679                         vec3_t v;
2680                         VectorClear(v);
2681                         v[i >> 1] = (i & 1) ? -1 : 1;
2682                         Matrix4x4_Transform(&rtlight->matrix_lighttoworld, v, plane.normal);
2683                         VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
2684                         plane.dist = VectorNormalizeLength(plane.normal);
2685                         plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
2686                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2687                 }
2688         }
2689 #endif
2690
2691 #if 0
2692         // add the world-space reduced box planes
2693         for (i = 0;i < 6;i++)
2694         {
2695                 VectorClear(plane.normal);
2696                 plane.normal[i >> 1] = (i & 1) ? -1 : 1;
2697                 plane.dist = (i & 1) ? -rsurface.rtlight_cullmaxs[i >> 1] : rsurface.rtlight_cullmins[i >> 1];
2698                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2699         }
2700 #endif
2701
2702 #if 0
2703         {
2704         int j, oldnum;
2705         vec3_t points[8];
2706         vec_t bestdist;
2707         // reduce all plane distances to tightly fit the rtlight cull box, which
2708         // is in worldspace
2709         VectorSet(points[0], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2710         VectorSet(points[1], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2711         VectorSet(points[2], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2712         VectorSet(points[3], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2713         VectorSet(points[4], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2714         VectorSet(points[5], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2715         VectorSet(points[6], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2716         VectorSet(points[7], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2717         oldnum = rsurface.rtlight_numfrustumplanes;
2718         rsurface.rtlight_numfrustumplanes = 0;
2719         for (j = 0;j < oldnum;j++)
2720         {
2721                 // find the nearest point on the box to this plane
2722                 bestdist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[0]);
2723                 for (i = 1;i < 8;i++)
2724                 {
2725                         dist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[i]);
2726                         if (bestdist > dist)
2727                                 bestdist = dist;
2728                 }
2729                 Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, rsurface.rtlight_frustumplanes[j].normal[0], rsurface.rtlight_frustumplanes[j].normal[1], rsurface.rtlight_frustumplanes[j].normal[2], rsurface.rtlight_frustumplanes[j].dist, bestdist);
2730                 // if the nearest point is near or behind the plane, we want this
2731                 // plane, otherwise the plane is useless as it won't cull anything
2732                 if (rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125)
2733                 {
2734                         PlaneClassify(&rsurface.rtlight_frustumplanes[j]);
2735                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = rsurface.rtlight_frustumplanes[j];
2736                 }
2737         }
2738         }
2739 #endif
2740 }
2741
2742 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2743 {
2744         RSurf_ActiveWorldEntity();
2745         if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2746         {
2747                 shadowmesh_t *mesh;
2748                 CHECKGLERROR
2749                 for (mesh = rsurface.rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2750                 {
2751                         r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2752                         R_Mesh_VertexPointer(mesh->vertex3f, mesh->vbo, mesh->vbooffset_vertex3f);
2753                         GL_LockArrays(0, mesh->numverts);
2754                         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2755                         {
2756                                 // decrement stencil if backface is behind depthbuffer
2757                                 GL_CullFace(r_refdef.view.cullface_front);
2758                                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2759                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2760                                 // increment stencil if frontface is behind depthbuffer
2761                                 GL_CullFace(r_refdef.view.cullface_back);
2762                                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2763                         }
2764                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2765                         GL_LockArrays(0, 0);
2766                 }
2767                 CHECKGLERROR
2768         }
2769         else if (numsurfaces && r_refdef.scene.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2770         {
2771                 int t, tend;
2772                 int surfacelistindex;
2773                 msurface_t *surface;
2774                 R_Shadow_PrepareShadowMark(r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles);
2775                 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2776                 {
2777                         surface = r_refdef.scene.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2778                         for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2779                                 if (CHECKPVSBIT(trispvs, t))
2780                                         shadowmarklist[numshadowmark++] = t;
2781                 }
2782                 R_Shadow_VolumeFromList(r_refdef.scene.worldmodel->brush.shadowmesh->numverts, r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles, r_refdef.scene.worldmodel->brush.shadowmesh->vertex3f, r_refdef.scene.worldmodel->brush.shadowmesh->element3i, r_refdef.scene.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.scene.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
2783         }
2784         else if (numsurfaces)
2785                 r_refdef.scene.worldmodel->DrawShadowVolume(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
2786 }
2787
2788 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2789 {
2790         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2791         vec_t relativeshadowradius;
2792         RSurf_ActiveModelEntity(ent, false, false);
2793         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, relativeshadoworigin);
2794         relativeshadowradius = rsurface.rtlight->radius / ent->scale;
2795         relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2796         relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2797         relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2798         relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2799         relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2800         relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2801         ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2802 }
2803
2804 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2805 {
2806         // set up properties for rendering light onto this entity
2807         RSurf_ActiveModelEntity(ent, true, true);
2808         Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
2809         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2810         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2811         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2812         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2813                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2814 }
2815
2816 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2817 {
2818         if (!r_refdef.scene.worldmodel->DrawLight)
2819                 return;
2820
2821         // set up properties for rendering light onto this entity
2822         RSurf_ActiveWorldEntity();
2823         rsurface.entitytolight = rsurface.rtlight->matrix_worldtolight;
2824         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2825         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2826         VectorCopy(rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2827         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2828                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2829
2830         r_refdef.scene.worldmodel->DrawLight(r_refdef.scene.worldentity, numsurfaces, surfacelist, trispvs);
2831 }
2832
2833 void R_Shadow_DrawEntityLight(entity_render_t *ent)
2834 {
2835         model_t *model = ent->model;
2836         if (!model->DrawLight)
2837                 return;
2838
2839         R_Shadow_SetupEntityLight(ent);
2840
2841         model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2842 }
2843
2844 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2845 {
2846         int i;
2847         float f;
2848         int numleafs, numsurfaces;
2849         int *leaflist, *surfacelist;
2850         unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2851         int numlightentities;
2852         int numlightentities_noselfshadow;
2853         int numshadowentities;
2854         int numshadowentities_noselfshadow;
2855         entity_render_t *lightentities[MAX_EDICTS];
2856         entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
2857         entity_render_t *shadowentities[MAX_EDICTS];
2858         entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
2859
2860         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2861         // skip lights that are basically invisible (color 0 0 0)
2862         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2863                 return;
2864
2865         // loading is done before visibility checks because loading should happen
2866         // all at once at the start of a level, not when it stalls gameplay.
2867         // (especially important to benchmarks)
2868         // compile light
2869         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2870                 R_RTLight_Compile(rtlight);
2871         // load cubemap
2872         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2873
2874         // look up the light style value at this time
2875         f = (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
2876         VectorScale(rtlight->color, f, rtlight->currentcolor);
2877         /*
2878         if (rtlight->selected)
2879         {
2880                 f = 2 + sin(realtime * M_PI * 4.0);
2881                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2882         }
2883         */
2884
2885         // if lightstyle is currently off, don't draw the light
2886         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2887                 return;
2888
2889         // if the light box is offscreen, skip it
2890         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2891                 return;
2892
2893         VectorCopy(rtlight->cullmins, rsurface.rtlight_cullmins);
2894         VectorCopy(rtlight->cullmaxs, rsurface.rtlight_cullmaxs);
2895
2896         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2897         {
2898                 // compiled light, world available and can receive realtime lighting
2899                 // retrieve leaf information
2900                 numleafs = rtlight->static_numleafs;
2901                 leaflist = rtlight->static_leaflist;
2902                 leafpvs = rtlight->static_leafpvs;
2903                 numsurfaces = rtlight->static_numsurfaces;
2904                 surfacelist = rtlight->static_surfacelist;
2905                 shadowtrispvs = rtlight->static_shadowtrispvs;
2906                 lighttrispvs = rtlight->static_lighttrispvs;
2907         }
2908         else if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->GetLightInfo)
2909         {
2910                 // dynamic light, world available and can receive realtime lighting
2911                 // calculate lit surfaces and leafs
2912                 R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.scene.worldmodel->brush.num_leafs, r_refdef.scene.worldmodel->num_surfaces, r_refdef.scene.worldmodel->brush.shadowmesh ? r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles : r_refdef.scene.worldmodel->surfmesh.num_triangles, r_refdef.scene.worldmodel->surfmesh.num_triangles);
2913                 r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.worldentity, rtlight->shadoworigin, rtlight->radius, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2914                 leaflist = r_shadow_buffer_leaflist;
2915                 leafpvs = r_shadow_buffer_leafpvs;
2916                 surfacelist = r_shadow_buffer_surfacelist;
2917                 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2918                 lighttrispvs = r_shadow_buffer_lighttrispvs;
2919                 // if the reduced leaf bounds are offscreen, skip it
2920                 if (R_CullBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2921                         return;
2922         }
2923         else
2924         {
2925                 // no world
2926                 numleafs = 0;
2927                 leaflist = NULL;
2928                 leafpvs = NULL;
2929                 numsurfaces = 0;
2930                 surfacelist = NULL;
2931                 shadowtrispvs = NULL;
2932                 lighttrispvs = NULL;
2933         }
2934         // check if light is illuminating any visible leafs
2935         if (numleafs)
2936         {
2937                 for (i = 0;i < numleafs;i++)
2938                         if (r_refdef.viewcache.world_leafvisible[leaflist[i]])
2939                                 break;
2940                 if (i == numleafs)
2941                         return;
2942         }
2943         // set up a scissor rectangle for this light
2944         if (R_Shadow_ScissorForBBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2945                 return;
2946
2947         R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
2948
2949         // make a list of lit entities and shadow casting entities
2950         numlightentities = 0;
2951         numlightentities_noselfshadow = 0;
2952         numshadowentities = 0;
2953         numshadowentities_noselfshadow = 0;
2954         // add dynamic entities that are lit by the light
2955         if (r_drawentities.integer)
2956         {
2957                 for (i = 0;i < r_refdef.scene.numentities;i++)
2958                 {
2959                         model_t *model;
2960                         entity_render_t *ent = r_refdef.scene.entities[i];
2961                         vec3_t org;
2962                         if (!BoxesOverlap(ent->mins, ent->maxs, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2963                                 continue;
2964                         // skip the object entirely if it is not within the valid
2965                         // shadow-casting region (which includes the lit region)
2966                         if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rsurface.rtlight_numfrustumplanes, rsurface.rtlight_frustumplanes))
2967                                 continue;
2968                         if (!(model = ent->model))
2969                                 continue;
2970                         if (r_refdef.viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2971                         {
2972                                 // this entity wants to receive light, is visible, and is
2973                                 // inside the light box
2974                                 // TODO: check if the surfaces in the model can receive light
2975                                 // so now check if it's in a leaf seen by the light
2976                                 if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
2977                                         continue;
2978                                 if (ent->flags & RENDER_NOSELFSHADOW)
2979                                         lightentities_noselfshadow[numlightentities_noselfshadow++] = ent;
2980                                 else
2981                                         lightentities[numlightentities++] = ent;
2982                                 // since it is lit, it probably also casts a shadow...
2983                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2984                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2985                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2986                                 {
2987                                         // note: exterior models without the RENDER_NOSELFSHADOW
2988                                         // flag still create a RENDER_NOSELFSHADOW shadow but
2989                                         // are lit normally, this means that they are
2990                                         // self-shadowing but do not shadow other
2991                                         // RENDER_NOSELFSHADOW entities such as the gun
2992                                         // (very weird, but keeps the player shadow off the gun)
2993                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
2994                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
2995                                         else
2996                                                 shadowentities[numshadowentities++] = ent;
2997                                 }
2998                         }
2999                         else if (ent->flags & RENDER_SHADOW)
3000                         {
3001                                 // this entity is not receiving light, but may still need to
3002                                 // cast a shadow...
3003                                 // TODO: check if the surfaces in the model can cast shadow
3004                                 // now check if it is in a leaf seen by the light
3005                                 if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
3006                                         continue;
3007                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
3008                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
3009                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
3010                                 {
3011                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
3012                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
3013                                         else
3014                                                 shadowentities[numshadowentities++] = ent;
3015                                 }
3016                         }
3017                 }
3018         }
3019
3020         // return if there's nothing at all to light
3021         if (!numlightentities && !numsurfaces)
3022                 return;
3023
3024         // don't let sound skip if going slow
3025         if (r_refdef.scene.extraupdate)
3026                 S_ExtraUpdate ();
3027
3028         // make this the active rtlight for rendering purposes
3029         R_Shadow_RenderMode_ActiveLight(rtlight);
3030         // count this light in the r_speeds
3031         r_refdef.stats.lights++;
3032
3033         if (r_showshadowvolumes.integer && r_refdef.view.showdebug && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3034         {
3035                 // optionally draw visible shape of the shadow volumes
3036                 // for performance analysis by level designers
3037                 R_Shadow_RenderMode_VisibleShadowVolumes();
3038                 if (numsurfaces)
3039                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3040                 for (i = 0;i < numshadowentities;i++)
3041                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3042                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3043                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3044         }
3045
3046         if (gl_stencil && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3047         {
3048                 // draw stencil shadow volumes to mask off pixels that are in shadow
3049                 // so that they won't receive lighting
3050                 R_Shadow_RenderMode_StencilShadowVolumes(true);
3051                 if (numsurfaces)
3052                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3053                 for (i = 0;i < numshadowentities;i++)
3054                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3055                 if (numlightentities_noselfshadow)
3056                 {
3057                         // draw lighting in the unmasked areas
3058                         R_Shadow_RenderMode_Lighting(true, false);
3059                         for (i = 0;i < numlightentities_noselfshadow;i++)
3060                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3061
3062                         // optionally draw the illuminated areas
3063                         // for performance analysis by level designers
3064                         if (r_showlighting.integer && r_refdef.view.showdebug)
3065                         {
3066                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3067                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3068                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3069                         }
3070
3071                         R_Shadow_RenderMode_StencilShadowVolumes(false);
3072                 }
3073                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3074                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3075
3076                 if (numsurfaces + numlightentities)
3077                 {
3078                         // draw lighting in the unmasked areas
3079                         R_Shadow_RenderMode_Lighting(true, false);
3080                         if (numsurfaces)
3081                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3082                         for (i = 0;i < numlightentities;i++)
3083                                 R_Shadow_DrawEntityLight(lightentities[i]);
3084
3085                         // optionally draw the illuminated areas
3086                         // for performance analysis by level designers
3087                         if (r_showlighting.integer && r_refdef.view.showdebug)
3088                         {
3089                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3090                                 if (numsurfaces)
3091                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3092                                 for (i = 0;i < numlightentities;i++)
3093                                         R_Shadow_DrawEntityLight(lightentities[i]);
3094                         }
3095                 }
3096         }
3097         else
3098         {
3099                 if (numsurfaces + numlightentities)
3100                 {
3101                         // draw lighting in the unmasked areas
3102                         R_Shadow_RenderMode_Lighting(false, false);
3103                         if (numsurfaces)
3104                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3105                         for (i = 0;i < numlightentities;i++)
3106                                 R_Shadow_DrawEntityLight(lightentities[i]);
3107                         for (i = 0;i < numlightentities_noselfshadow;i++)
3108                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3109
3110                         // optionally draw the illuminated areas
3111                         // for performance analysis by level designers
3112                         if (r_showlighting.integer && r_refdef.view.showdebug)
3113                         {
3114                                 R_Shadow_RenderMode_VisibleLighting(false, false);
3115                                 if (numsurfaces)
3116                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3117                                 for (i = 0;i < numlightentities;i++)
3118                                         R_Shadow_DrawEntityLight(lightentities[i]);
3119                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3120                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3121                         }
3122                 }
3123         }
3124 }
3125
3126 void R_Shadow_DrawLightSprites(void);
3127 void R_ShadowVolumeLighting(qboolean visible)
3128 {
3129         int flag;
3130         int lnum;
3131         size_t lightindex;
3132         dlight_t *light;
3133
3134         if (r_editlights.integer)
3135                 R_Shadow_DrawLightSprites();
3136
3137         R_Shadow_RenderMode_Begin();
3138
3139         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3140         if (r_shadow_debuglight.integer >= 0)
3141         {
3142                 lightindex = r_shadow_debuglight.integer;
3143                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3144                 if (light && (light->flags & flag))
3145                         R_DrawRTLight(&light->rtlight, visible);
3146         }
3147         else
3148         {
3149                 for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3150                 {
3151                         light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3152                         if (light && (light->flags & flag))
3153                                 R_DrawRTLight(&light->rtlight, visible);
3154                 }
3155         }
3156         if (r_refdef.rtdlight)
3157                 for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++)
3158                         R_DrawRTLight(&r_refdef.scene.lights[lnum], visible);
3159
3160         R_Shadow_RenderMode_End();
3161 }
3162
3163 extern void R_SetupView(qboolean allowwaterclippingplane);
3164 extern cvar_t r_shadows_throwdistance;
3165 void R_DrawModelShadows(void)
3166 {
3167         int i;
3168         float relativethrowdistance;
3169         entity_render_t *ent;
3170         vec3_t relativelightorigin;
3171         vec3_t relativelightdirection;
3172         vec3_t relativeshadowmins, relativeshadowmaxs;
3173         float vertex3f[12];
3174
3175         if (!r_drawentities.integer || !gl_stencil)
3176                 return;
3177
3178         CHECKGLERROR
3179         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3180
3181         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
3182
3183         if (gl_ext_separatestencil.integer)
3184                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
3185         else if (gl_ext_stenciltwoside.integer)
3186                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
3187         else
3188                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
3189
3190         R_Shadow_RenderMode_StencilShadowVolumes(true);
3191
3192         for (i = 0;i < r_refdef.scene.numentities;i++)
3193         {
3194                 ent = r_refdef.scene.entities[i];
3195                 // cast shadows from anything that is not a submodel of the map
3196                 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
3197                 {
3198                         relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
3199                         VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
3200                         VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
3201                         VectorNegate(ent->modellight_lightdir, relativelightdirection);
3202                         VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
3203                         RSurf_ActiveModelEntity(ent, false, false);
3204                         ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
3205                 }
3206         }
3207
3208         // not really the right mode, but this will disable any silly stencil features
3209         R_Shadow_RenderMode_VisibleLighting(true, true);
3210
3211         // vertex coordinates for a quad that covers the screen exactly
3212         vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
3213         vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
3214         vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
3215         vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
3216
3217         // set up ortho view for rendering this pass
3218         GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
3219         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3220         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3221         GL_ScissorTest(true);
3222         R_Mesh_Matrix(&identitymatrix);
3223         R_Mesh_ResetTextureState();
3224         R_Mesh_VertexPointer(vertex3f, 0, 0);
3225         R_Mesh_ColorPointer(NULL, 0, 0);
3226
3227         // set up a 50% darkening blend on shadowed areas
3228         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3229         GL_DepthRange(0, 1);
3230         GL_DepthTest(false);
3231         GL_DepthMask(false);
3232         GL_PolygonOffset(0, 0);CHECKGLERROR
3233         GL_Color(0, 0, 0, 0.5);
3234         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3235         qglDepthFunc(GL_ALWAYS);CHECKGLERROR
3236         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
3237         qglStencilMask(~0);CHECKGLERROR
3238         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
3239         qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
3240
3241         // apply the blend to the shadowed areas
3242         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
3243
3244         // restoring the perspective view is done by R_RenderScene
3245         //R_SetupView(true);
3246
3247         // restore other state to normal
3248         R_Shadow_RenderMode_End();
3249 }
3250
3251 void R_DrawCoronas(void)
3252 {
3253         int i, flag;
3254         float cscale, scale;
3255         size_t lightindex;
3256         dlight_t *light;
3257         rtlight_t *rtlight;
3258         if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
3259                 return;
3260         R_Mesh_Matrix(&identitymatrix);
3261         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3262         // FIXME: these traces should scan all render entities instead of cl.world
3263         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3264         {
3265                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3266                 if (!light)
3267                         continue;
3268                 rtlight = &light->rtlight;
3269                 if (!(rtlight->flags & flag))
3270                         continue;
3271                 if (rtlight->corona * r_coronas.value <= 0)
3272                         continue;
3273                 if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex)
3274                         continue;
3275                 cscale = rtlight->corona * r_coronas.value* 0.25f;
3276                 scale = rtlight->radius * rtlight->coronasizescale;
3277                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 16.0f * 16.0f)
3278                         continue;
3279                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3280                         continue;
3281                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3282         }
3283         for (i = 0;i < r_refdef.scene.numlights;i++)
3284         {
3285                 rtlight = &r_refdef.scene.lights[i];
3286                 if (!(rtlight->flags & flag))
3287                         continue;
3288                 if (rtlight->corona <= 0)
3289                         continue;
3290                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f)
3291                         continue;
3292                 if (gl_flashblend.integer)
3293                 {
3294                         cscale = rtlight->corona * 1.0f;
3295                         scale = rtlight->radius * rtlight->coronasizescale * 2.0f;
3296                 }
3297                 else
3298                 {
3299                         cscale = rtlight->corona * r_coronas.value* 0.25f;
3300                         scale = rtlight->radius * rtlight->coronasizescale;
3301                 }
3302                 if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f))
3303                         continue;
3304                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3305                         continue;
3306                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3307         }
3308 }
3309
3310
3311
3312 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
3313 typedef struct suffixinfo_s
3314 {
3315         char *suffix;
3316         qboolean flipx, flipy, flipdiagonal;
3317 }
3318 suffixinfo_t;
3319 static suffixinfo_t suffix[3][6] =
3320 {
3321         {
3322                 {"px",   false, false, false},
3323                 {"nx",   false, false, false},
3324                 {"py",   false, false, false},
3325                 {"ny",   false, false, false},
3326                 {"pz",   false, false, false},
3327                 {"nz",   false, false, false}
3328         },
3329         {
3330                 {"posx", false, false, false},
3331                 {"negx", false, false, false},
3332                 {"posy", false, false, false},
3333                 {"negy", false, false, false},
3334                 {"posz", false, false, false},
3335                 {"negz", false, false, false}
3336         },
3337         {
3338                 {"rt",    true, false,  true},
3339                 {"lf",   false,  true,  true},
3340                 {"ft",    true,  true, false},
3341                 {"bk",   false, false, false},
3342                 {"up",    true, false,  true},
3343                 {"dn",    true, false,  true}
3344         }
3345 };
3346
3347 static int componentorder[4] = {0, 1, 2, 3};
3348
3349 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
3350 {
3351         int i, j, cubemapsize;
3352         unsigned char *cubemappixels, *image_buffer;
3353         rtexture_t *cubemaptexture;
3354         char name[256];
3355         // must start 0 so the first loadimagepixels has no requested width/height
3356         cubemapsize = 0;
3357         cubemappixels = NULL;
3358         cubemaptexture = NULL;
3359         // keep trying different suffix groups (posx, px, rt) until one loads
3360         for (j = 0;j < 3 && !cubemappixels;j++)
3361         {
3362                 // load the 6 images in the suffix group
3363                 for (i = 0;i < 6;i++)
3364                 {
3365                         // generate an image name based on the base and and suffix
3366                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3367                         // load it
3368                         if ((image_buffer = loadimagepixelsbgra(name, false, false)))
3369                         {
3370                                 // an image loaded, make sure width and height are equal
3371                                 if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
3372                                 {
3373                                         // if this is the first image to load successfully, allocate the cubemap memory
3374                                         if (!cubemappixels && image_width >= 1)
3375                                         {
3376                                                 cubemapsize = image_width;
3377                                                 // note this clears to black, so unavailable sides are black
3378                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3379                                         }
3380                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3381                                         if (cubemappixels)
3382                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_buffer, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
3383                                 }
3384                                 else
3385                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3386                                 // free the image
3387                                 Mem_Free(image_buffer);
3388                         }
3389                 }
3390         }
3391         // if a cubemap loaded, upload it
3392         if (cubemappixels)
3393         {
3394                 if (!r_shadow_filters_texturepool)
3395                         r_shadow_filters_texturepool = R_AllocTexturePool();
3396                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
3397                 Mem_Free(cubemappixels);
3398         }
3399         else
3400         {
3401                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3402                 for (j = 0;j < 3;j++)
3403                         for (i = 0;i < 6;i++)
3404                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3405                 Con_Print(" and was unable to find any of them.\n");
3406         }
3407         return cubemaptexture;
3408 }
3409
3410 rtexture_t *R_Shadow_Cubemap(const char *basename)
3411 {
3412         int i;
3413         for (i = 0;i < numcubemaps;i++)
3414                 if (!strcasecmp(cubemaps[i].basename, basename))
3415                         return cubemaps[i].texture;
3416         if (i >= MAX_CUBEMAPS)
3417                 return r_texture_whitecube;
3418         numcubemaps++;
3419         strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
3420         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3421         if (!cubemaps[i].texture)
3422                 cubemaps[i].texture = r_texture_whitecube;
3423         return cubemaps[i].texture;
3424 }
3425
3426 void R_Shadow_FreeCubemaps(void)
3427 {
3428         numcubemaps = 0;
3429         R_FreeTexturePool(&r_shadow_filters_texturepool);
3430 }
3431
3432 dlight_t *R_Shadow_NewWorldLight(void)
3433 {
3434         return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
3435 }
3436
3437 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)
3438 {
3439         matrix4x4_t matrix;
3440         // validate parameters
3441         if (style < 0 || style >= MAX_LIGHTSTYLES)
3442         {
3443                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3444                 style = 0;
3445         }
3446         if (!cubemapname)
3447                 cubemapname = "";
3448
3449         // copy to light properties
3450         VectorCopy(origin, light->origin);
3451         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3452         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3453         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3454         light->color[0] = max(color[0], 0);
3455         light->color[1] = max(color[1], 0);
3456         light->color[2] = max(color[2], 0);
3457         light->radius = max(radius, 0);
3458         light->style = style;
3459         light->shadow = shadowenable;
3460         light->corona = corona;
3461         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3462         light->coronasizescale = coronasizescale;
3463         light->ambientscale = ambientscale;
3464         light->diffusescale = diffusescale;
3465         light->specularscale = specularscale;
3466         light->flags = flags;
3467
3468         // update renderable light data
3469         Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
3470         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);
3471 }
3472
3473 void R_Shadow_FreeWorldLight(dlight_t *light)
3474 {
3475         if (r_shadow_selectedlight == light)
3476                 r_shadow_selectedlight = NULL;
3477         R_RTLight_Uncompile(&light->rtlight);
3478         Mem_ExpandableArray_FreeRecord(&r_shadow_worldlightsarray, light);
3479 }
3480
3481 void R_Shadow_ClearWorldLights(void)
3482 {
3483         size_t lightindex;
3484         dlight_t *light;
3485         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3486         {
3487                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3488                 if (light)
3489                         R_Shadow_FreeWorldLight(light);
3490         }
3491         r_shadow_selectedlight = NULL;
3492         R_Shadow_FreeCubemaps();
3493 }
3494
3495 void R_Shadow_SelectLight(dlight_t *light)
3496 {
3497         if (r_shadow_selectedlight)
3498                 r_shadow_selectedlight->selected = false;
3499         r_shadow_selectedlight = light;
3500         if (r_shadow_selectedlight)
3501                 r_shadow_selectedlight->selected = true;
3502 }
3503
3504 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3505 {
3506         // this is never batched (there can be only one)
3507         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprcursor->tex, r_editlights_sprcursor->tex, false, false, r_editlights_cursorlocation, r_refdef.view.right, r_refdef.view.up, EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, EDLIGHTSPRSIZE, 1, 1, 1, 1);
3508 }
3509
3510 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3511 {
3512         float intensity;
3513         float s;
3514         vec3_t spritecolor;
3515         cachepic_t *pic;
3516
3517         // this is never batched (due to the ent parameter changing every time)
3518         // so numsurfaces == 1 and surfacelist[0] == lightnumber
3519         const dlight_t *light = (dlight_t *)ent;
3520         s = EDLIGHTSPRSIZE;
3521         intensity = 0.5f;
3522         VectorScale(light->color, intensity, spritecolor);
3523         if (VectorLength(spritecolor) < 0.1732f)
3524                 VectorSet(spritecolor, 0.1f, 0.1f, 0.1f);
3525         if (VectorLength(spritecolor) > 1.0f)
3526                 VectorNormalize(spritecolor);
3527
3528         // draw light sprite
3529         if (light->cubemapname[0] && !light->shadow)
3530                 pic = r_editlights_sprcubemapnoshadowlight;
3531         else if (light->cubemapname[0])
3532                 pic = r_editlights_sprcubemaplight;
3533         else if (!light->shadow)
3534                 pic = r_editlights_sprnoshadowlight;
3535         else
3536                 pic = r_editlights_sprlight;
3537         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, pic->tex, pic->tex, false, false, light->origin, r_refdef.view.right, r_refdef.view.up, s, -s, -s, s, spritecolor[0], spritecolor[1], spritecolor[2], 1);
3538         // draw selection sprite if light is selected
3539         if (light->selected)
3540                 R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprselection->tex, r_editlights_sprselection->tex, false, false, light->origin, r_refdef.view.right, r_refdef.view.up, s, -s, -s, s, 1, 1, 1, 1);
3541         // VorteX todo: add normalmode/realtime mode light overlay sprites?
3542 }
3543
3544 void R_Shadow_DrawLightSprites(void)
3545 {
3546         size_t lightindex;
3547         dlight_t *light;
3548         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3549         {
3550                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3551                 if (light)
3552                         R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
3553         }
3554         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
3555 }
3556
3557 void R_Shadow_SelectLightInView(void)
3558 {
3559         float bestrating, rating, temp[3];
3560         dlight_t *best;
3561         size_t lightindex;
3562         dlight_t *light;
3563         best = NULL;
3564         bestrating = 0;
3565         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3566         {
3567                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3568                 if (!light)
3569                         continue;
3570                 VectorSubtract(light->origin, r_refdef.view.origin, temp);
3571                 rating = (DotProduct(temp, r_refdef.view.forward) / sqrt(DotProduct(temp, temp)));
3572                 if (rating >= 0.95)
3573                 {
3574                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3575                         if (bestrating < rating && CL_Move(light->origin, vec3_origin, vec3_origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1.0f)
3576                         {
3577                                 bestrating = rating;
3578                                 best = light;
3579                         }
3580                 }
3581         }
3582         R_Shadow_SelectLight(best);
3583 }
3584
3585 void R_Shadow_LoadWorldLights(void)
3586 {
3587         int n, a, style, shadow, flags;
3588         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3589         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3590         if (cl.worldmodel == NULL)
3591         {
3592                 Con_Print("No map loaded.\n");
3593                 return;
3594         }
3595         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3596         strlcat (name, ".rtlights", sizeof (name));
3597         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3598         if (lightsstring)
3599         {
3600                 s = lightsstring;
3601                 n = 0;
3602                 while (*s)
3603                 {
3604                         t = s;
3605                         /*
3606                         shadow = true;
3607                         for (;COM_Parse(t, true) && strcmp(
3608                         if (COM_Parse(t, true))
3609                         {
3610                                 if (com_token[0] == '!')
3611                                 {
3612                                         shadow = false;
3613                                         origin[0] = atof(com_token+1);
3614                                 }
3615                                 else
3616                                         origin[0] = atof(com_token);
3617                                 if (Com_Parse(t
3618                         }
3619                         */
3620                         t = s;
3621                         while (*s && *s != '\n' && *s != '\r')
3622                                 s++;
3623                         if (!*s)
3624                                 break;
3625                         tempchar = *s;
3626                         shadow = true;
3627                         // check for modifier flags
3628                         if (*t == '!')
3629                         {
3630                                 shadow = false;
3631                                 t++;
3632                         }
3633                         *s = 0;
3634                         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);
3635                         *s = tempchar;
3636                         if (a < 18)
3637                                 flags = LIGHTFLAG_REALTIMEMODE;
3638                         if (a < 17)
3639                                 specularscale = 1;
3640                         if (a < 16)
3641                                 diffusescale = 1;
3642                         if (a < 15)
3643                                 ambientscale = 0;
3644                         if (a < 14)
3645                                 coronasizescale = 0.25f;
3646                         if (a < 13)
3647                                 VectorClear(angles);
3648                         if (a < 10)
3649                                 corona = 0;
3650                         if (a < 9 || !strcmp(cubemapname, "\"\""))
3651                                 cubemapname[0] = 0;
3652                         // remove quotes on cubemapname
3653                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3654                         {
3655                                 size_t namelen;
3656                                 namelen = strlen(cubemapname) - 2;
3657                                 memmove(cubemapname, cubemapname + 1, namelen);
3658                                 cubemapname[namelen] = '\0';
3659                         }
3660                         if (a < 8)
3661                         {
3662                                 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);
3663                                 break;
3664                         }
3665                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3666                         if (*s == '\r')
3667                                 s++;
3668                         if (*s == '\n')
3669                                 s++;
3670                         n++;
3671                 }
3672                 if (*s)
3673                         Con_Printf("invalid rtlights file \"%s\"\n", name);
3674                 Mem_Free(lightsstring);
3675         }
3676 }
3677
3678 void R_Shadow_SaveWorldLights(void)
3679 {
3680         size_t lightindex;
3681         dlight_t *light;
3682         size_t bufchars, bufmaxchars;
3683         char *buf, *oldbuf;
3684         char name[MAX_QPATH];
3685         char line[MAX_INPUTLINE];
3686         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
3687                 return;
3688         if (cl.worldmodel == NULL)
3689         {
3690                 Con_Print("No map loaded.\n");
3691                 return;
3692         }
3693         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3694         strlcat (name, ".rtlights", sizeof (name));
3695         bufchars = bufmaxchars = 0;
3696         buf = NULL;
3697         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3698         {
3699                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3700                 if (!light)
3701                         continue;
3702                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3703                         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);
3704                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3705                         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]);
3706                 else
3707                         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);
3708                 if (bufchars + strlen(line) > bufmaxchars)
3709                 {
3710                         bufmaxchars = bufchars + strlen(line) + 2048;
3711                         oldbuf = buf;
3712                         buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3713                         if (oldbuf)
3714                         {
3715                                 if (bufchars)
3716                                         memcpy(buf, oldbuf, bufchars);
3717                                 Mem_Free(oldbuf);
3718                         }
3719                 }
3720                 if (strlen(line))
3721                 {
3722                         memcpy(buf + bufchars, line, strlen(line));
3723                         bufchars += strlen(line);
3724                 }
3725         }
3726         if (bufchars)
3727                 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3728         if (buf)
3729                 Mem_Free(buf);
3730 }
3731
3732 void R_Shadow_LoadLightsFile(void)
3733 {
3734         int n, a, style;
3735         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3736         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3737         if (cl.worldmodel == NULL)
3738         {
3739                 Con_Print("No map loaded.\n");
3740                 return;
3741         }
3742         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3743         strlcat (name, ".lights", sizeof (name));
3744         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3745         if (lightsstring)
3746         {
3747                 s = lightsstring;
3748                 n = 0;
3749                 while (*s)
3750                 {
3751                         t = s;
3752                         while (*s && *s != '\n' && *s != '\r')
3753                                 s++;
3754                         if (!*s)
3755                                 break;
3756                         tempchar = *s;
3757                         *s = 0;
3758                         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);
3759                         *s = tempchar;
3760                         if (a < 14)
3761                         {
3762                                 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);
3763                                 break;
3764                         }
3765                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3766                         radius = bound(15, radius, 4096);
3767                         VectorScale(color, (2.0f / (8388608.0f)), color);
3768                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3769                         if (*s == '\r')
3770                                 s++;
3771                         if (*s == '\n')
3772                                 s++;
3773                         n++;
3774                 }
3775                 if (*s)
3776                         Con_Printf("invalid lights file \"%s\"\n", name);
3777                 Mem_Free(lightsstring);
3778         }
3779 }
3780
3781 // tyrlite/hmap2 light types in the delay field
3782 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3783
3784 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3785 {
3786         int entnum, style, islight, skin, pflags, effects, type, n;
3787         char *entfiledata;
3788         const char *data;
3789         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3790         char key[256], value[MAX_INPUTLINE];
3791
3792         if (cl.worldmodel == NULL)
3793         {
3794                 Con_Print("No map loaded.\n");
3795                 return;
3796         }
3797         // try to load a .ent file first
3798         FS_StripExtension (cl.worldmodel->name, key, sizeof (key));
3799         strlcat (key, ".ent", sizeof (key));
3800         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3801         // and if that is not found, fall back to the bsp file entity string
3802         if (!data)
3803                 data = cl.worldmodel->brush.entities;
3804         if (!data)
3805                 return;
3806         for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
3807         {
3808                 type = LIGHTTYPE_MINUSX;
3809                 origin[0] = origin[1] = origin[2] = 0;
3810                 originhack[0] = originhack[1] = originhack[2] = 0;
3811                 angles[0] = angles[1] = angles[2] = 0;
3812                 color[0] = color[1] = color[2] = 1;
3813                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3814                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3815                 fadescale = 1;
3816                 lightscale = 1;
3817                 style = 0;
3818                 skin = 0;
3819                 pflags = 0;
3820                 effects = 0;
3821                 islight = false;
3822                 while (1)
3823                 {
3824                         if (!COM_ParseToken_Simple(&data, false, false))
3825                                 break; // error
3826                         if (com_token[0] == '}')
3827                                 break; // end of entity
3828                         if (com_token[0] == '_')
3829                                 strlcpy(key, com_token + 1, sizeof(key));
3830                         else
3831                                 strlcpy(key, com_token, sizeof(key));
3832                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3833                                 key[strlen(key)-1] = 0;
3834                         if (!COM_ParseToken_Simple(&data, false, false))
3835                                 break; // error
3836                         strlcpy(value, com_token, sizeof(value));
3837
3838                         // now that we have the key pair worked out...
3839                         if (!strcmp("light", key))
3840                         {
3841                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3842                                 if (n == 1)
3843                                 {
3844                                         // quake
3845                                         light[0] = vec[0] * (1.0f / 256.0f);
3846                                         light[1] = vec[0] * (1.0f / 256.0f);
3847                                         light[2] = vec[0] * (1.0f / 256.0f);
3848                                         light[3] = vec[0];
3849                                 }
3850                                 else if (n == 4)
3851                                 {
3852                                         // halflife
3853                                         light[0] = vec[0] * (1.0f / 255.0f);
3854                                         light[1] = vec[1] * (1.0f / 255.0f);
3855                                         light[2] = vec[2] * (1.0f / 255.0f);
3856                                         light[3] = vec[3];
3857                                 }
3858                         }
3859                         else if (!strcmp("delay", key))
3860                                 type = atoi(value);
3861                         else if (!strcmp("origin", key))
3862                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3863                         else if (!strcmp("angle", key))
3864                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3865                         else if (!strcmp("angles", key))
3866                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3867                         else if (!strcmp("color", key))
3868                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3869                         else if (!strcmp("wait", key))
3870                                 fadescale = atof(value);
3871                         else if (!strcmp("classname", key))
3872                         {
3873                                 if (!strncmp(value, "light", 5))
3874                                 {
3875                                         islight = true;
3876                                         if (!strcmp(value, "light_fluoro"))
3877                                         {
3878                                                 originhack[0] = 0;
3879                                                 originhack[1] = 0;
3880                                                 originhack[2] = 0;
3881                                                 overridecolor[0] = 1;
3882                                                 overridecolor[1] = 1;
3883                                                 overridecolor[2] = 1;
3884                                         }
3885                                         if (!strcmp(value, "light_fluorospark"))
3886                                         {
3887                                                 originhack[0] = 0;
3888                                                 originhack[1] = 0;
3889                                                 originhack[2] = 0;
3890                                                 overridecolor[0] = 1;
3891                                                 overridecolor[1] = 1;
3892                                                 overridecolor[2] = 1;
3893                                         }
3894                                         if (!strcmp(value, "light_globe"))
3895                                         {
3896                                                 originhack[0] = 0;
3897                                                 originhack[1] = 0;
3898                                                 originhack[2] = 0;
3899                                                 overridecolor[0] = 1;
3900                                                 overridecolor[1] = 0.8;
3901                                                 overridecolor[2] = 0.4;
3902                                         }
3903                                         if (!strcmp(value, "light_flame_large_yellow"))
3904                                         {
3905                                                 originhack[0] = 0;
3906                                                 originhack[1] = 0;
3907                                                 originhack[2] = 0;
3908                                                 overridecolor[0] = 1;
3909                                                 overridecolor[1] = 0.5;
3910                                                 overridecolor[2] = 0.1;
3911                                         }
3912                                         if (!strcmp(value, "light_flame_small_yellow"))
3913                                         {
3914                                                 originhack[0] = 0;
3915                                                 originhack[1] = 0;
3916                                                 originhack[2] = 0;
3917                                                 overridecolor[0] = 1;
3918                                                 overridecolor[1] = 0.5;
3919                                                 overridecolor[2] = 0.1;
3920                                         }
3921                                         if (!strcmp(value, "light_torch_small_white"))
3922                                         {
3923                                                 originhack[0] = 0;
3924                                                 originhack[1] = 0;
3925                                                 originhack[2] = 0;
3926                                                 overridecolor[0] = 1;
3927                                                 overridecolor[1] = 0.5;
3928                                                 overridecolor[2] = 0.1;
3929                                         }
3930                                         if (!strcmp(value, "light_torch_small_walltorch"))
3931                                         {
3932                                                 originhack[0] = 0;
3933                                                 originhack[1] = 0;
3934                                                 originhack[2] = 0;
3935                                                 overridecolor[0] = 1;
3936                                                 overridecolor[1] = 0.5;
3937                                                 overridecolor[2] = 0.1;
3938                                         }
3939                                 }
3940                         }
3941                         else if (!strcmp("style", key))
3942                                 style = atoi(value);
3943                         else if (!strcmp("skin", key))
3944                                 skin = (int)atof(value);
3945                         else if (!strcmp("pflags", key))
3946                                 pflags = (int)atof(value);
3947                         else if (!strcmp("effects", key))
3948                                 effects = (int)atof(value);
3949                         else if (cl.worldmodel->type == mod_brushq3)
3950                         {
3951                                 if (!strcmp("scale", key))
3952                                         lightscale = atof(value);
3953                                 if (!strcmp("fade", key))
3954                                         fadescale = atof(value);
3955                         }
3956                 }
3957                 if (!islight)
3958                         continue;
3959                 if (lightscale <= 0)
3960                         lightscale = 1;
3961                 if (fadescale <= 0)
3962                         fadescale = 1;
3963                 if (color[0] == color[1] && color[0] == color[2])
3964                 {
3965                         color[0] *= overridecolor[0];
3966                         color[1] *= overridecolor[1];
3967                         color[2] *= overridecolor[2];
3968                 }
3969                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3970                 color[0] = color[0] * light[0];
3971                 color[1] = color[1] * light[1];
3972                 color[2] = color[2] * light[2];
3973                 switch (type)
3974                 {
3975                 case LIGHTTYPE_MINUSX:
3976                         break;
3977                 case LIGHTTYPE_RECIPX:
3978                         radius *= 2;
3979                         VectorScale(color, (1.0f / 16.0f), color);
3980                         break;
3981                 case LIGHTTYPE_RECIPXX:
3982                         radius *= 2;
3983                         VectorScale(color, (1.0f / 16.0f), color);
3984                         break;
3985                 default:
3986                 case LIGHTTYPE_NONE:
3987                         break;
3988                 case LIGHTTYPE_SUN:
3989                         break;
3990                 case LIGHTTYPE_MINUSXX:
3991                         break;
3992                 }
3993                 VectorAdd(origin, originhack, origin);
3994                 if (radius >= 1)
3995                         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);
3996         }
3997         if (entfiledata)
3998                 Mem_Free(entfiledata);
3999 }
4000
4001
4002 void R_Shadow_SetCursorLocationForView(void)
4003 {
4004         vec_t dist, push;
4005         vec3_t dest, endpos;
4006         trace_t trace;
4007         VectorMA(r_refdef.view.origin, r_editlights_cursordistance.value, r_refdef.view.forward, dest);
4008         trace = CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
4009         if (trace.fraction < 1)
4010         {
4011                 dist = trace.fraction * r_editlights_cursordistance.value;
4012                 push = r_editlights_cursorpushback.value;
4013                 if (push > dist)
4014                         push = dist;
4015                 push = -push;
4016                 VectorMA(trace.endpos, push, r_refdef.view.forward, endpos);
4017                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
4018         }
4019         else
4020         {
4021                 VectorClear( endpos );
4022         }
4023         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4024         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4025         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4026 }
4027
4028 void R_Shadow_UpdateWorldLightSelection(void)
4029 {
4030         if (r_editlights.integer)
4031         {
4032                 R_Shadow_SetCursorLocationForView();
4033                 R_Shadow_SelectLightInView();
4034         }
4035         else
4036                 R_Shadow_SelectLight(NULL);
4037 }
4038
4039 void R_Shadow_EditLights_Clear_f(void)
4040 {
4041         R_Shadow_ClearWorldLights();
4042 }
4043
4044 void R_Shadow_EditLights_Reload_f(void)
4045 {
4046         if (!cl.worldmodel)
4047                 return;
4048         strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
4049         R_Shadow_ClearWorldLights();
4050         R_Shadow_LoadWorldLights();
4051         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4052         {
4053                 R_Shadow_LoadLightsFile();
4054                 if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4055                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4056         }
4057 }
4058
4059 void R_Shadow_EditLights_Save_f(void)
4060 {
4061         if (!cl.worldmodel)
4062                 return;
4063         R_Shadow_SaveWorldLights();
4064 }
4065
4066 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
4067 {
4068         R_Shadow_ClearWorldLights();
4069         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4070 }
4071
4072 void R_Shadow_EditLights_ImportLightsFile_f(void)
4073 {
4074         R_Shadow_ClearWorldLights();
4075         R_Shadow_LoadLightsFile();
4076 }
4077
4078 void R_Shadow_EditLights_Spawn_f(void)
4079 {
4080         vec3_t color;
4081         if (!r_editlights.integer)
4082         {
4083                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4084                 return;
4085         }
4086         if (Cmd_Argc() != 1)
4087         {
4088                 Con_Print("r_editlights_spawn does not take parameters\n");
4089                 return;
4090         }
4091         color[0] = color[1] = color[2] = 1;
4092         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
4093 }
4094
4095 void R_Shadow_EditLights_Edit_f(void)
4096 {
4097         vec3_t origin, angles, color;
4098         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
4099         int style, shadows, flags, normalmode, realtimemode;
4100         char cubemapname[MAX_INPUTLINE];
4101         if (!r_editlights.integer)
4102         {
4103                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4104                 return;
4105         }
4106         if (!r_shadow_selectedlight)
4107         {
4108                 Con_Print("No selected light.\n");
4109                 return;
4110         }
4111         VectorCopy(r_shadow_selectedlight->origin, origin);
4112         VectorCopy(r_shadow_selectedlight->angles, angles);
4113         VectorCopy(r_shadow_selectedlight->color, color);
4114         radius = r_shadow_selectedlight->radius;
4115         style = r_shadow_selectedlight->style;
4116         if (r_shadow_selectedlight->cubemapname)
4117                 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
4118         else
4119                 cubemapname[0] = 0;
4120         shadows = r_shadow_selectedlight->shadow;
4121         corona = r_shadow_selectedlight->corona;
4122         coronasizescale = r_shadow_selectedlight->coronasizescale;
4123         ambientscale = r_shadow_selectedlight->ambientscale;
4124         diffusescale = r_shadow_selectedlight->diffusescale;
4125         specularscale = r_shadow_selectedlight->specularscale;
4126         flags = r_shadow_selectedlight->flags;
4127         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
4128         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
4129         if (!strcmp(Cmd_Argv(1), "origin"))
4130         {
4131                 if (Cmd_Argc() != 5)
4132                 {
4133                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4134                         return;
4135                 }
4136                 origin[0] = atof(Cmd_Argv(2));
4137                 origin[1] = atof(Cmd_Argv(3));
4138                 origin[2] = atof(Cmd_Argv(4));
4139         }
4140         else if (!strcmp(Cmd_Argv(1), "originx"))
4141         {
4142                 if (Cmd_Argc() != 3)
4143                 {
4144                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4145                         return;
4146                 }
4147                 origin[0] = atof(Cmd_Argv(2));
4148         }
4149         else if (!strcmp(Cmd_Argv(1), "originy"))
4150         {
4151                 if (Cmd_Argc() != 3)
4152                 {
4153                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4154                         return;
4155                 }
4156                 origin[1] = atof(Cmd_Argv(2));
4157         }
4158         else if (!strcmp(Cmd_Argv(1), "originz"))
4159         {
4160                 if (Cmd_Argc() != 3)
4161                 {
4162                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4163                         return;
4164                 }
4165                 origin[2] = atof(Cmd_Argv(2));
4166         }
4167         else if (!strcmp(Cmd_Argv(1), "move"))
4168         {
4169                 if (Cmd_Argc() != 5)
4170                 {
4171                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4172                         return;
4173                 }
4174                 origin[0] += atof(Cmd_Argv(2));
4175                 origin[1] += atof(Cmd_Argv(3));
4176                 origin[2] += atof(Cmd_Argv(4));
4177         }
4178         else if (!strcmp(Cmd_Argv(1), "movex"))
4179         {
4180                 if (Cmd_Argc() != 3)
4181                 {
4182                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4183                         return;
4184                 }
4185                 origin[0] += atof(Cmd_Argv(2));
4186         }
4187         else if (!strcmp(Cmd_Argv(1), "movey"))
4188         {
4189                 if (Cmd_Argc() != 3)
4190                 {
4191                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4192                         return;
4193                 }
4194                 origin[1] += atof(Cmd_Argv(2));
4195         }
4196         else if (!strcmp(Cmd_Argv(1), "movez"))
4197         {
4198                 if (Cmd_Argc() != 3)
4199                 {
4200                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4201                         return;
4202                 }
4203                 origin[2] += atof(Cmd_Argv(2));
4204         }
4205         else if (!strcmp(Cmd_Argv(1), "angles"))
4206         {
4207                 if (Cmd_Argc() != 5)
4208                 {
4209                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4210                         return;
4211                 }
4212                 angles[0] = atof(Cmd_Argv(2));
4213                 angles[1] = atof(Cmd_Argv(3));
4214                 angles[2] = atof(Cmd_Argv(4));
4215         }
4216         else if (!strcmp(Cmd_Argv(1), "anglesx"))
4217         {
4218                 if (Cmd_Argc() != 3)
4219                 {
4220                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4221                         return;
4222                 }
4223                 angles[0] = atof(Cmd_Argv(2));
4224         }
4225         else if (!strcmp(Cmd_Argv(1), "anglesy"))
4226         {
4227                 if (Cmd_Argc() != 3)
4228                 {
4229                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4230                         return;
4231                 }
4232                 angles[1] = atof(Cmd_Argv(2));
4233         }
4234         else if (!strcmp(Cmd_Argv(1), "anglesz"))
4235         {
4236                 if (Cmd_Argc() != 3)
4237                 {
4238                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4239                         return;
4240                 }
4241                 angles[2] = atof(Cmd_Argv(2));
4242         }
4243         else if (!strcmp(Cmd_Argv(1), "color"))
4244         {
4245                 if (Cmd_Argc() != 5)
4246                 {
4247                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
4248                         return;
4249                 }
4250                 color[0] = atof(Cmd_Argv(2));
4251                 color[1] = atof(Cmd_Argv(3));
4252                 color[2] = atof(Cmd_Argv(4));
4253         }
4254         else if (!strcmp(Cmd_Argv(1), "radius"))
4255         {
4256                 if (Cmd_Argc() != 3)
4257                 {
4258                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4259                         return;
4260                 }
4261                 radius = atof(Cmd_Argv(2));
4262         }
4263         else if (!strcmp(Cmd_Argv(1), "colorscale"))
4264         {
4265                 if (Cmd_Argc() == 3)
4266                 {
4267                         double scale = atof(Cmd_Argv(2));
4268                         color[0] *= scale;
4269                         color[1] *= scale;
4270                         color[2] *= scale;
4271                 }
4272                 else
4273                 {
4274                         if (Cmd_Argc() != 5)
4275                         {
4276                                 Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
4277                                 return;
4278                         }
4279                         color[0] *= atof(Cmd_Argv(2));
4280                         color[1] *= atof(Cmd_Argv(3));
4281                         color[2] *= atof(Cmd_Argv(4));
4282                 }
4283         }
4284         else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
4285         {
4286                 if (Cmd_Argc() != 3)
4287                 {
4288                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4289                         return;
4290                 }
4291                 radius *= atof(Cmd_Argv(2));
4292         }
4293         else if (!strcmp(Cmd_Argv(1), "style"))
4294         {
4295                 if (Cmd_Argc() != 3)
4296                 {
4297                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4298                         return;
4299                 }
4300                 style = atoi(Cmd_Argv(2));
4301         }
4302         else if (!strcmp(Cmd_Argv(1), "cubemap"))
4303         {
4304                 if (Cmd_Argc() > 3)
4305                 {
4306                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4307                         return;
4308                 }
4309                 if (Cmd_Argc() == 3)
4310                         strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
4311                 else
4312                         cubemapname[0] = 0;
4313         }
4314         else if (!strcmp(Cmd_Argv(1), "shadows"))
4315         {
4316                 if (Cmd_Argc() != 3)
4317                 {
4318                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4319                         return;
4320                 }
4321                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4322         }
4323         else if (!strcmp(Cmd_Argv(1), "corona"))
4324         {
4325                 if (Cmd_Argc() != 3)
4326                 {
4327                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4328                         return;
4329                 }
4330                 corona = atof(Cmd_Argv(2));
4331         }
4332         else if (!strcmp(Cmd_Argv(1), "coronasize"))
4333         {
4334                 if (Cmd_Argc() != 3)
4335                 {
4336                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4337                         return;
4338                 }
4339                 coronasizescale = atof(Cmd_Argv(2));
4340         }
4341         else if (!strcmp(Cmd_Argv(1), "ambient"))
4342         {
4343                 if (Cmd_Argc() != 3)
4344                 {
4345                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4346                         return;
4347                 }
4348                 ambientscale = atof(Cmd_Argv(2));
4349         }
4350         else if (!strcmp(Cmd_Argv(1), "diffuse"))
4351         {
4352                 if (Cmd_Argc() != 3)
4353                 {
4354                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4355                         return;
4356                 }
4357                 diffusescale = atof(Cmd_Argv(2));
4358         }
4359         else if (!strcmp(Cmd_Argv(1), "specular"))
4360         {
4361                 if (Cmd_Argc() != 3)
4362                 {
4363                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4364                         return;
4365                 }
4366                 specularscale = atof(Cmd_Argv(2));
4367         }
4368         else if (!strcmp(Cmd_Argv(1), "normalmode"))
4369         {
4370                 if (Cmd_Argc() != 3)
4371                 {
4372                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4373                         return;
4374                 }
4375                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4376         }
4377         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
4378         {
4379                 if (Cmd_Argc() != 3)
4380                 {
4381                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4382                         return;
4383                 }
4384                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4385         }
4386         else
4387         {
4388                 Con_Print("usage: r_editlights_edit [property] [value]\n");
4389                 Con_Print("Selected light's properties:\n");
4390                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4391                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4392                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4393                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
4394                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
4395                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
4396                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4397                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
4398                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
4399                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
4400                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
4401                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
4402                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4403                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4404                 return;
4405         }
4406         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4407         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4408 }
4409
4410 void R_Shadow_EditLights_EditAll_f(void)
4411 {
4412         size_t lightindex;
4413         dlight_t *light;
4414
4415         if (!r_editlights.integer)
4416         {
4417                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4418                 return;
4419         }
4420
4421         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4422         {
4423                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4424                 if (!light)
4425                         continue;
4426                 R_Shadow_SelectLight(light);
4427                 R_Shadow_EditLights_Edit_f();
4428         }
4429 }
4430
4431 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4432 {
4433         int lightnumber, lightcount;
4434         size_t lightindex;
4435         dlight_t *light;
4436         float x, y;
4437         char temp[256];
4438         if (!r_editlights.integer)
4439                 return;
4440         x = vid_conwidth.value - 240;
4441         y = 5;
4442         DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
4443         lightnumber = -1;
4444         lightcount = 0;
4445         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4446         {
4447                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4448                 if (!light)
4449                         continue;
4450                 if (light == r_shadow_selectedlight)
4451                         lightnumber = lightindex;
4452                 lightcount++;
4453         }
4454         sprintf(temp, "Cursor origin: %.0f %.0f %.0f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4455         sprintf(temp, "Total lights : %i active (%i total)", lightcount, (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4456         y += 8;
4457         if (r_shadow_selectedlight == NULL)
4458                 return;
4459         sprintf(temp, "Light #%i properties:", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4460         sprintf(temp, "Origin       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4461         sprintf(temp, "Angles       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4462         sprintf(temp, "Color        : %.2f %.2f %.2f\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, NULL, true);y += 8;
4463         sprintf(temp, "Radius       : %.0f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4464         sprintf(temp, "Corona       : %.0f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4465         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4466         sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4467         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4468         sprintf(temp, "CoronaSize   : %.2f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4469         sprintf(temp, "Ambient      : %.2f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4470         sprintf(temp, "Diffuse      : %.2f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4471         sprintf(temp, "Specular     : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4472         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, NULL, true);y += 8;
4473         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, NULL, true);y += 8;
4474 }
4475
4476 void R_Shadow_EditLights_ToggleShadow_f(void)
4477 {
4478         if (!r_editlights.integer)
4479         {
4480                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4481                 return;
4482         }
4483         if (!r_shadow_selectedlight)
4484         {
4485                 Con_Print("No selected light.\n");
4486                 return;
4487         }
4488         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);
4489 }
4490
4491 void R_Shadow_EditLights_ToggleCorona_f(void)
4492 {
4493         if (!r_editlights.integer)
4494         {
4495                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4496                 return;
4497         }
4498         if (!r_shadow_selectedlight)
4499         {
4500                 Con_Print("No selected light.\n");
4501                 return;
4502         }
4503         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);
4504 }
4505
4506 void R_Shadow_EditLights_Remove_f(void)
4507 {
4508         if (!r_editlights.integer)
4509         {
4510                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
4511                 return;
4512         }
4513         if (!r_shadow_selectedlight)
4514         {
4515                 Con_Print("No selected light.\n");
4516                 return;
4517         }
4518         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4519         r_shadow_selectedlight = NULL;
4520 }
4521
4522 void R_Shadow_EditLights_Help_f(void)
4523 {
4524         Con_Print(
4525 "Documentation on r_editlights system:\n"
4526 "Settings:\n"
4527 "r_editlights : enable/disable editing mode\n"
4528 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4529 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4530 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4531 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4532 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4533 "Commands:\n"
4534 "r_editlights_help : this help\n"
4535 "r_editlights_clear : remove all lights\n"
4536 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4537 "r_editlights_save : save to .rtlights file\n"
4538 "r_editlights_spawn : create a light with default settings\n"
4539 "r_editlights_edit command : edit selected light - more documentation below\n"
4540 "r_editlights_remove : remove selected light\n"
4541 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4542 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4543 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4544 "Edit commands:\n"
4545 "origin x y z : set light location\n"
4546 "originx x: set x component of light location\n"
4547 "originy y: set y component of light location\n"
4548 "originz z: set z component of light location\n"
4549 "move x y z : adjust light location\n"
4550 "movex x: adjust x component of light location\n"
4551 "movey y: adjust y component of light location\n"
4552 "movez z: adjust z component of light location\n"
4553 "angles x y z : set light angles\n"
4554 "anglesx x: set x component of light angles\n"
4555 "anglesy y: set y component of light angles\n"
4556 "anglesz z: set z component of light angles\n"
4557 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4558 "radius radius : set radius (size) of light\n"
4559 "colorscale grey : multiply color of light (1 does nothing)\n"
4560 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
4561 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
4562 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
4563 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4564 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4565 "shadows 1/0 : turn on/off shadows\n"
4566 "corona n : set corona intensity\n"
4567 "coronasize n : set corona size (0-1)\n"
4568 "ambient n : set ambient intensity (0-1)\n"
4569 "diffuse n : set diffuse intensity (0-1)\n"
4570 "specular n : set specular intensity (0-1)\n"
4571 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4572 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4573 "<nothing> : print light properties to console\n"
4574         );
4575 }
4576
4577 void R_Shadow_EditLights_CopyInfo_f(void)
4578 {
4579         if (!r_editlights.integer)
4580         {
4581                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
4582                 return;
4583         }
4584         if (!r_shadow_selectedlight)
4585         {
4586                 Con_Print("No selected light.\n");
4587                 return;
4588         }
4589         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4590         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4591         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4592         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4593         if (r_shadow_selectedlight->cubemapname)
4594                 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
4595         else
4596                 r_shadow_bufferlight.cubemapname[0] = 0;
4597         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4598         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4599         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4600         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4601         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4602         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4603         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4604 }
4605
4606 void R_Shadow_EditLights_PasteInfo_f(void)
4607 {
4608         if (!r_editlights.integer)
4609         {
4610                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
4611                 return;
4612         }
4613         if (!r_shadow_selectedlight)
4614         {
4615                 Con_Print("No selected light.\n");
4616                 return;
4617         }
4618         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);
4619 }
4620
4621 void R_Shadow_EditLights_Init(void)
4622 {
4623         Cvar_RegisterVariable(&r_editlights);
4624         Cvar_RegisterVariable(&r_editlights_cursordistance);
4625         Cvar_RegisterVariable(&r_editlights_cursorpushback);
4626         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4627         Cvar_RegisterVariable(&r_editlights_cursorgrid);
4628         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4629         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
4630         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
4631         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)");
4632         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
4633         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
4634         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
4635         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)");
4636         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
4637         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
4638         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
4639         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
4640         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
4641         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
4642         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)");
4643 }
4644
4645
4646
4647 /*
4648 =============================================================================
4649
4650 LIGHT SAMPLING
4651
4652 =============================================================================
4653 */
4654
4655 void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic)
4656 {
4657         VectorClear(diffusecolor);
4658         VectorClear(diffusenormal);
4659
4660         if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
4661         {
4662                 ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_ambient.value * (2.0f / 128.0f);
4663                 r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
4664         }
4665         else
4666                 VectorSet(ambientcolor, 1, 1, 1);
4667
4668         if (dynamic)
4669         {
4670                 int i;
4671                 float f, v[3];
4672                 rtlight_t *light;
4673                 for (i = 0;i < r_refdef.scene.numlights;i++)
4674                 {
4675                         light = &r_refdef.scene.lights[i];
4676                         Matrix4x4_Transform(&light->matrix_worldtolight, p, v);
4677                         f = 1 - VectorLength2(v);
4678                         if (f > 0 && CL_Move(p, vec3_origin, vec3_origin, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
4679                                 VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
4680                 }
4681         }
4682 }