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