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