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