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