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