]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
removed 3 unused variables
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is normally rendered using Carmack's Reverse technique, in which
11 backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
12 zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
13 where shadows did not intersect the visible geometry, suitable as a stencil
14 mask for rendering lighting everywhere but shadow.
15
16 In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
17 as decrement and the frontfaces as increment, and we redefine the DepthFunc to
18 GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
19 and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
20 additionally we clear stencil to 128 to avoid the need for the unclamped
21 incr/decr extension (not related to patent).
22
23 Patent warning:
24 This algorithm may be covered by Creative's patent (US Patent #6384822),
25 however that patent is quite specific about increment on backfaces and
26 decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
27 opposite this implementation and partially opposite Carmack's Reverse paper
28 (which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
29
30
31
32 Terminology: Stencil Light Volume (sometimes called Light Volumes)
33 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
34 areas in shadow it contains the areas in light, this can only be built
35 quickly for certain limited cases (such as portal visibility from a point),
36 but is quite useful for some effects (sunlight coming from sky polygons is
37 one possible example, translucent occluders is another example).
38
39
40
41 Terminology: Optimized Stencil Shadow Volume
42 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
43 no duplicate coverage of areas (no need to shadow an area twice), often this
44 greatly improves performance but is an operation too costly to use on moving
45 lights (however completely optimal Stencil Light Volumes can be constructed
46 in some ideal cases).
47
48
49
50 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
51 Per pixel evaluation of lighting equations, at a bare minimum this involves
52 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
53 vector and surface normal, using a texture of the surface bumps, called a
54 NormalMap) if supported by hardware; in our case there is support for cards
55 which are incapable of DOT3, the quality is quite poor however.  Additionally
56 it is desirable to have specular evaluation per pixel, per vertex
57 normalization of specular halfangle vectors causes noticable distortion but
58 is unavoidable on hardware without GL_ARB_fragment_program or
59 GL_ARB_fragment_shader.
60
61
62
63 Terminology: Normalization CubeMap
64 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
65 encoded as RGB colors) for any possible direction, this technique allows per
66 pixel calculation of incidence vector for per pixel lighting purposes, which
67 would not otherwise be possible per pixel without GL_ARB_fragment_program or
68 GL_ARB_fragment_shader.
69
70
71
72 Terminology: 2D+1D Attenuation Texturing
73 A very crude approximation of light attenuation with distance which results
74 in cylindrical light shapes which fade vertically as a streak (some games
75 such as Doom3 allow this to be rotated to be less noticable in specific
76 cases), the technique is simply modulating lighting by two 2D textures (which
77 can be the same) on different axes of projection (XY and Z, typically), this
78 is the second best technique available without 3D Attenuation Texturing,
79 GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
80
81
82
83 Terminology: 2D+1D Inverse Attenuation Texturing
84 A clever method described in papers on the Abducted engine, this has a squared
85 distance texture (bright on the outside, black in the middle), which is used
86 twice using GL_ADD blending, the result of this is used in an inverse modulate
87 (GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
88 lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
89 texturing).
90
91
92
93 Terminology: 3D Attenuation Texturing
94 A slightly crude approximation of light attenuation with distance, its flaws
95 are limited radius and resolution (performance tradeoffs).
96
97
98
99 Terminology: 3D Attenuation-Normalization Texturing
100 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
101 vectors shorter the lighting becomes darker, a very effective optimization of
102 diffuse lighting if 3D Attenuation Textures are already used.
103
104
105
106 Terminology: Light Cubemap Filtering
107 A technique for modeling non-uniform light distribution according to
108 direction, for example a lantern may use a cubemap to describe the light
109 emission pattern of the cage around the lantern (as well as soot buildup
110 discoloring the light in certain areas), often also used for softened grate
111 shadows and light shining through a stained glass window (done crudely by
112 texturing the lighting with a cubemap), another good example would be a disco
113 light.  This technique is used heavily in many games (Doom3 does not support
114 this however).
115
116
117
118 Terminology: Light Projection Filtering
119 A technique for modeling shadowing of light passing through translucent
120 surfaces, allowing stained glass windows and other effects to be done more
121 elegantly than possible with Light Cubemap Filtering by applying an occluder
122 texture to the lighting combined with a stencil light volume to limit the lit
123 area, this technique is used by Doom3 for spotlights and flashlights, among
124 other things, this can also be used more generally to render light passing
125 through multiple translucent occluders in a scene (using a light volume to
126 describe the area beyond the occluder, and thus mask off rendering of all
127 other areas).
128
129
130
131 Terminology: Doom3 Lighting
132 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
133 CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
134 demonstrated by the game Doom3.
135 */
136
137 #include "quakedef.h"
138 #include "r_shadow.h"
139 #include "cl_collision.h"
140 #include "portals.h"
141 #include "image.h"
142
143 extern void R_Shadow_EditLights_Init(void);
144
145 typedef enum r_shadowstage_e
146 {
147         R_SHADOWSTAGE_NONE,
148         R_SHADOWSTAGE_STENCIL,
149         R_SHADOWSTAGE_STENCILTWOSIDE,
150         R_SHADOWSTAGE_LIGHT_VERTEX,
151         R_SHADOWSTAGE_LIGHT_DOT3,
152         R_SHADOWSTAGE_LIGHT_GLSL,
153         R_SHADOWSTAGE_VISIBLEVOLUMES,
154         R_SHADOWSTAGE_VISIBLELIGHTING,
155 }
156 r_shadowstage_t;
157
158 r_shadowstage_t r_shadowstage = R_SHADOWSTAGE_NONE;
159
160 mempool_t *r_shadow_mempool;
161
162 int maxshadowelements;
163 int *shadowelements;
164
165 int maxshadowmark;
166 int numshadowmark;
167 int *shadowmark;
168 int *shadowmarklist;
169 int shadowmarkcount;
170
171 int maxvertexupdate;
172 int *vertexupdate;
173 int *vertexremap;
174 int vertexupdatenum;
175
176 int r_shadow_buffer_numleafpvsbytes;
177 qbyte *r_shadow_buffer_leafpvs;
178 int *r_shadow_buffer_leaflist;
179
180 int r_shadow_buffer_numsurfacepvsbytes;
181 qbyte *r_shadow_buffer_surfacepvs;
182 int *r_shadow_buffer_surfacelist;
183
184 rtexturepool_t *r_shadow_texturepool;
185 rtexture_t *r_shadow_attenuation2dtexture;
186 rtexture_t *r_shadow_attenuation3dtexture;
187
188 // lights are reloaded when this changes
189 char r_shadow_mapname[MAX_QPATH];
190
191 // used only for light filters (cubemaps)
192 rtexturepool_t *r_shadow_filters_texturepool;
193
194 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
195 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
196 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
197 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1"};
198 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
199 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
200 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
201 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
202 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
203 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
204 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
205 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1"};
206 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1"};
207 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0"};
208 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0"};
209 cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1"};
210 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0"};
211 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1"};
212 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1"};
213 cvar_t r_shadow_realtime_world_compilelight = {0, "r_shadow_realtime_world_compilelight", "1"};
214 cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1"};
215 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
216 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0"};
217 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1"};
218 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
219 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
220 cvar_t r_shadow_visiblelighting = {0, "r_shadow_visiblelighting", "0"};
221 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
222 cvar_t r_shadow_glsl = {0, "r_shadow_glsl", "1"};
223 cvar_t r_shadow_glsl_offsetmapping = {0, "r_shadow_glsl_offsetmapping", "1"};
224 cvar_t r_shadow_glsl_offsetmapping_scale = {0, "r_shadow_glsl_offsetmapping_scale", "-0.04"};
225 cvar_t r_shadow_glsl_offsetmapping_bias = {0, "r_shadow_glsl_offsetmapping_bias", "0.04"};
226 cvar_t r_shadow_glsl_geforcefxlowquality = {0, "r_shadow_glsl_geforcefxlowquality", "1"};
227 cvar_t r_shadow_glsl_surfacenormalize = {0, "r_shadow_glsl_surfacenormalize", "1"};
228 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
229 cvar_t r_editlights = {0, "r_editlights", "0"};
230 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024"};
231 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0"};
232 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4"};
233 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4"};
234 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
235 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
236 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
237
238 float r_shadow_attenpower, r_shadow_attenscale;
239
240 rtlight_t *r_shadow_compilingrtlight;
241 dlight_t *r_shadow_worldlightchain;
242 dlight_t *r_shadow_selectedlight;
243 dlight_t r_shadow_bufferlight;
244 vec3_t r_editlights_cursorlocation;
245
246 rtexture_t *lighttextures[5];
247
248 extern int con_vislines;
249
250 typedef struct cubemapinfo_s
251 {
252         char basename[64];
253         rtexture_t *texture;
254 }
255 cubemapinfo_t;
256
257 #define MAX_CUBEMAPS 256
258 static int numcubemaps;
259 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
260
261 #define SHADERPERMUTATION_SPECULAR (1<<0)
262 #define SHADERPERMUTATION_FOG (1<<1)
263 #define SHADERPERMUTATION_CUBEFILTER (1<<2)
264 #define SHADERPERMUTATION_OFFSETMAPPING (1<<3)
265 #define SHADERPERMUTATION_SURFACENORMALIZE (1<<4)
266 #define SHADERPERMUTATION_GEFORCEFX (1<<5)
267 #define SHADERPERMUTATION_COUNT (1<<6)
268
269 GLhandleARB r_shadow_program_light[SHADERPERMUTATION_COUNT];
270
271 void R_Shadow_UncompileWorldLights(void);
272 void R_Shadow_ClearWorldLights(void);
273 void R_Shadow_SaveWorldLights(void);
274 void R_Shadow_LoadWorldLights(void);
275 void R_Shadow_LoadLightsFile(void);
276 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
277 void R_Shadow_EditLights_Reload_f(void);
278 void R_Shadow_ValidateCvars(void);
279 static void R_Shadow_MakeTextures(void);
280 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
281
282 const char *builtinshader_light_vert =
283 "// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
284 "// written by Forest 'LordHavoc' Hale\n"
285 "\n"
286 "uniform vec3 LightPosition;\n"
287 "\n"
288 "varying vec2 TexCoord;\n"
289 "varying vec3 CubeVector;\n"
290 "varying vec3 LightVector;\n"
291 "\n"
292 "#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
293 "uniform vec3 EyePosition;\n"
294 "varying vec3 EyeVector;\n"
295 "#endif\n"
296 "\n"
297 "// TODO: get rid of tangentt (texcoord2) and use a crossproduct to regenerate it from tangents (texcoord1) and normal (texcoord3)\n"
298 "\n"
299 "void main(void)\n"
300 "{\n"
301 "       // copy the surface texcoord\n"
302 "       TexCoord = gl_MultiTexCoord0.st;\n"
303 "\n"
304 "       // transform vertex position into light attenuation/cubemap space\n"
305 "       // (-1 to +1 across the light box)\n"
306 "       CubeVector = vec3(gl_TextureMatrix[3] * gl_Vertex);\n"
307 "\n"
308 "       // transform unnormalized light direction into tangent space\n"
309 "       // (we use unnormalized to ensure that it interpolates correctly and then\n"
310 "       //  normalize it per pixel)\n"
311 "       vec3 lightminusvertex = LightPosition - gl_Vertex.xyz;\n"
312 "       LightVector.x = -dot(lightminusvertex, gl_MultiTexCoord1.xyz);\n"
313 "       LightVector.y = -dot(lightminusvertex, gl_MultiTexCoord2.xyz);\n"
314 "       LightVector.z = -dot(lightminusvertex, gl_MultiTexCoord3.xyz);\n"
315 "\n"
316 "#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
317 "       // transform unnormalized eye direction into tangent space\n"
318 "       vec3 eyeminusvertex = EyePosition - gl_Vertex.xyz;\n"
319 "       EyeVector.x = -dot(eyeminusvertex, gl_MultiTexCoord1.xyz);\n"
320 "       EyeVector.y = -dot(eyeminusvertex, gl_MultiTexCoord2.xyz);\n"
321 "       EyeVector.z = -dot(eyeminusvertex, gl_MultiTexCoord3.xyz);\n"
322 "#endif\n"
323 "\n"
324 "       // transform vertex to camera space, using ftransform to match non-VS\n"
325 "       // rendering\n"
326 "       gl_Position = ftransform();\n"
327 "}\n"
328 ;
329
330 const char *builtinshader_light_frag =
331 "// ambient+diffuse+specular+normalmap+attenuation+cubemap+fog shader\n"
332 "// written by Forest 'LordHavoc' Hale\n"
333 "\n"
334 "// use half floats on GEFORCEFX for math performance, otherwise don't\n"
335 "#ifndef GEFORCEFX\n"
336 "#define half float\n"
337 "#define hvec2 vec2\n"
338 "#define hvec3 vec3\n"
339 "#define hvec4 vec4\n"
340 "#endif\n"
341 "\n"
342 "uniform hvec3 LightColor;\n"
343 "#ifdef USEOFFSETMAPPING\n"
344 "uniform half OffsetMapping_Scale;\n"
345 "uniform half OffsetMapping_Bias;\n"
346 "#endif\n"
347 "#ifdef USESPECULAR\n"
348 "uniform half SpecularPower;\n"
349 "#endif\n"
350 "#ifdef USEFOG\n"
351 "uniform half FogRangeRecip;\n"
352 "#endif\n"
353 "uniform half AmbientScale;\n"
354 "uniform half DiffuseScale;\n"
355 "#ifdef USESPECULAR\n"
356 "uniform half SpecularScale;\n"
357 "#endif\n"
358 "\n"
359 "uniform sampler2D Texture_Normal;\n"
360 "uniform sampler2D Texture_Color;\n"
361 "#ifdef USESPECULAR\n"
362 "uniform sampler2D Texture_Gloss;\n"
363 "#endif\n"
364 "#ifdef USECUBEFILTER\n"
365 "uniform samplerCube Texture_Cube;\n"
366 "#endif\n"
367 "#ifdef USEFOG\n"
368 "uniform sampler2D Texture_FogMask;\n"
369 "#endif\n"
370 "\n"
371 "varying vec2 TexCoord;\n"
372 "varying vec3 CubeVector;\n"
373 "varying vec3 LightVector;\n"
374 "#if defined(USESPECULAR) || defined(USEFOG) || defined(USEOFFSETMAPPING)\n"
375 "varying vec3 EyeVector;\n"
376 "#endif\n"
377 "\n"
378 "void main(void)\n"
379 "{\n"
380 "       // attenuation\n"
381 "       //\n"
382 "       // the attenuation is (1-(x*x+y*y+z*z)) which gives a large bright\n"
383 "       // center and sharp falloff at the edge, this is about the most efficient\n"
384 "       // we can get away with as far as providing illumination.\n"
385 "       //\n"
386 "       // pow(1-(x*x+y*y+z*z), 4) is far more realistic but needs large lights to\n"
387 "       // provide significant illumination, large = slow = pain.\n"
388 "       half colorscale = max(1.0 - dot(CubeVector, CubeVector), 0.0);\n"
389 "\n"
390 "#ifdef USEFOG\n"
391 "       // apply fog\n"
392 "       colorscale *= texture2D(Texture_FogMask, hvec2(length(EyeVector)*FogRangeRecip, 0)).x;\n"
393 "#endif\n"
394 "\n"
395 "#ifdef USEOFFSETMAPPING\n"
396 "       // this is 3 sample because of ATI Radeon 9500-9800/X300 limits\n"
397 "       hvec2 OffsetVector = normalize(EyeVector).xy * vec2(-0.333, 0.333);\n"
398 "       hvec2 TexCoordOffset = TexCoord + OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoord).w);\n"
399 "       TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
400 "       TexCoordOffset += OffsetVector * (OffsetMapping_Bias + OffsetMapping_Scale * texture2D(Texture_Normal, TexCoordOffset).w);\n"
401 "#define TexCoord TexCoordOffset\n"
402 "#endif\n"
403 "\n"
404 "       // get the surface normal\n"
405 "#ifdef SURFACENORMALIZE\n"
406 "       hvec3 surfacenormal = normalize(hvec3(texture2D(Texture_Normal, TexCoord)) - 0.5);\n"
407 "#else\n"
408 "       hvec3 surfacenormal = -1.0 + 2.0 * hvec3(texture2D(Texture_Normal, TexCoord));\n"
409 "#endif\n"
410 "\n"
411 "       // calculate shading\n"
412 "       hvec3 diffusenormal = hvec3(normalize(LightVector));\n"
413 "       hvec3 color = hvec3(texture2D(Texture_Color, TexCoord)) * (AmbientScale + DiffuseScale * max(dot(surfacenormal, diffusenormal), 0.0));\n"
414 "#ifdef USESPECULAR\n"
415 "       hvec3 specularnormal = hvec3(normalize(diffusenormal + hvec3(normalize(EyeVector))));\n"
416 "       color += hvec3(texture2D(Texture_Gloss, TexCoord)) * (SpecularScale * pow(max(dot(surfacenormal, specularnormal), 0.0), SpecularPower));\n"
417 "#endif\n"
418 "\n"
419 "#ifdef USECUBEFILTER\n"
420 "       // apply light cubemap filter\n"
421 "       color *= hvec3(textureCube(Texture_Cube, CubeVector));\n"
422 "#endif\n"
423 "\n"
424 "       // calculate fragment color (apply light color and attenuation/fog scaling)\n"
425 "       gl_FragColor = hvec4(color * LightColor * colorscale, 1);\n"
426 "}\n"
427 ;
428
429 void r_shadow_start(void)
430 {
431         int i;
432         // if not a GeForce FX, turn off the lowquality cvar
433         if (strncmp(gl_renderer, "GeForce FX ", strlen("GeForce FX ")))
434                 Cvar_SetValue("r_shadow_glsl_geforcefxlowquality", 0);
435         // allocate vertex processing arrays
436         numcubemaps = 0;
437         r_shadow_attenuation2dtexture = NULL;
438         r_shadow_attenuation3dtexture = NULL;
439         r_shadow_texturepool = NULL;
440         r_shadow_filters_texturepool = NULL;
441         R_Shadow_ValidateCvars();
442         R_Shadow_MakeTextures();
443         maxshadowelements = 0;
444         shadowelements = NULL;
445         maxvertexupdate = 0;
446         vertexupdate = NULL;
447         vertexremap = NULL;
448         vertexupdatenum = 0;
449         maxshadowmark = 0;
450         numshadowmark = 0;
451         shadowmark = NULL;
452         shadowmarklist = NULL;
453         shadowmarkcount = 0;
454         r_shadow_buffer_numleafpvsbytes = 0;
455         r_shadow_buffer_leafpvs = NULL;
456         r_shadow_buffer_leaflist = NULL;
457         r_shadow_buffer_numsurfacepvsbytes = 0;
458         r_shadow_buffer_surfacepvs = NULL;
459         r_shadow_buffer_surfacelist = NULL;
460         for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
461                 r_shadow_program_light[i] = 0;
462         if (gl_support_fragment_shader)
463         {
464                 char *vertstring, *fragstring;
465                 int vertstrings_count;
466                 int fragstrings_count;
467                 const char *vertstrings_list[SHADERPERMUTATION_COUNT+1];
468                 const char *fragstrings_list[SHADERPERMUTATION_COUNT+1];
469                 vertstring = (char *)FS_LoadFile("glsl/light.vert", tempmempool, false);
470                 fragstring = (char *)FS_LoadFile("glsl/light.frag", tempmempool, false);
471                 for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
472                 {
473                         vertstrings_count = 0;
474                         fragstrings_count = 0;
475                         if (i & SHADERPERMUTATION_SPECULAR)
476                         {
477                                 vertstrings_list[vertstrings_count++] = "#define USESPECULAR\n";
478                                 fragstrings_list[fragstrings_count++] = "#define USESPECULAR\n";
479                         }
480                         if (i & SHADERPERMUTATION_FOG)
481                         {
482                                 vertstrings_list[vertstrings_count++] = "#define USEFOG\n";
483                                 fragstrings_list[fragstrings_count++] = "#define USEFOG\n";
484                         }
485                         if (i & SHADERPERMUTATION_CUBEFILTER)
486                         {
487                                 vertstrings_list[vertstrings_count++] = "#define USECUBEFILTER\n";
488                                 fragstrings_list[fragstrings_count++] = "#define USECUBEFILTER\n";
489                         }
490                         if (i & SHADERPERMUTATION_OFFSETMAPPING)
491                         {
492                                 vertstrings_list[vertstrings_count++] = "#define USEOFFSETMAPPING\n";
493                                 fragstrings_list[fragstrings_count++] = "#define USEOFFSETMAPPING\n";
494                         }
495                         if (i & SHADERPERMUTATION_SURFACENORMALIZE)
496                         {
497                                 vertstrings_list[vertstrings_count++] = "#define SURFACENORMALIZE\n";
498                                 fragstrings_list[fragstrings_count++] = "#define SURFACENORMALIZE\n";
499                         }
500                         if (i & SHADERPERMUTATION_GEFORCEFX)
501                         {
502                                 vertstrings_list[vertstrings_count++] = "#define GEFORCEFX\n";
503                                 fragstrings_list[fragstrings_count++] = "#define GEFORCEFX\n";
504                         }
505                         vertstrings_list[vertstrings_count++] = vertstring ? vertstring : builtinshader_light_vert;
506                         fragstrings_list[fragstrings_count++] = fragstring ? fragstring : builtinshader_light_frag;
507                         r_shadow_program_light[i] = GL_Backend_CompileProgram(vertstrings_count, vertstrings_list, fragstrings_count, fragstrings_list);
508                         if (!r_shadow_program_light[i])
509                         {
510                                 Con_Printf("permutation %s %s %s %s failed for shader %s, some features may not work properly!\n", i & 1 ? "specular" : "", i & 2 ? "fog" : "", i & 4 ? "cubefilter" : "", i & 8 ? "offsetmapping" : "", "glsl/light");
511                                 continue;
512                         }
513                         qglUseProgramObjectARB(r_shadow_program_light[i]);
514                         qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Normal"), 0);CHECKGLERROR
515                         qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Color"), 1);CHECKGLERROR
516                         if (i & SHADERPERMUTATION_SPECULAR)
517                         {
518                                 qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Gloss"), 2);CHECKGLERROR
519                         }
520                         if (i & SHADERPERMUTATION_CUBEFILTER)
521                         {
522                                 qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_Cube"), 3);CHECKGLERROR
523                         }
524                         if (i & SHADERPERMUTATION_FOG)
525                         {
526                                 qglUniform1iARB(qglGetUniformLocationARB(r_shadow_program_light[i], "Texture_FogMask"), 4);CHECKGLERROR
527                         }
528                 }
529                 qglUseProgramObjectARB(0);
530                 if (fragstring)
531                         Mem_Free(fragstring);
532                 if (vertstring)
533                         Mem_Free(vertstring);
534         }
535 }
536
537 void r_shadow_shutdown(void)
538 {
539         int i;
540         R_Shadow_UncompileWorldLights();
541         for (i = 0;i < SHADERPERMUTATION_COUNT;i++)
542         {
543                 if (r_shadow_program_light[i])
544                 {
545                         GL_Backend_FreeProgram(r_shadow_program_light[i]);
546                         r_shadow_program_light[i] = 0;
547                 }
548         }
549         numcubemaps = 0;
550         r_shadow_attenuation2dtexture = NULL;
551         r_shadow_attenuation3dtexture = NULL;
552         R_FreeTexturePool(&r_shadow_texturepool);
553         R_FreeTexturePool(&r_shadow_filters_texturepool);
554         maxshadowelements = 0;
555         if (shadowelements)
556                 Mem_Free(shadowelements);
557         shadowelements = NULL;
558         maxvertexupdate = 0;
559         if (vertexupdate)
560                 Mem_Free(vertexupdate);
561         vertexupdate = NULL;
562         if (vertexremap)
563                 Mem_Free(vertexremap);
564         vertexremap = NULL;
565         vertexupdatenum = 0;
566         maxshadowmark = 0;
567         numshadowmark = 0;
568         if (shadowmark)
569                 Mem_Free(shadowmark);
570         shadowmark = NULL;
571         if (shadowmarklist)
572                 Mem_Free(shadowmarklist);
573         shadowmarklist = NULL;
574         shadowmarkcount = 0;
575         r_shadow_buffer_numleafpvsbytes = 0;
576         if (r_shadow_buffer_leafpvs)
577                 Mem_Free(r_shadow_buffer_leafpvs);
578         r_shadow_buffer_leafpvs = NULL;
579         if (r_shadow_buffer_leaflist)
580                 Mem_Free(r_shadow_buffer_leaflist);
581         r_shadow_buffer_leaflist = NULL;
582         r_shadow_buffer_numsurfacepvsbytes = 0;
583         if (r_shadow_buffer_surfacepvs)
584                 Mem_Free(r_shadow_buffer_surfacepvs);
585         r_shadow_buffer_surfacepvs = NULL;
586         if (r_shadow_buffer_surfacelist)
587                 Mem_Free(r_shadow_buffer_surfacelist);
588         r_shadow_buffer_surfacelist = NULL;
589 }
590
591 void r_shadow_newmap(void)
592 {
593 }
594
595 void R_Shadow_Help_f(void)
596 {
597         Con_Printf(
598 "Documentation on r_shadow system:\n"
599 "Settings:\n"
600 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
601 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
602 "r_shadow_debuglight : render only this light number (-1 = all)\n"
603 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
604 "r_shadow_gloss2intensity : brightness of forced gloss\n"
605 "r_shadow_glossintensity : brightness of textured gloss\n"
606 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
607 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
608 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
609 "r_shadow_portallight : use portal visibility for static light precomputation\n"
610 "r_shadow_projectdistance : shadow volume projection distance\n"
611 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
612 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
613 "r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
614 "r_shadow_realtime_world : use high quality world lighting mode\n"
615 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
616 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
617 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
618 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
619 "r_shadow_realtime_world_compilelight : compile lighting geometry\n"
620 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
621 "r_shadow_glsl : use OpenGL Shading Language for lighting\n"
622 "r_shadow_glsl_offsetmapping : enables Offset Mapping bumpmap enhancement\n"
623 "r_shadow_glsl_offsetmapping_scale : controls depth of Offset Mapping\n"
624 "r_shadow_glsl_offsetmapping_bias : should be negative half of scale\n"
625 "r_shadow_glsl_geforcefxlowquality : use lower quality lighting\n"
626 "r_shadow_glsl_surfacenormalize : makes bumpmapping slightly higher quality\n"
627 "r_shadow_scissor : use scissor optimization\n"
628 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
629 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
630 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
631 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
632 "r_shadow_visiblelighting : useful for performance testing; bright = slow!\n"
633 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
634 "Commands:\n"
635 "r_shadow_help : this help\n"
636         );
637 }
638
639 void R_Shadow_Init(void)
640 {
641         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
642         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
643         Cvar_RegisterVariable(&r_shadow_debuglight);
644         Cvar_RegisterVariable(&r_shadow_gloss);
645         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
646         Cvar_RegisterVariable(&r_shadow_glossintensity);
647         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
648         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
649         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
650         Cvar_RegisterVariable(&r_shadow_portallight);
651         Cvar_RegisterVariable(&r_shadow_projectdistance);
652         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
653         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
654         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
655         Cvar_RegisterVariable(&r_shadow_realtime_world);
656         Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
657         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
658         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
659         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
660         Cvar_RegisterVariable(&r_shadow_realtime_world_compilelight);
661         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
662         Cvar_RegisterVariable(&r_shadow_scissor);
663         Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
664         Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
665         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
666         Cvar_RegisterVariable(&r_shadow_texture3d);
667         Cvar_RegisterVariable(&r_shadow_visiblelighting);
668         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
669         Cvar_RegisterVariable(&r_shadow_glsl);
670         Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping);
671         Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_scale);
672         Cvar_RegisterVariable(&r_shadow_glsl_offsetmapping_bias);
673         Cvar_RegisterVariable(&r_shadow_glsl_geforcefxlowquality);
674         Cvar_RegisterVariable(&r_shadow_glsl_surfacenormalize);
675         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
676         if (gamemode == GAME_TENEBRAE)
677         {
678                 Cvar_SetValue("r_shadow_gloss", 2);
679                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
680         }
681         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
682         R_Shadow_EditLights_Init();
683         r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
684         r_shadow_worldlightchain = NULL;
685         maxshadowelements = 0;
686         shadowelements = NULL;
687         maxvertexupdate = 0;
688         vertexupdate = NULL;
689         vertexremap = NULL;
690         vertexupdatenum = 0;
691         maxshadowmark = 0;
692         numshadowmark = 0;
693         shadowmark = NULL;
694         shadowmarklist = NULL;
695         shadowmarkcount = 0;
696         r_shadow_buffer_numleafpvsbytes = 0;
697         r_shadow_buffer_leafpvs = NULL;
698         r_shadow_buffer_leaflist = NULL;
699         r_shadow_buffer_numsurfacepvsbytes = 0;
700         r_shadow_buffer_surfacepvs = NULL;
701         r_shadow_buffer_surfacelist = NULL;
702         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
703 }
704
705 matrix4x4_t matrix_attenuationxyz =
706 {
707         {
708                 {0.5, 0.0, 0.0, 0.5},
709                 {0.0, 0.5, 0.0, 0.5},
710                 {0.0, 0.0, 0.5, 0.5},
711                 {0.0, 0.0, 0.0, 1.0}
712         }
713 };
714
715 matrix4x4_t matrix_attenuationz =
716 {
717         {
718                 {0.0, 0.0, 0.5, 0.5},
719                 {0.0, 0.0, 0.0, 0.5},
720                 {0.0, 0.0, 0.0, 0.5},
721                 {0.0, 0.0, 0.0, 1.0}
722         }
723 };
724
725 int *R_Shadow_ResizeShadowElements(int numtris)
726 {
727         // make sure shadowelements is big enough for this volume
728         if (maxshadowelements < numtris * 24)
729         {
730                 maxshadowelements = numtris * 24;
731                 if (shadowelements)
732                         Mem_Free(shadowelements);
733                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
734         }
735         return shadowelements;
736 }
737
738 static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
739 {
740         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
741         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
742         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
743         {
744                 if (r_shadow_buffer_leafpvs)
745                         Mem_Free(r_shadow_buffer_leafpvs);
746                 if (r_shadow_buffer_leaflist)
747                         Mem_Free(r_shadow_buffer_leaflist);
748                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
749                 r_shadow_buffer_leafpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
750                 r_shadow_buffer_leaflist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
751         }
752         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
753         {
754                 if (r_shadow_buffer_surfacepvs)
755                         Mem_Free(r_shadow_buffer_surfacepvs);
756                 if (r_shadow_buffer_surfacelist)
757                         Mem_Free(r_shadow_buffer_surfacelist);
758                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
759                 r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
760                 r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
761         }
762 }
763
764 void R_Shadow_PrepareShadowMark(int numtris)
765 {
766         // make sure shadowmark is big enough for this volume
767         if (maxshadowmark < numtris)
768         {
769                 maxshadowmark = numtris;
770                 if (shadowmark)
771                         Mem_Free(shadowmark);
772                 if (shadowmarklist)
773                         Mem_Free(shadowmarklist);
774                 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
775                 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
776                 shadowmarkcount = 0;
777         }
778         shadowmarkcount++;
779         // if shadowmarkcount wrapped we clear the array and adjust accordingly
780         if (shadowmarkcount == 0)
781         {
782                 shadowmarkcount = 1;
783                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
784         }
785         numshadowmark = 0;
786 }
787
788 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
789 {
790         int i, j;
791         int outtriangles = 0, outvertices = 0;
792         const int *element;
793         const float *vertex;
794
795         if (maxvertexupdate < innumvertices)
796         {
797                 maxvertexupdate = innumvertices;
798                 if (vertexupdate)
799                         Mem_Free(vertexupdate);
800                 if (vertexremap)
801                         Mem_Free(vertexremap);
802                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
803                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
804                 vertexupdatenum = 0;
805         }
806         vertexupdatenum++;
807         if (vertexupdatenum == 0)
808         {
809                 vertexupdatenum = 1;
810                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
811                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
812         }
813
814         for (i = 0;i < numshadowmarktris;i++)
815                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
816
817         for (i = 0;i < numshadowmarktris;i++)
818         {
819                 element = inelement3i + shadowmarktris[i] * 3;
820                 // make sure the vertices are created
821                 for (j = 0;j < 3;j++)
822                 {
823                         if (vertexupdate[element[j]] != vertexupdatenum)
824                         {
825                                 float ratio, direction[3];
826                                 vertexupdate[element[j]] = vertexupdatenum;
827                                 vertexremap[element[j]] = outvertices;
828                                 vertex = invertex3f + element[j] * 3;
829                                 // project one copy of the vertex to the sphere radius of the light
830                                 // (FIXME: would projecting it to the light box be better?)
831                                 VectorSubtract(vertex, projectorigin, direction);
832                                 ratio = projectdistance / VectorLength(direction);
833                                 VectorCopy(vertex, outvertex3f);
834                                 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
835                                 outvertex3f += 6;
836                                 outvertices += 2;
837                         }
838                 }
839         }
840
841         for (i = 0;i < numshadowmarktris;i++)
842         {
843                 int remappedelement[3];
844                 int markindex;
845                 const int *neighbortriangle;
846
847                 markindex = shadowmarktris[i] * 3;
848                 element = inelement3i + markindex;
849                 neighbortriangle = inneighbor3i + markindex;
850                 // output the front and back triangles
851                 outelement3i[0] = vertexremap[element[0]];
852                 outelement3i[1] = vertexremap[element[1]];
853                 outelement3i[2] = vertexremap[element[2]];
854                 outelement3i[3] = vertexremap[element[2]] + 1;
855                 outelement3i[4] = vertexremap[element[1]] + 1;
856                 outelement3i[5] = vertexremap[element[0]] + 1;
857
858                 outelement3i += 6;
859                 outtriangles += 2;
860                 // output the sides (facing outward from this triangle)
861                 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
862                 {
863                         remappedelement[0] = vertexremap[element[0]];
864                         remappedelement[1] = vertexremap[element[1]];
865                         outelement3i[0] = remappedelement[1];
866                         outelement3i[1] = remappedelement[0];
867                         outelement3i[2] = remappedelement[0] + 1;
868                         outelement3i[3] = remappedelement[1];
869                         outelement3i[4] = remappedelement[0] + 1;
870                         outelement3i[5] = remappedelement[1] + 1;
871
872                         outelement3i += 6;
873                         outtriangles += 2;
874                 }
875                 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
876                 {
877                         remappedelement[1] = vertexremap[element[1]];
878                         remappedelement[2] = vertexremap[element[2]];
879                         outelement3i[0] = remappedelement[2];
880                         outelement3i[1] = remappedelement[1];
881                         outelement3i[2] = remappedelement[1] + 1;
882                         outelement3i[3] = remappedelement[2];
883                         outelement3i[4] = remappedelement[1] + 1;
884                         outelement3i[5] = remappedelement[2] + 1;
885
886                         outelement3i += 6;
887                         outtriangles += 2;
888                 }
889                 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
890                 {
891                         remappedelement[0] = vertexremap[element[0]];
892                         remappedelement[2] = vertexremap[element[2]];
893                         outelement3i[0] = remappedelement[0];
894                         outelement3i[1] = remappedelement[2];
895                         outelement3i[2] = remappedelement[2] + 1;
896                         outelement3i[3] = remappedelement[0];
897                         outelement3i[4] = remappedelement[2] + 1;
898                         outelement3i[5] = remappedelement[0] + 1;
899
900                         outelement3i += 6;
901                         outtriangles += 2;
902                 }
903         }
904         if (outnumvertices)
905                 *outnumvertices = outvertices;
906         return outtriangles;
907 }
908
909 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
910 {
911         int tris, outverts;
912         if (projectdistance < 0.1)
913         {
914                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
915                 return;
916         }
917         if (!numverts || !nummarktris)
918                 return;
919         // make sure shadowelements is big enough for this volume
920         if (maxshadowelements < nummarktris * 24)
921                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
922         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
923         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
924 }
925
926 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
927 {
928         int t, tend;
929         const int *e;
930         const float *v[3];
931         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
932                 return;
933         tend = firsttriangle + numtris;
934         if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
935          && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
936          && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
937         {
938                 // surface box entirely inside light box, no box cull
939                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
940                         if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
941                                 shadowmarklist[numshadowmark++] = t;
942         }
943         else
944         {
945                 // surface box not entirely inside light box, cull each triangle
946                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
947                 {
948                         v[0] = invertex3f + e[0] * 3;
949                         v[1] = invertex3f + e[1] * 3;
950                         v[2] = invertex3f + e[2] * 3;
951                         if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
952                          && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
953                          && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
954                          && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
955                          && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
956                          && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
957                          && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
958                                 shadowmarklist[numshadowmark++] = t;
959                 }
960         }
961 }
962
963 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
964 {
965         rmeshstate_t m;
966         if (r_shadow_compilingrtlight)
967         {
968                 // if we're compiling an rtlight, capture the mesh
969                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
970                 return;
971         }
972         memset(&m, 0, sizeof(m));
973         m.pointer_vertex = vertex3f;
974         R_Mesh_State(&m);
975         GL_LockArrays(0, numvertices);
976         if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
977         {
978                 // decrement stencil if backface is behind depthbuffer
979                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
980                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
981                 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
982                 c_rt_shadowmeshes++;
983                 c_rt_shadowtris += numtriangles;
984                 // increment stencil if frontface is behind depthbuffer
985                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
986                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
987         }
988         R_Mesh_Draw(0, numvertices, numtriangles, element3i);
989         c_rt_shadowmeshes++;
990         c_rt_shadowtris += numtriangles;
991         GL_LockArrays(0, 0);
992 }
993
994 static void R_Shadow_MakeTextures(void)
995 {
996         int x, y, z, d;
997         float v[3], intensity;
998         qbyte *data;
999         R_FreeTexturePool(&r_shadow_texturepool);
1000         r_shadow_texturepool = R_AllocTexturePool();
1001         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
1002         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
1003 #define ATTEN2DSIZE 64
1004 #define ATTEN3DSIZE 32
1005         data = Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
1006         for (y = 0;y < ATTEN2DSIZE;y++)
1007         {
1008                 for (x = 0;x < ATTEN2DSIZE;x++)
1009                 {
1010                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
1011                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
1012                         v[2] = 0;
1013                         intensity = 1.0f - sqrt(DotProduct(v, v));
1014                         if (intensity > 0)
1015                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
1016                         d = bound(0, intensity, 255);
1017                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
1018                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
1019                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
1020                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
1021                 }
1022         }
1023         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
1024         if (r_shadow_texture3d.integer)
1025         {
1026                 for (z = 0;z < ATTEN3DSIZE;z++)
1027                 {
1028                         for (y = 0;y < ATTEN3DSIZE;y++)
1029                         {
1030                                 for (x = 0;x < ATTEN3DSIZE;x++)
1031                                 {
1032                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
1033                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
1034                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
1035                                         intensity = 1.0f - sqrt(DotProduct(v, v));
1036                                         if (intensity > 0)
1037                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
1038                                         d = bound(0, intensity, 255);
1039                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
1040                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
1041                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
1042                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
1043                                 }
1044                         }
1045                 }
1046                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
1047         }
1048         Mem_Free(data);
1049 }
1050
1051 void R_Shadow_ValidateCvars(void)
1052 {
1053         if (r_shadow_texture3d.integer && !gl_texture3d)
1054                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
1055         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
1056                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
1057 }
1058
1059 // light currently being rendered
1060 static rtlight_t *r_shadow_rtlight;
1061 // light filter cubemap being used by the light
1062 static rtexture_t *r_shadow_lightcubemap;
1063
1064 // this is the location of the eye in entity space
1065 static vec3_t r_shadow_entityeyeorigin;
1066 // this is the location of the light in entity space
1067 static vec3_t r_shadow_entitylightorigin;
1068 // this transforms entity coordinates to light filter cubemap coordinates
1069 // (also often used for other purposes)
1070 static matrix4x4_t r_shadow_entitytolight;
1071 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
1072 // of attenuation texturing in full 3D (Z result often ignored)
1073 static matrix4x4_t r_shadow_entitytoattenuationxyz;
1074 // this transforms only the Z to S, and T is always 0.5
1075 static matrix4x4_t r_shadow_entitytoattenuationz;
1076 // rtlight->color * r_dlightstylevalue[rtlight->style] / 256 * r_shadow_lightintensityscale.value * ent->colormod * ent->alpha
1077 static vec3_t r_shadow_entitylightcolor;
1078
1079 static int r_shadow_lightpermutation;
1080 static int r_shadow_lightprog;
1081
1082 void R_Shadow_Stage_Begin(void)
1083 {
1084         rmeshstate_t m;
1085
1086         R_Shadow_ValidateCvars();
1087
1088         if (!r_shadow_attenuation2dtexture
1089          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
1090          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
1091          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
1092                 R_Shadow_MakeTextures();
1093
1094         memset(&m, 0, sizeof(m));
1095         GL_BlendFunc(GL_ONE, GL_ZERO);
1096         GL_DepthMask(false);
1097         GL_DepthTest(true);
1098         R_Mesh_State(&m);
1099         GL_Color(0, 0, 0, 1);
1100         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1101         qglEnable(GL_CULL_FACE);
1102         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1103         r_shadowstage = R_SHADOWSTAGE_NONE;
1104 }
1105
1106 void R_Shadow_Stage_ActiveLight(rtlight_t *rtlight)
1107 {
1108         r_shadow_rtlight = rtlight;
1109 }
1110
1111 void R_Shadow_Stage_Reset(void)
1112 {
1113         rmeshstate_t m;
1114         if (gl_support_stenciltwoside)
1115                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1116         if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
1117         {
1118                 qglUseProgramObjectARB(0);
1119                 // HACK HACK HACK: work around for stupid NVIDIA bug that causes GL_OUT_OF_MEMORY and/or software rendering in 6xxx drivers
1120                 qglBegin(GL_TRIANGLES);
1121                 qglEnd();
1122                 CHECKGLERROR
1123         }
1124         memset(&m, 0, sizeof(m));
1125         R_Mesh_State(&m);
1126 }
1127
1128 void R_Shadow_Stage_StencilShadowVolumes(void)
1129 {
1130         R_Shadow_Stage_Reset();
1131         GL_Color(1, 1, 1, 1);
1132         GL_ColorMask(0, 0, 0, 0);
1133         GL_BlendFunc(GL_ONE, GL_ZERO);
1134         GL_DepthMask(false);
1135         GL_DepthTest(true);
1136         qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
1137         //if (r_shadow_shadow_polygonoffset.value != 0)
1138         //{
1139         //      qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
1140         //      qglEnable(GL_POLYGON_OFFSET_FILL);
1141         //}
1142         //else
1143         //      qglDisable(GL_POLYGON_OFFSET_FILL);
1144         qglDepthFunc(GL_LESS);
1145         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1146         qglEnable(GL_STENCIL_TEST);
1147         qglStencilFunc(GL_ALWAYS, 128, ~0);
1148         if (gl_ext_stenciltwoside.integer)
1149         {
1150                 r_shadowstage = R_SHADOWSTAGE_STENCILTWOSIDE;
1151                 qglDisable(GL_CULL_FACE);
1152                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1153                 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
1154                 qglStencilMask(~0);
1155                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
1156                 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
1157                 qglStencilMask(~0);
1158                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
1159         }
1160         else
1161         {
1162                 r_shadowstage = R_SHADOWSTAGE_STENCIL;
1163                 qglEnable(GL_CULL_FACE);
1164                 qglStencilMask(~0);
1165                 // this is changed by every shadow render so its value here is unimportant
1166                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1167         }
1168         GL_Clear(GL_STENCIL_BUFFER_BIT);
1169         c_rt_clears++;
1170 }
1171
1172 void R_Shadow_Stage_Lighting(int stenciltest)
1173 {
1174         rmeshstate_t m;
1175         R_Shadow_Stage_Reset();
1176         GL_BlendFunc(GL_ONE, GL_ONE);
1177         GL_DepthMask(false);
1178         GL_DepthTest(true);
1179         qglPolygonOffset(0, 0);
1180         //qglDisable(GL_POLYGON_OFFSET_FILL);
1181         GL_Color(1, 1, 1, 1);
1182         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1183         qglDepthFunc(GL_EQUAL);
1184         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1185         qglEnable(GL_CULL_FACE);
1186         if (r_shadowstage == R_SHADOWSTAGE_STENCIL || r_shadowstage == R_SHADOWSTAGE_STENCILTWOSIDE)
1187                 qglEnable(GL_STENCIL_TEST);
1188         else
1189                 qglDisable(GL_STENCIL_TEST);
1190         qglStencilMask(~0);
1191         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1192         // only draw light where this geometry was already rendered AND the
1193         // stencil is 128 (values other than this mean shadow)
1194         qglStencilFunc(GL_EQUAL, 128, ~0);
1195         if (r_shadow_glsl.integer && r_shadow_program_light[0])
1196         {
1197                 r_shadowstage = R_SHADOWSTAGE_LIGHT_GLSL;
1198                 memset(&m, 0, sizeof(m));
1199                 m.pointer_vertex = varray_vertex3f;
1200                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1201                 m.pointer_texcoord3f[1] = varray_svector3f;
1202                 m.pointer_texcoord3f[2] = varray_tvector3f;
1203                 m.pointer_texcoord3f[3] = varray_normal3f;
1204                 m.tex[0] = R_GetTexture(r_texture_blanknormalmap); // normal
1205                 m.tex[1] = R_GetTexture(r_texture_white); // diffuse
1206                 m.tex[2] = R_GetTexture(r_texture_white); // gloss
1207                 m.texcubemap[3] = R_GetTexture(r_shadow_lightcubemap); // light filter
1208                 // TODO: support fog (after renderer is converted to texture fog)
1209                 m.tex[4] = R_GetTexture(r_texture_white); // fog
1210                 //m.texmatrix[3] = r_shadow_entitytolight; // light filter matrix
1211                 R_Mesh_State(&m);
1212                 GL_BlendFunc(GL_ONE, GL_ONE);
1213                 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1214                 CHECKGLERROR
1215                 r_shadow_lightpermutation = 0;
1216                 // only add a feature to the permutation if that permutation exists
1217                 // (otherwise it might end up not using a shader at all, which looks
1218                 // worse than using less features)
1219                 if (r_shadow_rtlight->specularscale && r_shadow_gloss.integer >= 1 && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SPECULAR])
1220                         r_shadow_lightpermutation |= SHADERPERMUTATION_SPECULAR;
1221                 //if (fog && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_FOG])
1222                 //      r_shadow_lightpermutation |= SHADERPERMUTATION_FOG;
1223                 if (r_shadow_lightcubemap != r_texture_whitecube && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_CUBEFILTER])
1224                         r_shadow_lightpermutation |= SHADERPERMUTATION_CUBEFILTER;
1225                 if (r_shadow_glsl_offsetmapping.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_OFFSETMAPPING])
1226                         r_shadow_lightpermutation |= SHADERPERMUTATION_OFFSETMAPPING;
1227                 if (r_shadow_glsl_surfacenormalize.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_SURFACENORMALIZE])
1228                         r_shadow_lightpermutation |= SHADERPERMUTATION_SURFACENORMALIZE;
1229                 if (r_shadow_glsl_geforcefxlowquality.integer && r_shadow_program_light[r_shadow_lightpermutation | SHADERPERMUTATION_GEFORCEFX])
1230                         r_shadow_lightpermutation |= SHADERPERMUTATION_GEFORCEFX;
1231                 r_shadow_lightprog = r_shadow_program_light[r_shadow_lightpermutation];
1232                 qglUseProgramObjectARB(r_shadow_lightprog);CHECKGLERROR
1233                 // TODO: support fog (after renderer is converted to texture fog)
1234                 if (r_shadow_lightpermutation & SHADERPERMUTATION_FOG)
1235                 {
1236                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "FogRangeRecip"), 0);CHECKGLERROR
1237                 }
1238                 qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "AmbientScale"), r_shadow_rtlight->ambientscale);CHECKGLERROR
1239                 qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "DiffuseScale"), r_shadow_rtlight->diffusescale);CHECKGLERROR
1240                 if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
1241                 {
1242                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularPower"), 8);CHECKGLERROR
1243                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), r_shadow_rtlight->specularscale);CHECKGLERROR
1244                 }
1245                 //qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
1246                 //qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), relativelightorigin[0], relativelightorigin[1], relativelightorigin[2]);CHECKGLERROR
1247                 //if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
1248                 //{
1249                 //      qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), relativeeyeorigin[0], relativeeyeorigin[1], relativeeyeorigin[2]);CHECKGLERROR
1250                 //}
1251                 if (r_shadow_lightpermutation & SHADERPERMUTATION_OFFSETMAPPING)
1252                 {
1253                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Scale"), r_shadow_glsl_offsetmapping_scale.value);CHECKGLERROR
1254                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "OffsetMapping_Bias"), r_shadow_glsl_offsetmapping_bias.value);CHECKGLERROR
1255                 }
1256         }
1257         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1258                 r_shadowstage = R_SHADOWSTAGE_LIGHT_DOT3;
1259         else
1260                 r_shadowstage = R_SHADOWSTAGE_LIGHT_VERTEX;
1261 }
1262
1263 void R_Shadow_Stage_VisibleShadowVolumes(void)
1264 {
1265         R_Shadow_Stage_Reset();
1266         GL_BlendFunc(GL_ONE, GL_ONE);
1267         GL_DepthMask(false);
1268         GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
1269         qglPolygonOffset(0, 0);
1270         GL_Color(0.0, 0.0125, 0.1, 1);
1271         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1272         qglDepthFunc(GL_GEQUAL);
1273         qglCullFace(GL_FRONT); // this culls back
1274         qglDisable(GL_CULL_FACE);
1275         qglDisable(GL_STENCIL_TEST);
1276         r_shadowstage = R_SHADOWSTAGE_VISIBLEVOLUMES;
1277 }
1278
1279 void R_Shadow_Stage_VisibleLighting(int stenciltest)
1280 {
1281         R_Shadow_Stage_Reset();
1282         GL_BlendFunc(GL_ONE, GL_ONE);
1283         GL_DepthMask(false);
1284         GL_DepthTest(r_shadow_visiblelighting.integer < 2);
1285         qglPolygonOffset(0, 0);
1286         GL_Color(0.1, 0.0125, 0, 1);
1287         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1288         qglDepthFunc(GL_EQUAL);
1289         qglCullFace(GL_FRONT); // this culls back
1290         qglEnable(GL_CULL_FACE);
1291         if (stenciltest)
1292                 qglEnable(GL_STENCIL_TEST);
1293         else
1294                 qglDisable(GL_STENCIL_TEST);
1295         r_shadowstage = R_SHADOWSTAGE_VISIBLELIGHTING;
1296 }
1297
1298 void R_Shadow_Stage_End(void)
1299 {
1300         R_Shadow_Stage_Reset();
1301         R_Shadow_Stage_ActiveLight(NULL);
1302         GL_BlendFunc(GL_ONE, GL_ZERO);
1303         GL_DepthMask(true);
1304         GL_DepthTest(true);
1305         qglPolygonOffset(0, 0);
1306         //qglDisable(GL_POLYGON_OFFSET_FILL);
1307         GL_Color(1, 1, 1, 1);
1308         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1309         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1310         qglDepthFunc(GL_LEQUAL);
1311         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1312         qglDisable(GL_STENCIL_TEST);
1313         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1314         if (gl_support_stenciltwoside)
1315                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1316         qglStencilMask(~0);
1317         qglStencilFunc(GL_ALWAYS, 128, ~0);
1318         r_shadowstage = R_SHADOWSTAGE_NONE;
1319 }
1320
1321 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1322 {
1323         int i, ix1, iy1, ix2, iy2;
1324         float x1, y1, x2, y2;
1325         vec4_t v, v2;
1326         rmesh_t mesh;
1327         mplane_t planes[11];
1328         float vertex3f[256*3];
1329
1330         // if view is inside the light box, just say yes it's visible
1331         if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1332         {
1333                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1334                 return false;
1335         }
1336
1337         // create a temporary brush describing the area the light can affect in worldspace
1338         VectorNegate(frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -frustum[0].dist;
1339         VectorNegate(frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -frustum[1].dist;
1340         VectorNegate(frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -frustum[2].dist;
1341         VectorNegate(frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -frustum[3].dist;
1342         VectorNegate(frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -frustum[4].dist;
1343         VectorSet   (planes[ 5].normal,  1, 0, 0);         planes[ 5].dist =  maxs[0];
1344         VectorSet   (planes[ 6].normal, -1, 0, 0);         planes[ 6].dist = -mins[0];
1345         VectorSet   (planes[ 7].normal, 0,  1, 0);         planes[ 7].dist =  maxs[1];
1346         VectorSet   (planes[ 8].normal, 0, -1, 0);         planes[ 8].dist = -mins[1];
1347         VectorSet   (planes[ 9].normal, 0, 0,  1);         planes[ 9].dist =  maxs[2];
1348         VectorSet   (planes[10].normal, 0, 0, -1);         planes[10].dist = -mins[2];
1349
1350         // turn the brush into a mesh
1351         memset(&mesh, 0, sizeof(rmesh_t));
1352         mesh.maxvertices = 256;
1353         mesh.vertex3f = vertex3f;
1354         mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
1355         R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
1356
1357         // if that mesh is empty, the light is not visible at all
1358         if (!mesh.numvertices)
1359                 return true;
1360
1361         if (!r_shadow_scissor.integer)
1362                 return false;
1363
1364         // if that mesh is not empty, check what area of the screen it covers
1365         x1 = y1 = x2 = y2 = 0;
1366         v[3] = 1.0f;
1367         for (i = 0;i < mesh.numvertices;i++)
1368         {
1369                 VectorCopy(mesh.vertex3f + i * 3, v);
1370                 GL_TransformToScreen(v, v2);
1371                 //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]);
1372                 if (i)
1373                 {
1374                         if (x1 > v2[0]) x1 = v2[0];
1375                         if (x2 < v2[0]) x2 = v2[0];
1376                         if (y1 > v2[1]) y1 = v2[1];
1377                         if (y2 < v2[1]) y2 = v2[1];
1378                 }
1379                 else
1380                 {
1381                         x1 = x2 = v2[0];
1382                         y1 = y2 = v2[1];
1383                 }
1384         }
1385
1386         // now convert the scissor rectangle to integer screen coordinates
1387         ix1 = x1 - 1.0f;
1388         iy1 = y1 - 1.0f;
1389         ix2 = x2 + 1.0f;
1390         iy2 = y2 + 1.0f;
1391         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1392
1393         // clamp it to the screen
1394         if (ix1 < r_view_x) ix1 = r_view_x;
1395         if (iy1 < r_view_y) iy1 = r_view_y;
1396         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1397         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1398
1399         // if it is inside out, it's not visible
1400         if (ix2 <= ix1 || iy2 <= iy1)
1401                 return true;
1402
1403         // the light area is visible, set up the scissor rectangle
1404         GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
1405         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1406         //qglEnable(GL_SCISSOR_TEST);
1407         c_rt_scissored++;
1408         return false;
1409 }
1410
1411 static void R_Shadow_VertexShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
1412 {
1413         float *color4f = varray_color4f;
1414         float dist, dot, intensity, v[3], n[3];
1415         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1416         {
1417                 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1418                 if ((dist = DotProduct(v, v)) < 1)
1419                 {
1420                         Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1421                         if ((dot = DotProduct(n, v)) > 0)
1422                         {
1423                                 dist = sqrt(dist);
1424                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1425                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1426                                 VectorScale(lightcolor, intensity, color4f);
1427                                 color4f[3] = 1;
1428                         }
1429                         else
1430                         {
1431                                 VectorClear(color4f);
1432                                 color4f[3] = 1;
1433                         }
1434                 }
1435                 else
1436                 {
1437                         VectorClear(color4f);
1438                         color4f[3] = 1;
1439                 }
1440         }
1441 }
1442
1443 static void R_Shadow_VertexShadingWithZAttenuation(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
1444 {
1445         float *color4f = varray_color4f;
1446         float dist, dot, intensity, v[3], n[3];
1447         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1448         {
1449                 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1450                 if ((dist = fabs(v[2])) < 1)
1451                 {
1452                         Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1453                         if ((dot = DotProduct(n, v)) > 0)
1454                         {
1455                                 intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1456                                 intensity *= pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1457                                 VectorScale(lightcolor, intensity, color4f);
1458                                 color4f[3] = 1;
1459                         }
1460                         else
1461                         {
1462                                 VectorClear(color4f);
1463                                 color4f[3] = 1;
1464                         }
1465                 }
1466                 else
1467                 {
1468                         VectorClear(color4f);
1469                         color4f[3] = 1;
1470                 }
1471         }
1472 }
1473
1474 static void R_Shadow_VertexShading(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor)
1475 {
1476         float *color4f = varray_color4f;
1477         float dot, intensity, v[3], n[3];
1478         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1479         {
1480                 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1481                 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1482                 if ((dot = DotProduct(n, v)) > 0)
1483                 {
1484                         intensity = dot / sqrt(VectorLength2(v) * VectorLength2(n));
1485                         VectorScale(lightcolor, intensity, color4f);
1486                         color4f[3] = 1;
1487                 }
1488                 else
1489                 {
1490                         VectorClear(color4f);
1491                         color4f[3] = 1;
1492                 }
1493         }
1494 }
1495
1496 static void R_Shadow_VertexNoShadingWithXYZAttenuation(int numverts, const float *vertex3f, const float *lightcolor)
1497 {
1498         float *color4f = varray_color4f;
1499         float dist, intensity, v[3];
1500         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1501         {
1502                 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1503                 if ((dist = DotProduct(v, v)) < 1)
1504                 {
1505                         dist = sqrt(dist);
1506                         intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1507                         VectorScale(lightcolor, intensity, color4f);
1508                         color4f[3] = 1;
1509                 }
1510                 else
1511                 {
1512                         VectorClear(color4f);
1513                         color4f[3] = 1;
1514                 }
1515         }
1516 }
1517
1518 static void R_Shadow_VertexNoShadingWithZAttenuation(int numverts, const float *vertex3f, const float *lightcolor)
1519 {
1520         float *color4f = varray_color4f;
1521         float dist, intensity, v[3];
1522         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1523         {
1524                 Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1525                 if ((dist = fabs(v[2])) < 1)
1526                 {
1527                         intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1528                         VectorScale(lightcolor, intensity, color4f);
1529                         color4f[3] = 1;
1530                 }
1531                 else
1532                 {
1533                         VectorClear(color4f);
1534                         color4f[3] = 1;
1535                 }
1536         }
1537 }
1538
1539 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1540 #define USETEXMATRIX
1541
1542 #ifndef USETEXMATRIX
1543 // this should be done in a texture matrix or vertex program when possible, but here's code to do it manually
1544 // if hardware texcoord manipulation is not available (or not suitable, this would really benefit from 3DNow! or SSE
1545 static void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1546 {
1547         do
1548         {
1549                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1550                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1551                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1552                 vertex3f += 3;
1553                 tc3f += 3;
1554         }
1555         while (--numverts);
1556 }
1557
1558 static void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1559 {
1560         do
1561         {
1562                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1563                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1564                 vertex3f += 3;
1565                 tc2f += 2;
1566         }
1567         while (--numverts);
1568 }
1569 #endif
1570
1571 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin)
1572 {
1573         int i;
1574         float lightdir[3];
1575         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1576         {
1577                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1578                 // the cubemap normalizes this for us
1579                 out3f[0] = DotProduct(svector3f, lightdir);
1580                 out3f[1] = DotProduct(tvector3f, lightdir);
1581                 out3f[2] = DotProduct(normal3f, lightdir);
1582         }
1583 }
1584
1585 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(float *out3f, int numverts, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const vec3_t relativelightorigin, const vec3_t relativeeyeorigin)
1586 {
1587         int i;
1588         float lightdir[3], eyedir[3], halfdir[3];
1589         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1590         {
1591                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1592                 VectorNormalize(lightdir);
1593                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1594                 VectorNormalize(eyedir);
1595                 VectorAdd(lightdir, eyedir, halfdir);
1596                 // the cubemap normalizes this for us
1597                 out3f[0] = DotProduct(svector3f, halfdir);
1598                 out3f[1] = DotProduct(tvector3f, halfdir);
1599                 out3f[2] = DotProduct(normal3f, halfdir);
1600         }
1601 }
1602
1603 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *lightcolorbase, const float *lightcolorpants, const float *lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *bumptexture, rtexture_t *glosstexture)
1604 {
1605         int renders;
1606         float color[3], color2[3], colorscale, specularscale;
1607         rmeshstate_t m;
1608         // FIXME: support MATERIALFLAG_NODEPTHTEST
1609         if (!basetexture)
1610                 basetexture = r_texture_white;
1611         if (!bumptexture)
1612                 bumptexture = r_texture_blanknormalmap;
1613         if (!pantstexture)
1614                 lightcolorpants = vec3_origin;
1615         if (!shirttexture)
1616                 lightcolorshirt = vec3_origin;
1617         if (glosstexture && r_shadow_gloss.integer >= 1 && r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
1618                 specularscale = r_shadow_rtlight->specularscale * r_shadow_glossintensity.value;
1619         else if (!glosstexture && r_shadow_gloss.integer >= 2 && r_shadow_gloss2intensity.value > 0 && r_shadow_glossintensity.value > 0 && r_shadow_rtlight->specularscale > 0)
1620         {
1621                 glosstexture = r_texture_white;
1622                 specularscale = r_shadow_rtlight->specularscale * r_shadow_gloss2intensity.value;
1623         }
1624         else
1625         {
1626                 glosstexture = r_texture_black;
1627                 specularscale = 0;
1628         }
1629         if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * (VectorLength2(lightcolorbase) + VectorLength2(lightcolorpants) + VectorLength2(lightcolorshirt)) + specularscale * VectorLength2(lightcolorbase) <= 0.001)
1630                 return;
1631         if (r_shadowstage == R_SHADOWSTAGE_VISIBLELIGHTING)
1632         {
1633                 int passes = 0;
1634                 if (r_shadow_glsl.integer && r_shadow_program_light[0])
1635                         passes++; // GLSL shader path (GFFX5200, Radeon 9500)
1636                 else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1637                 {
1638                         // TODO: add direct pants/shirt rendering
1639                         if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
1640                                 R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
1641                         if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
1642                                 R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
1643                         if (r_shadow_rtlight->ambientscale)
1644                         {
1645                                 colorscale = r_shadow_rtlight->ambientscale;
1646                                 if (r_shadow_texture3d.integer && r_shadow_lightcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1647                                 {
1648                                 }
1649                                 else if (r_shadow_texture3d.integer && r_shadow_lightcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1650                                 {
1651                                 }
1652                                 else if (r_textureunits.integer >= 4 && r_shadow_lightcubemap != r_texture_whitecube)
1653                                 {
1654                                 }
1655                                 else if (r_textureunits.integer >= 3 && r_shadow_lightcubemap == r_texture_whitecube)
1656                                 {
1657                                 }
1658                                 else
1659                                         passes++;
1660                                 VectorScale(lightcolorbase, colorscale, color2);
1661                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1662                                         passes++;
1663                         }
1664                         if (r_shadow_rtlight->diffusescale)
1665                         {
1666                                 colorscale = r_shadow_rtlight->diffusescale;
1667                                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1668                                 {
1669                                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1670                                         passes++;
1671                                 }
1672                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube)
1673                                 {
1674                                         // 1/2/2 3D combine path (original Radeon)
1675                                         passes += 2;
1676                                 }
1677                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube)
1678                                 {
1679                                         // 2/2 3D combine path (original Radeon)
1680                                         passes++;
1681                                 }
1682                                 else if (r_textureunits.integer >= 4)
1683                                 {
1684                                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1685                                         passes++;
1686                                 }
1687                                 else
1688                                 {
1689                                         // 2/2/2 2D combine path (any dot3 card)
1690                                         passes += 2;
1691                                 }
1692                                 VectorScale(lightcolorbase, colorscale, color2);
1693                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1694                                         passes++;
1695                         }
1696                         if (specularscale && glosstexture != r_texture_black)
1697                         {
1698                                 //if (gl_support_blendsquare)
1699                                 {
1700                                         colorscale = specularscale;
1701                                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1702                                                 passes += 4;
1703                                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1704                                                 passes += 3;
1705                                         else
1706                                                 passes += 4;
1707                                         VectorScale(lightcolorbase, colorscale, color2);
1708                                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1709                                                 passes++;
1710                                 }
1711                         }
1712                 }
1713                 else
1714                 {
1715                         // TODO: add direct pants/shirt rendering
1716                         if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
1717                                 R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
1718                         if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
1719                                 R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
1720                         if (r_shadow_rtlight->ambientscale)
1721                         {
1722                                 VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale, color2);
1723                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1724                                         passes++;
1725                         }
1726                         if (r_shadow_rtlight->diffusescale)
1727                         {
1728                                 VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale, color2);
1729                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1730                                         passes++;
1731                         }
1732                 }
1733                 if (passes)
1734                 {
1735                         GL_Color(0.1*passes, 0.025*passes, 0, 1);
1736                         memset(&m, 0, sizeof(m));
1737                         m.pointer_vertex = vertex3f;
1738                         R_Mesh_State(&m);
1739                         GL_LockArrays(firstvertex, numvertices);
1740                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1741                         GL_LockArrays(0, 0);
1742                 }
1743                 return;
1744         }
1745         else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
1746         {
1747                 // GLSL shader path (GFFX5200, Radeon 9500)
1748                 R_Mesh_VertexPointer(vertex3f);
1749                 R_Mesh_TexCoordPointer(0, 2, texcoord2f);
1750                 R_Mesh_TexCoordPointer(1, 3, svector3f);
1751                 R_Mesh_TexCoordPointer(2, 3, tvector3f);
1752                 R_Mesh_TexCoordPointer(3, 3, normal3f);
1753                 R_Mesh_TexBind(0, R_GetTexture(bumptexture));
1754                 R_Mesh_TexBind(1, R_GetTexture(basetexture));
1755                 R_Mesh_TexBind(2, R_GetTexture(glosstexture));
1756                 if (r_shadow_lightpermutation & SHADERPERMUTATION_SPECULAR)
1757                 {
1758                         qglUniform1fARB(qglGetUniformLocationARB(r_shadow_lightprog, "SpecularScale"), specularscale);CHECKGLERROR
1759                 }
1760                 qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorbase[0], lightcolorbase[1], lightcolorbase[2]);CHECKGLERROR
1761                 GL_LockArrays(firstvertex, numvertices);
1762                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1763                 c_rt_lightmeshes++;
1764                 c_rt_lighttris += numtriangles;
1765                 // TODO: add direct pants/shirt rendering
1766                 if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
1767                 {
1768                         R_Mesh_TexBind(1, R_GetTexture(pantstexture));
1769                         qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorpants[0], lightcolorpants[1], lightcolorpants[2]);CHECKGLERROR
1770                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1771                         c_rt_lightmeshes++;
1772                         c_rt_lighttris += numtriangles;
1773                 }
1774                 if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
1775                 {
1776                         R_Mesh_TexBind(1, R_GetTexture(shirttexture));
1777                         qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightColor"), lightcolorshirt[0], lightcolorshirt[1], lightcolorshirt[2]);CHECKGLERROR
1778                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1779                         c_rt_lightmeshes++;
1780                         c_rt_lighttris += numtriangles;
1781                 }
1782                 GL_LockArrays(0, 0);
1783         }
1784         else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_DOT3)
1785         {
1786                 // TODO: add direct pants/shirt rendering
1787                 if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
1788                         R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
1789                 if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
1790                         R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
1791                 if (r_shadow_rtlight->ambientscale)
1792                 {
1793                         GL_Color(1,1,1,1);
1794                         colorscale = r_shadow_rtlight->ambientscale;
1795                         // colorscale accounts for how much we multiply the brightness
1796                         // during combine.
1797                         //
1798                         // mult is how many times the final pass of the lighting will be
1799                         // performed to get more brightness than otherwise possible.
1800                         //
1801                         // Limit mult to 64 for sanity sake.
1802                         if (r_shadow_texture3d.integer && r_shadow_lightcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1803                         {
1804                                 // 3 3D combine path (Geforce3, Radeon 8500)
1805                                 memset(&m, 0, sizeof(m));
1806                                 m.pointer_vertex = vertex3f;
1807                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1808 #ifdef USETEXMATRIX
1809                                 m.pointer_texcoord3f[0] = vertex3f;
1810                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1811 #else
1812                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1813                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1814 #endif
1815                                 m.tex[1] = R_GetTexture(basetexture);
1816                                 m.pointer_texcoord[1] = texcoord2f;
1817                                 m.texcubemap[2] = R_GetTexture(r_shadow_lightcubemap);
1818 #ifdef USETEXMATRIX
1819                                 m.pointer_texcoord3f[2] = vertex3f;
1820                                 m.texmatrix[2] = r_shadow_entitytolight;
1821 #else
1822                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1823                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
1824 #endif
1825                                 GL_BlendFunc(GL_ONE, GL_ONE);
1826                         }
1827                         else if (r_shadow_texture3d.integer && r_shadow_lightcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1828                         {
1829                                 // 2 3D combine path (Geforce3, original Radeon)
1830                                 memset(&m, 0, sizeof(m));
1831                                 m.pointer_vertex = vertex3f;
1832                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1833 #ifdef USETEXMATRIX
1834                                 m.pointer_texcoord3f[0] = vertex3f;
1835                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1836 #else
1837                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1838                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1839 #endif
1840                                 m.tex[1] = R_GetTexture(basetexture);
1841                                 m.pointer_texcoord[1] = texcoord2f;
1842                                 GL_BlendFunc(GL_ONE, GL_ONE);
1843                         }
1844                         else if (r_textureunits.integer >= 4 && r_shadow_lightcubemap != r_texture_whitecube)
1845                         {
1846                                 // 4 2D combine path (Geforce3, Radeon 8500)
1847                                 memset(&m, 0, sizeof(m));
1848                                 m.pointer_vertex = vertex3f;
1849                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1850 #ifdef USETEXMATRIX
1851                                 m.pointer_texcoord3f[0] = vertex3f;
1852                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1853 #else
1854                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1855                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1856 #endif
1857                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1858 #ifdef USETEXMATRIX
1859                                 m.pointer_texcoord3f[1] = vertex3f;
1860                                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1861 #else
1862                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1863                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
1864 #endif
1865                                 m.tex[2] = R_GetTexture(basetexture);
1866                                 m.pointer_texcoord[2] = texcoord2f;
1867                                 if (r_shadow_lightcubemap != r_texture_whitecube)
1868                                 {
1869                                         m.texcubemap[3] = R_GetTexture(r_shadow_lightcubemap);
1870 #ifdef USETEXMATRIX
1871                                         m.pointer_texcoord3f[3] = vertex3f;
1872                                         m.texmatrix[3] = r_shadow_entitytolight;
1873 #else
1874                                         m.pointer_texcoord3f[3] = varray_texcoord3f[3];
1875                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
1876 #endif
1877                                 }
1878                                 GL_BlendFunc(GL_ONE, GL_ONE);
1879                         }
1880                         else if (r_textureunits.integer >= 3 && r_shadow_lightcubemap == r_texture_whitecube)
1881                         {
1882                                 // 3 2D combine path (Geforce3, original Radeon)
1883                                 memset(&m, 0, sizeof(m));
1884                                 m.pointer_vertex = vertex3f;
1885                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1886 #ifdef USETEXMATRIX
1887                                 m.pointer_texcoord3f[0] = vertex3f;
1888                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1889 #else
1890                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1891                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1892 #endif
1893                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1894 #ifdef USETEXMATRIX
1895                                 m.pointer_texcoord3f[1] = vertex3f;
1896                                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1897 #else
1898                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1899                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
1900 #endif
1901                                 m.tex[2] = R_GetTexture(basetexture);
1902                                 m.pointer_texcoord[2] = texcoord2f;
1903                                 GL_BlendFunc(GL_ONE, GL_ONE);
1904                         }
1905                         else
1906                         {
1907                                 // 2/2/2 2D combine path (any dot3 card)
1908                                 memset(&m, 0, sizeof(m));
1909                                 m.pointer_vertex = vertex3f;
1910                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1911 #ifdef USETEXMATRIX
1912                                 m.pointer_texcoord3f[0] = vertex3f;
1913                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1914 #else
1915                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1916                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1917 #endif
1918                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1919 #ifdef USETEXMATRIX
1920                                 m.pointer_texcoord3f[1] = vertex3f;
1921                                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1922 #else
1923                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1924                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
1925 #endif
1926                                 R_Mesh_State(&m);
1927                                 GL_ColorMask(0,0,0,1);
1928                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1929                                 GL_LockArrays(firstvertex, numvertices);
1930                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1931                                 GL_LockArrays(0, 0);
1932                                 c_rt_lightmeshes++;
1933                                 c_rt_lighttris += numtriangles;
1934
1935                                 memset(&m, 0, sizeof(m));
1936                                 m.pointer_vertex = vertex3f;
1937                                 m.tex[0] = R_GetTexture(basetexture);
1938                                 m.pointer_texcoord[0] = texcoord2f;
1939                                 if (r_shadow_lightcubemap != r_texture_whitecube)
1940                                 {
1941                                         m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
1942 #ifdef USETEXMATRIX
1943                                         m.pointer_texcoord3f[1] = vertex3f;
1944                                         m.texmatrix[1] = r_shadow_entitytolight;
1945 #else
1946                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1947                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
1948 #endif
1949                                 }
1950                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1951                         }
1952                         // this final code is shared
1953                         R_Mesh_State(&m);
1954                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1955                         VectorScale(lightcolorbase, colorscale, color2);
1956                         GL_LockArrays(firstvertex, numvertices);
1957                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1958                         {
1959                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1960                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
1961                                 c_rt_lightmeshes++;
1962                                 c_rt_lighttris += numtriangles;
1963                         }
1964                         GL_LockArrays(0, 0);
1965                 }
1966                 if (r_shadow_rtlight->diffusescale)
1967                 {
1968                         GL_Color(1,1,1,1);
1969                         colorscale = r_shadow_rtlight->diffusescale;
1970                         // colorscale accounts for how much we multiply the brightness
1971                         // during combine.
1972                         //
1973                         // mult is how many times the final pass of the lighting will be
1974                         // performed to get more brightness than otherwise possible.
1975                         //
1976                         // Limit mult to 64 for sanity sake.
1977                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1978                         {
1979                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1980                                 memset(&m, 0, sizeof(m));
1981                                 m.pointer_vertex = vertex3f;
1982                                 m.tex[0] = R_GetTexture(bumptexture);
1983                                 m.texcombinergb[0] = GL_REPLACE;
1984                                 m.pointer_texcoord[0] = texcoord2f;
1985                                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1986                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1987                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1988                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
1989                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1990 #ifdef USETEXMATRIX
1991                                 m.pointer_texcoord3f[2] = vertex3f;
1992                                 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1993 #else
1994                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1995                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
1996 #endif
1997                                 R_Mesh_State(&m);
1998                                 GL_ColorMask(0,0,0,1);
1999                                 GL_BlendFunc(GL_ONE, GL_ZERO);
2000                                 GL_LockArrays(firstvertex, numvertices);
2001                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2002                                 GL_LockArrays(0, 0);
2003                                 c_rt_lightmeshes++;
2004                                 c_rt_lighttris += numtriangles;
2005
2006                                 memset(&m, 0, sizeof(m));
2007                                 m.pointer_vertex = vertex3f;
2008                                 m.tex[0] = R_GetTexture(basetexture);
2009                                 m.pointer_texcoord[0] = texcoord2f;
2010                                 if (r_shadow_lightcubemap != r_texture_whitecube)
2011                                 {
2012                                         m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2013 #ifdef USETEXMATRIX
2014                                         m.pointer_texcoord3f[1] = vertex3f;
2015                                         m.texmatrix[1] = r_shadow_entitytolight;
2016 #else
2017                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2018                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2019 #endif
2020                                 }
2021                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2022                         }
2023                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube)
2024                         {
2025                                 // 1/2/2 3D combine path (original Radeon)
2026                                 memset(&m, 0, sizeof(m));
2027                                 m.pointer_vertex = vertex3f;
2028                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
2029 #ifdef USETEXMATRIX
2030                                 m.pointer_texcoord3f[0] = vertex3f;
2031                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
2032 #else
2033                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
2034                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2035 #endif
2036                                 R_Mesh_State(&m);
2037                                 GL_ColorMask(0,0,0,1);
2038                                 GL_BlendFunc(GL_ONE, GL_ZERO);
2039                                 GL_LockArrays(firstvertex, numvertices);
2040                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2041                                 GL_LockArrays(0, 0);
2042                                 c_rt_lightmeshes++;
2043                                 c_rt_lighttris += numtriangles;
2044
2045                                 memset(&m, 0, sizeof(m));
2046                                 m.pointer_vertex = vertex3f;
2047                                 m.tex[0] = R_GetTexture(bumptexture);
2048                                 m.texcombinergb[0] = GL_REPLACE;
2049                                 m.pointer_texcoord[0] = texcoord2f;
2050                                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2051                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2052                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2053                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
2054                                 R_Mesh_State(&m);
2055                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2056                                 GL_LockArrays(firstvertex, numvertices);
2057                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2058                                 GL_LockArrays(0, 0);
2059                                 c_rt_lightmeshes++;
2060                                 c_rt_lighttris += numtriangles;
2061
2062                                 memset(&m, 0, sizeof(m));
2063                                 m.pointer_vertex = vertex3f;
2064                                 m.tex[0] = R_GetTexture(basetexture);
2065                                 m.pointer_texcoord[0] = texcoord2f;
2066                                 if (r_shadow_lightcubemap != r_texture_whitecube)
2067                                 {
2068                                         m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2069 #ifdef USETEXMATRIX
2070                                         m.pointer_texcoord3f[1] = vertex3f;
2071                                         m.texmatrix[1] = r_shadow_entitytolight;
2072 #else
2073                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2074                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2075 #endif
2076                                 }
2077                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2078                         }
2079                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube)
2080                         {
2081                                 // 2/2 3D combine path (original Radeon)
2082                                 memset(&m, 0, sizeof(m));
2083                                 m.pointer_vertex = vertex3f;
2084                                 m.tex[0] = R_GetTexture(bumptexture);
2085                                 m.texcombinergb[0] = GL_REPLACE;
2086                                 m.pointer_texcoord[0] = texcoord2f;
2087                                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2088                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2089                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2090                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
2091                                 R_Mesh_State(&m);
2092                                 GL_ColorMask(0,0,0,1);
2093                                 GL_BlendFunc(GL_ONE, GL_ZERO);
2094                                 GL_LockArrays(firstvertex, numvertices);
2095                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2096                                 GL_LockArrays(0, 0);
2097                                 c_rt_lightmeshes++;
2098                                 c_rt_lighttris += numtriangles;
2099
2100                                 memset(&m, 0, sizeof(m));
2101                                 m.pointer_vertex = vertex3f;
2102                                 m.tex[0] = R_GetTexture(basetexture);
2103                                 m.pointer_texcoord[0] = texcoord2f;
2104                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2105 #ifdef USETEXMATRIX
2106                                 m.pointer_texcoord3f[1] = vertex3f;
2107                                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2108 #else
2109                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2110                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2111 #endif
2112                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2113                         }
2114                         else if (r_textureunits.integer >= 4)
2115                         {
2116                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
2117                                 memset(&m, 0, sizeof(m));
2118                                 m.pointer_vertex = vertex3f;
2119                                 m.tex[0] = R_GetTexture(bumptexture);
2120                                 m.texcombinergb[0] = GL_REPLACE;
2121                                 m.pointer_texcoord[0] = texcoord2f;
2122                                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2123                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2124                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2125                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
2126                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2127 #ifdef USETEXMATRIX
2128                                 m.pointer_texcoord3f[2] = vertex3f;
2129                                 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
2130 #else
2131                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
2132                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2133 #endif
2134                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
2135 #ifdef USETEXMATRIX
2136                                 m.pointer_texcoord3f[3] = vertex3f;
2137                                 m.texmatrix[3] = r_shadow_entitytoattenuationz;
2138 #else
2139                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
2140                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
2141 #endif
2142                                 R_Mesh_State(&m);
2143                                 GL_ColorMask(0,0,0,1);
2144                                 GL_BlendFunc(GL_ONE, GL_ZERO);
2145                                 GL_LockArrays(firstvertex, numvertices);
2146                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2147                                 GL_LockArrays(0, 0);
2148                                 c_rt_lightmeshes++;
2149                                 c_rt_lighttris += numtriangles;
2150
2151                                 memset(&m, 0, sizeof(m));
2152                                 m.pointer_vertex = vertex3f;
2153                                 m.tex[0] = R_GetTexture(basetexture);
2154                                 m.pointer_texcoord[0] = texcoord2f;
2155                                 if (r_shadow_lightcubemap != r_texture_whitecube)
2156                                 {
2157                                         m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2158 #ifdef USETEXMATRIX
2159                                         m.pointer_texcoord3f[1] = vertex3f;
2160                                         m.texmatrix[1] = r_shadow_entitytolight;
2161 #else
2162                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2163                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2164 #endif
2165                                 }
2166                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2167                         }
2168                         else
2169                         {
2170                                 // 2/2/2 2D combine path (any dot3 card)
2171                                 memset(&m, 0, sizeof(m));
2172                                 m.pointer_vertex = vertex3f;
2173                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2174 #ifdef USETEXMATRIX
2175                                 m.pointer_texcoord3f[0] = vertex3f;
2176                                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
2177 #else
2178                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
2179                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2180 #endif
2181                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2182 #ifdef USETEXMATRIX
2183                                 m.pointer_texcoord3f[1] = vertex3f;
2184                                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
2185 #else
2186                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
2187                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
2188 #endif
2189                                 R_Mesh_State(&m);
2190                                 GL_ColorMask(0,0,0,1);
2191                                 GL_BlendFunc(GL_ONE, GL_ZERO);
2192                                 GL_LockArrays(firstvertex, numvertices);
2193                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2194                                 GL_LockArrays(0, 0);
2195                                 c_rt_lightmeshes++;
2196                                 c_rt_lighttris += numtriangles;
2197
2198                                 memset(&m, 0, sizeof(m));
2199                                 m.pointer_vertex = vertex3f;
2200                                 m.tex[0] = R_GetTexture(bumptexture);
2201                                 m.texcombinergb[0] = GL_REPLACE;
2202                                 m.pointer_texcoord[0] = texcoord2f;
2203                                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2204                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2205                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2206                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin);
2207                                 R_Mesh_State(&m);
2208                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2209                                 GL_LockArrays(firstvertex, numvertices);
2210                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2211                                 GL_LockArrays(0, 0);
2212                                 c_rt_lightmeshes++;
2213                                 c_rt_lighttris += numtriangles;
2214
2215                                 memset(&m, 0, sizeof(m));
2216                                 m.pointer_vertex = vertex3f;
2217                                 m.tex[0] = R_GetTexture(basetexture);
2218                                 m.pointer_texcoord[0] = texcoord2f;
2219                                 if (r_shadow_lightcubemap != r_texture_whitecube)
2220                                 {
2221                                         m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2222 #ifdef USETEXMATRIX
2223                                         m.pointer_texcoord3f[1] = vertex3f;
2224                                         m.texmatrix[1] = r_shadow_entitytolight;
2225 #else
2226                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2227                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2228 #endif
2229                                 }
2230                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2231                         }
2232                         // this final code is shared
2233                         R_Mesh_State(&m);
2234                         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
2235                         VectorScale(lightcolorbase, colorscale, color2);
2236                         GL_LockArrays(firstvertex, numvertices);
2237                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2238                         {
2239                                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
2240                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2241                                 c_rt_lightmeshes++;
2242                                 c_rt_lighttris += numtriangles;
2243                         }
2244                         GL_LockArrays(0, 0);
2245                 }
2246                 if (specularscale && glosstexture != r_texture_black)
2247                 {
2248                         // FIXME: detect blendsquare!
2249                         //if (gl_support_blendsquare)
2250                         {
2251                                 colorscale = specularscale;
2252                                 GL_Color(1,1,1,1);
2253                                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2254                                 {
2255                                         // 2/0/0/1/2 3D combine blendsquare path
2256                                         memset(&m, 0, sizeof(m));
2257                                         m.pointer_vertex = vertex3f;
2258                                         m.tex[0] = R_GetTexture(bumptexture);
2259                                         m.pointer_texcoord[0] = texcoord2f;
2260                                         m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2261                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2262                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2263                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
2264                                         R_Mesh_State(&m);
2265                                         GL_ColorMask(0,0,0,1);
2266                                         // this squares the result
2267                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2268                                         GL_LockArrays(firstvertex, numvertices);
2269                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2270                                         GL_LockArrays(0, 0);
2271                                         c_rt_lightmeshes++;
2272                                         c_rt_lighttris += numtriangles;
2273
2274                                         memset(&m, 0, sizeof(m));
2275                                         m.pointer_vertex = vertex3f;
2276                                         R_Mesh_State(&m);
2277                                         GL_LockArrays(firstvertex, numvertices);
2278                                         // square alpha in framebuffer a few times to make it shiny
2279                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2280                                         // these comments are a test run through this math for intensity 0.5
2281                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
2282                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
2283                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
2284                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2285                                         c_rt_lightmeshes++;
2286                                         c_rt_lighttris += numtriangles;
2287                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2288                                         c_rt_lightmeshes++;
2289                                         c_rt_lighttris += numtriangles;
2290                                         GL_LockArrays(0, 0);
2291
2292                                         memset(&m, 0, sizeof(m));
2293                                         m.pointer_vertex = vertex3f;
2294                                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
2295 #ifdef USETEXMATRIX
2296                                         m.pointer_texcoord3f[0] = vertex3f;
2297                                         m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
2298 #else
2299                                         m.pointer_texcoord3f[0] = varray_texcoord3f[0];
2300                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2301 #endif
2302                                         R_Mesh_State(&m);
2303                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2304                                         GL_LockArrays(firstvertex, numvertices);
2305                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2306                                         GL_LockArrays(0, 0);
2307                                         c_rt_lightmeshes++;
2308                                         c_rt_lighttris += numtriangles;
2309
2310                                         memset(&m, 0, sizeof(m));
2311                                         m.pointer_vertex = vertex3f;
2312                                         m.tex[0] = R_GetTexture(glosstexture);
2313                                         m.pointer_texcoord[0] = texcoord2f;
2314                                         if (r_shadow_lightcubemap != r_texture_whitecube)
2315                                         {
2316                                                 m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2317 #ifdef USETEXMATRIX
2318                                                 m.pointer_texcoord3f[1] = vertex3f;
2319                                                 m.texmatrix[1] = r_shadow_entitytolight;
2320 #else
2321                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2322                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2323 #endif
2324                                         }
2325                                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2326                                 }
2327                                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_lightcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2328                                 {
2329                                         // 2/0/0/2 3D combine blendsquare path
2330                                         memset(&m, 0, sizeof(m));
2331                                         m.pointer_vertex = vertex3f;
2332                                         m.tex[0] = R_GetTexture(bumptexture);
2333                                         m.pointer_texcoord[0] = texcoord2f;
2334                                         m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2335                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2336                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2337                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
2338                                         R_Mesh_State(&m);
2339                                         GL_ColorMask(0,0,0,1);
2340                                         // this squares the result
2341                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2342                                         GL_LockArrays(firstvertex, numvertices);
2343                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2344                                         GL_LockArrays(0, 0);
2345                                         c_rt_lightmeshes++;
2346                                         c_rt_lighttris += numtriangles;
2347
2348                                         memset(&m, 0, sizeof(m));
2349                                         m.pointer_vertex = vertex3f;
2350                                         R_Mesh_State(&m);
2351                                         GL_LockArrays(firstvertex, numvertices);
2352                                         // square alpha in framebuffer a few times to make it shiny
2353                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2354                                         // these comments are a test run through this math for intensity 0.5
2355                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
2356                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
2357                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
2358                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2359                                         c_rt_lightmeshes++;
2360                                         c_rt_lighttris += numtriangles;
2361                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2362                                         c_rt_lightmeshes++;
2363                                         c_rt_lighttris += numtriangles;
2364                                         GL_LockArrays(0, 0);
2365
2366                                         memset(&m, 0, sizeof(m));
2367                                         m.pointer_vertex = vertex3f;
2368                                         m.tex[0] = R_GetTexture(glosstexture);
2369                                         m.pointer_texcoord[0] = texcoord2f;
2370                                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2371 #ifdef USETEXMATRIX
2372                                         m.pointer_texcoord3f[1] = vertex3f;
2373                                         m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2374 #else
2375                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2376                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2377 #endif
2378                                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2379                                 }
2380                                 else
2381                                 {
2382                                         // 2/0/0/2/2 2D combine blendsquare path
2383                                         memset(&m, 0, sizeof(m));
2384                                         m.pointer_vertex = vertex3f;
2385                                         m.tex[0] = R_GetTexture(bumptexture);
2386                                         m.pointer_texcoord[0] = texcoord2f;
2387                                         m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2388                                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2389                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2390                                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, svector3f + 3 * firstvertex, tvector3f + 3 * firstvertex, normal3f + 3 * firstvertex, r_shadow_entitylightorigin, r_shadow_entityeyeorigin);
2391                                         R_Mesh_State(&m);
2392                                         GL_ColorMask(0,0,0,1);
2393                                         // this squares the result
2394                                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2395                                         GL_LockArrays(firstvertex, numvertices);
2396                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2397                                         GL_LockArrays(0, 0);
2398                                         c_rt_lightmeshes++;
2399                                         c_rt_lighttris += numtriangles;
2400
2401                                         memset(&m, 0, sizeof(m));
2402                                         m.pointer_vertex = vertex3f;
2403                                         R_Mesh_State(&m);
2404                                         GL_LockArrays(firstvertex, numvertices);
2405                                         // square alpha in framebuffer a few times to make it shiny
2406                                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2407                                         // these comments are a test run through this math for intensity 0.5
2408                                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
2409                                         // 0.25 * 0.25 = 0.0625 (this is another pass)
2410                                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
2411                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2412                                         c_rt_lightmeshes++;
2413                                         c_rt_lighttris += numtriangles;
2414                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2415                                         c_rt_lightmeshes++;
2416                                         c_rt_lighttris += numtriangles;
2417                                         GL_LockArrays(0, 0);
2418
2419                                         memset(&m, 0, sizeof(m));
2420                                         m.pointer_vertex = vertex3f;
2421                                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2422 #ifdef USETEXMATRIX
2423                                         m.pointer_texcoord3f[0] = vertex3f;
2424                                         m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
2425 #else
2426                                         m.pointer_texcoord[0] = varray_texcoord2f[0];
2427                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2428 #endif
2429                                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2430 #ifdef USETEXMATRIX
2431                                         m.pointer_texcoord3f[1] = vertex3f;
2432                                         m.texmatrix[1] = r_shadow_entitytoattenuationz;
2433 #else
2434                                         m.pointer_texcoord[1] = varray_texcoord2f[1];
2435                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
2436 #endif
2437                                         R_Mesh_State(&m);
2438                                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2439                                         GL_LockArrays(firstvertex, numvertices);
2440                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2441                                         GL_LockArrays(0, 0);
2442                                         c_rt_lightmeshes++;
2443                                         c_rt_lighttris += numtriangles;
2444
2445                                         memset(&m, 0, sizeof(m));
2446                                         m.pointer_vertex = vertex3f;
2447                                         m.tex[0] = R_GetTexture(glosstexture);
2448                                         m.pointer_texcoord[0] = texcoord2f;
2449                                         if (r_shadow_lightcubemap != r_texture_whitecube)
2450                                         {
2451                                                 m.texcubemap[1] = R_GetTexture(r_shadow_lightcubemap);
2452 #ifdef USETEXMATRIX
2453                                                 m.pointer_texcoord3f[1] = vertex3f;
2454                                                 m.texmatrix[1] = r_shadow_entitytolight;
2455 #else
2456                                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
2457                                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytolight);
2458 #endif
2459                                         }
2460                                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2461                                 }
2462                                 R_Mesh_State(&m);
2463                                 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
2464                                 VectorScale(lightcolorbase, colorscale, color2);
2465                                 GL_LockArrays(firstvertex, numvertices);
2466                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2467                                 {
2468                                         GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
2469                                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2470                                         c_rt_lightmeshes++;
2471                                         c_rt_lighttris += numtriangles;
2472                                 }
2473                                 GL_LockArrays(0, 0);
2474                         }
2475                 }
2476         }
2477         else if (r_shadowstage == R_SHADOWSTAGE_LIGHT_VERTEX)
2478         {
2479                 // TODO: add direct pants/shirt rendering
2480                 if (pantstexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorpants) > 0.001)
2481                         R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorpants, vec3_origin, vec3_origin, pantstexture, NULL, NULL, bumptexture, NULL);
2482                 if (shirttexture && (r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorshirt) > 0.001)
2483                         R_Shadow_RenderLighting(firstvertex, numvertices, numtriangles, elements, vertex3f, svector3f, tvector3f, normal3f, texcoord2f, lightcolorshirt, vec3_origin, vec3_origin, shirttexture, NULL, NULL, bumptexture, NULL);
2484                 if (r_shadow_rtlight->ambientscale)
2485                 {
2486                         GL_BlendFunc(GL_ONE, GL_ONE);
2487                         VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale, color2);
2488                         memset(&m, 0, sizeof(m));
2489                         m.pointer_vertex = vertex3f;
2490                         m.tex[0] = R_GetTexture(basetexture);
2491                         m.pointer_texcoord[0] = texcoord2f;
2492                         if (r_textureunits.integer >= 2)
2493                         {
2494                                 // voodoo2
2495                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2496 #ifdef USETEXMATRIX
2497                                 m.pointer_texcoord3f[1] = vertex3f;
2498                                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2499 #else
2500                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
2501                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2502 #endif
2503                                 if (r_textureunits.integer >= 3)
2504                                 {
2505                                         // Geforce3/Radeon class but not using dot3
2506                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2507 #ifdef USETEXMATRIX
2508                                         m.pointer_texcoord3f[2] = vertex3f;
2509                                         m.texmatrix[2] = r_shadow_entitytoattenuationz;
2510 #else
2511                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
2512                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
2513 #endif
2514                                 }
2515                         }
2516                         if (r_textureunits.integer >= 3)
2517                                 m.pointer_color = NULL;
2518                         else
2519                                 m.pointer_color = varray_color4f;
2520                         R_Mesh_State(&m);
2521                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2522                         {
2523                                 color[0] = bound(0, color2[0], 1);
2524                                 color[1] = bound(0, color2[1], 1);
2525                                 color[2] = bound(0, color2[2], 1);
2526                                 if (r_textureunits.integer >= 3)
2527                                         GL_Color(color[0], color[1], color[2], 1);
2528                                 else if (r_textureunits.integer >= 2)
2529                                         R_Shadow_VertexNoShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, color);
2530                                 else
2531                                         R_Shadow_VertexNoShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, color);
2532                                 GL_LockArrays(firstvertex, numvertices);
2533                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2534                                 GL_LockArrays(0, 0);
2535                                 c_rt_lightmeshes++;
2536                                 c_rt_lighttris += numtriangles;
2537                         }
2538                 }
2539                 if (r_shadow_rtlight->diffusescale)
2540                 {
2541                         GL_BlendFunc(GL_ONE, GL_ONE);
2542                         VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale, color2);
2543                         memset(&m, 0, sizeof(m));
2544                         m.pointer_vertex = vertex3f;
2545                         m.pointer_color = varray_color4f;
2546                         m.tex[0] = R_GetTexture(basetexture);
2547                         m.pointer_texcoord[0] = texcoord2f;
2548                         if (r_textureunits.integer >= 2)
2549                         {
2550                                 // voodoo2
2551                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2552 #ifdef USETEXMATRIX
2553                                 m.pointer_texcoord3f[1] = vertex3f;
2554                                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
2555 #else
2556                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
2557                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationxyz);
2558 #endif
2559                                 if (r_textureunits.integer >= 3)
2560                                 {
2561                                         // Geforce3/Radeon class but not using dot3
2562                                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2563 #ifdef USETEXMATRIX
2564                                         m.pointer_texcoord3f[2] = vertex3f;
2565                                         m.texmatrix[2] = r_shadow_entitytoattenuationz;
2566 #else
2567                                         m.pointer_texcoord[2] = varray_texcoord2f[2];
2568                                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2] + 3 * firstvertex, numvertices, vertex3f + 3 * firstvertex, &r_shadow_entitytoattenuationz);
2569 #endif
2570                                 }
2571                         }
2572                         R_Mesh_State(&m);
2573                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
2574                         {
2575                                 color[0] = bound(0, color2[0], 1);
2576                                 color[1] = bound(0, color2[1], 1);
2577                                 color[2] = bound(0, color2[2], 1);
2578                                 if (r_textureunits.integer >= 3)
2579                                         R_Shadow_VertexShading(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
2580                                 else if (r_textureunits.integer >= 2)
2581                                         R_Shadow_VertexShadingWithZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
2582                                 else
2583                                         R_Shadow_VertexShadingWithXYZAttenuation(numvertices, vertex3f + 3 * firstvertex, normal3f + 3 * firstvertex, color);
2584                                 GL_LockArrays(firstvertex, numvertices);
2585                                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, elements);
2586                                 GL_LockArrays(0, 0);
2587                                 c_rt_lightmeshes++;
2588                                 c_rt_lighttris += numtriangles;
2589                         }
2590                 }
2591         }
2592 }
2593
2594 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
2595 {
2596         int j, k;
2597         float scale;
2598         R_RTLight_Uncompile(rtlight);
2599         memset(rtlight, 0, sizeof(*rtlight));
2600
2601         VectorCopy(light->origin, rtlight->shadoworigin);
2602         VectorCopy(light->color, rtlight->color);
2603         rtlight->radius = light->radius;
2604         //rtlight->cullradius = rtlight->radius;
2605         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2606         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2607         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2608         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2609         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2610         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2611         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2612         rtlight->cubemapname[0] = 0;
2613         if (light->cubemapname[0])
2614                 strcpy(rtlight->cubemapname, light->cubemapname);
2615         else if (light->cubemapnum > 0)
2616                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2617         rtlight->shadow = light->shadow;
2618         rtlight->corona = light->corona;
2619         rtlight->style = light->style;
2620         rtlight->isstatic = isstatic;
2621         rtlight->coronasizescale = light->coronasizescale;
2622         rtlight->ambientscale = light->ambientscale;
2623         rtlight->diffusescale = light->diffusescale;
2624         rtlight->specularscale = light->specularscale;
2625         rtlight->flags = light->flags;
2626         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2627         // ConcatScale won't work here because this needs to scale rotate and
2628         // translate, not just rotate
2629         scale = 1.0f / rtlight->radius;
2630         for (k = 0;k < 3;k++)
2631                 for (j = 0;j < 4;j++)
2632                         rtlight->matrix_worldtolight.m[k][j] *= scale;
2633
2634         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
2635         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
2636         VectorScale(rtlight->color, rtlight->radius * (rtlight->style >= 0 ? d_lightstylevalue[rtlight->style] : 128) * 0.125f, rtlight->lightmap_light);
2637         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
2638 }
2639
2640 // compiles rtlight geometry
2641 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2642 void R_RTLight_Compile(rtlight_t *rtlight)
2643 {
2644         int shadowmeshes, shadowtris, lightmeshes, lighttris, numleafs, numleafpvsbytes, numsurfaces;
2645         entity_render_t *ent = r_refdef.worldentity;
2646         model_t *model = r_refdef.worldmodel;
2647         qbyte *data;
2648
2649         // compile the light
2650         rtlight->compiled = true;
2651         rtlight->static_numleafs = 0;
2652         rtlight->static_numleafpvsbytes = 0;
2653         rtlight->static_leaflist = NULL;
2654         rtlight->static_leafpvs = NULL;
2655         rtlight->static_numsurfaces = 0;
2656         rtlight->static_surfacelist = NULL;
2657         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2658         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2659         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2660         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2661         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2662         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2663
2664         if (model && model->GetLightInfo)
2665         {
2666                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
2667                 r_shadow_compilingrtlight = rtlight;
2668                 R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
2669                 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);
2670                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2671                 data = Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
2672                 rtlight->static_numleafs = numleafs;
2673                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2674                 rtlight->static_leaflist = (void *)data;data += sizeof(int) * numleafs;
2675                 rtlight->static_leafpvs = (void *)data;data += numleafpvsbytes;
2676                 rtlight->static_numsurfaces = numsurfaces;
2677                 rtlight->static_surfacelist = (void *)data;data += sizeof(int) * numsurfaces;
2678                 if (numleafs)
2679                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2680                 if (numleafpvsbytes)
2681                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2682                 if (numsurfaces)
2683                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2684                 if (model->DrawShadowVolume && rtlight->shadow)
2685                 {
2686                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
2687                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist, rtlight->cullmins, rtlight->cullmaxs);
2688                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
2689                 }
2690                 if (model->DrawLight)
2691                 {
2692                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
2693                         model->DrawLight(ent, vec3_origin, numsurfaces, r_shadow_buffer_surfacelist);
2694                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
2695                 }
2696                 // switch back to rendering when DrawShadowVolume or DrawLight is called
2697                 r_shadow_compilingrtlight = NULL;
2698         }
2699
2700
2701         // use smallest available cullradius - box radius or light radius
2702         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2703         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2704
2705         shadowmeshes = 0;
2706         shadowtris = 0;
2707         if (rtlight->static_meshchain_shadow)
2708         {
2709                 shadowmesh_t *mesh;
2710                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2711                 {
2712                         shadowmeshes++;
2713                         shadowtris += mesh->numtriangles;
2714                 }
2715         }
2716
2717         lightmeshes = 0;
2718         lighttris = 0;
2719         if (rtlight->static_meshchain_light)
2720         {
2721                 shadowmesh_t *mesh;
2722                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2723                 {
2724                         lightmeshes++;
2725                         lighttris += mesh->numtriangles;
2726                 }
2727         }
2728
2729         Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], shadowtris, shadowmeshes, lighttris, lightmeshes);
2730 }
2731
2732 void R_RTLight_Uncompile(rtlight_t *rtlight)
2733 {
2734         if (rtlight->compiled)
2735         {
2736                 if (rtlight->static_meshchain_shadow)
2737                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2738                 rtlight->static_meshchain_shadow = NULL;
2739                 if (rtlight->static_meshchain_light)
2740                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2741                 rtlight->static_meshchain_light = NULL;
2742                 // these allocations are grouped
2743                 if (rtlight->static_leaflist)
2744                         Mem_Free(rtlight->static_leaflist);
2745                 rtlight->static_numleafs = 0;
2746                 rtlight->static_numleafpvsbytes = 0;
2747                 rtlight->static_leaflist = NULL;
2748                 rtlight->static_leafpvs = NULL;
2749                 rtlight->static_numsurfaces = 0;
2750                 rtlight->static_surfacelist = NULL;
2751                 rtlight->compiled = false;
2752         }
2753 }
2754
2755 void R_Shadow_UncompileWorldLights(void)
2756 {
2757         dlight_t *light;
2758         for (light = r_shadow_worldlightchain;light;light = light->next)
2759                 R_RTLight_Uncompile(&light->rtlight);
2760 }
2761
2762 void R_Shadow_DrawEntityShadow(entity_render_t *ent, rtlight_t *rtlight, int numsurfaces, int *surfacelist)
2763 {
2764         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2765         vec_t relativeshadowradius;
2766         if (ent == r_refdef.worldentity)
2767         {
2768                 if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2769                 {
2770                         shadowmesh_t *mesh;
2771                         R_Mesh_Matrix(&ent->matrix);
2772                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2773                         {
2774                                 R_Mesh_VertexPointer(mesh->vertex3f);
2775                                 GL_LockArrays(0, mesh->numverts);
2776                                 if (r_shadowstage == R_SHADOWSTAGE_STENCIL)
2777                                 {
2778                                         // decrement stencil if backface is behind depthbuffer
2779                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2780                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2781                                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2782                                         c_rtcached_shadowmeshes++;
2783                                         c_rtcached_shadowtris += mesh->numtriangles;
2784                                         // increment stencil if frontface is behind depthbuffer
2785                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2786                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2787                                 }
2788                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2789                                 c_rtcached_shadowmeshes++;
2790                                 c_rtcached_shadowtris += mesh->numtriangles;
2791                                 GL_LockArrays(0, 0);
2792                         }
2793                 }
2794                 else if (numsurfaces)
2795                 {
2796                         R_Mesh_Matrix(&ent->matrix);
2797                         ent->model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, surfacelist, rtlight->cullmins, rtlight->cullmaxs);
2798                 }
2799         }
2800         else
2801         {
2802                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativeshadoworigin);
2803                 relativeshadowradius = rtlight->radius / ent->scale;
2804                 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2805                 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2806                 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2807                 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2808                 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2809                 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2810                 R_Mesh_Matrix(&ent->matrix);
2811                 ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2812         }
2813 }
2814
2815 void R_Shadow_DrawEntityLight(entity_render_t *ent, rtlight_t *rtlight, vec3_t lightcolor, int numsurfaces, int *surfacelist)
2816 {
2817         shadowmesh_t *mesh;
2818         // set up properties for rendering light onto this entity
2819         r_shadow_entitylightcolor[0] = lightcolor[0] * ent->colormod[0] * ent->alpha;
2820         r_shadow_entitylightcolor[1] = lightcolor[1] * ent->colormod[1] * ent->alpha;
2821         r_shadow_entitylightcolor[2] = lightcolor[2] * ent->colormod[2] * ent->alpha;
2822         Matrix4x4_Concat(&r_shadow_entitytolight, &rtlight->matrix_worldtolight, &ent->matrix);
2823         Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2824         Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2825         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, r_shadow_entitylightorigin);
2826         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
2827         R_Mesh_Matrix(&ent->matrix);
2828         if (r_shadowstage == R_SHADOWSTAGE_LIGHT_GLSL)
2829         {
2830                 R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_lightcubemap));
2831                 R_Mesh_TexMatrix(3, &r_shadow_entitytolight);
2832                 qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "LightPosition"), r_shadow_entitylightorigin[0], r_shadow_entitylightorigin[1], r_shadow_entitylightorigin[2]);CHECKGLERROR
2833                 if (r_shadow_lightpermutation & (SHADERPERMUTATION_SPECULAR | SHADERPERMUTATION_FOG | SHADERPERMUTATION_OFFSETMAPPING))
2834                 {
2835                         qglUniform3fARB(qglGetUniformLocationARB(r_shadow_lightprog, "EyePosition"), r_shadow_entityeyeorigin[0], r_shadow_entityeyeorigin[1], r_shadow_entityeyeorigin[2]);CHECKGLERROR
2836                 }
2837         }
2838         if (ent == r_refdef.worldentity)
2839         {
2840                 if (rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compilelight.integer)
2841                 {
2842                         for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2843                                 R_Shadow_RenderLighting(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, r_shadow_entitylightcolor, vec3_origin, vec3_origin, mesh->map_diffuse, NULL, NULL, mesh->map_normal, mesh->map_specular);
2844                 }
2845                 else
2846                         ent->model->DrawLight(ent, r_shadow_entitylightcolor, numsurfaces, surfacelist);
2847         }
2848         else
2849                 ent->model->DrawLight(ent, r_shadow_entitylightcolor, ent->model->nummodelsurfaces, ent->model->surfacelist);
2850 }
2851
2852 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2853 {
2854         int i, usestencil;
2855         float f;
2856         vec3_t lightcolor;
2857         int numleafs, numsurfaces;
2858         int *leaflist, *surfacelist;
2859         qbyte *leafpvs;
2860         int numlightentities;
2861         int numshadowentities;
2862         entity_render_t *lightentities[MAX_EDICTS];
2863         entity_render_t *shadowentities[MAX_EDICTS];
2864
2865         // skip lights that don't light (corona only lights)
2866         if (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale < 0.01)
2867                 return;
2868
2869         f = (rtlight->style >= 0 ? d_lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2870         VectorScale(rtlight->color, f, lightcolor);
2871         if (VectorLength2(lightcolor) < 0.01)
2872                 return;
2873         /*
2874         if (rtlight->selected)
2875         {
2876                 f = 2 + sin(realtime * M_PI * 4.0);
2877                 VectorScale(lightcolor, f, lightcolor);
2878         }
2879         */
2880
2881         // loading is done before visibility checks because loading should happen
2882         // all at once at the start of a level, not when it stalls gameplay.
2883         // (especially important to benchmarks)
2884         // compile light
2885         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2886                 R_RTLight_Compile(rtlight);
2887         // load cubemap
2888         r_shadow_lightcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2889
2890         // if the light box is offscreen, skip it
2891         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2892                 return;
2893
2894         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2895         {
2896                 // compiled light, world available and can receive realtime lighting
2897                 // retrieve leaf information
2898                 numleafs = rtlight->static_numleafs;
2899                 leaflist = rtlight->static_leaflist;
2900                 leafpvs = rtlight->static_leafpvs;
2901                 numsurfaces = rtlight->static_numsurfaces;
2902                 surfacelist = rtlight->static_surfacelist;
2903         }
2904         else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2905         {
2906                 // dynamic light, world available and can receive realtime lighting
2907                 // calculate lit surfaces and leafs
2908                 R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
2909                 r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, 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);
2910                 leaflist = r_shadow_buffer_leaflist;
2911                 leafpvs = r_shadow_buffer_leafpvs;
2912                 surfacelist = r_shadow_buffer_surfacelist;
2913                 // if the reduced leaf bounds are offscreen, skip it
2914                 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2915                         return;
2916         }
2917         else
2918         {
2919                 // no world
2920                 numleafs = 0;
2921                 leaflist = NULL;
2922                 leafpvs = NULL;
2923                 numsurfaces = 0;
2924                 surfacelist = NULL;
2925         }
2926         // check if light is illuminating any visible leafs
2927         if (numleafs)
2928         {
2929                 for (i = 0;i < numleafs;i++)
2930                         if (r_worldleafvisible[leaflist[i]])
2931                                 break;
2932                 if (i == numleafs)
2933                         return;
2934         }
2935         // set up a scissor rectangle for this light
2936         if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2937                 return;
2938
2939         numlightentities = 0;
2940         if (numsurfaces)
2941                 lightentities[numlightentities++] = r_refdef.worldentity;
2942         numshadowentities = 0;
2943         if (numsurfaces)
2944                 shadowentities[numshadowentities++] = r_refdef.worldentity;
2945         if (r_drawentities.integer)
2946         {
2947                 for (i = 0;i < r_refdef.numentities;i++)
2948                 {
2949                         entity_render_t *ent = r_refdef.entities[i];
2950                         if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2951                          && ent->model
2952                          && !(ent->flags & RENDER_TRANSPARENT)
2953                          && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
2954                         {
2955                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2956                                 if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
2957                                         shadowentities[numshadowentities++] = ent;
2958                                 if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
2959                                         lightentities[numlightentities++] = ent;
2960                         }
2961                 }
2962         }
2963
2964         // return if there's nothing at all to light
2965         if (!numlightentities)
2966                 return;
2967
2968         R_Shadow_Stage_ActiveLight(rtlight);
2969         c_rt_lights++;
2970
2971         usestencil = false;
2972         if (numshadowentities && (!visible || r_shadow_visiblelighting.integer == 1) && gl_stencil && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
2973         {
2974                 usestencil = true;
2975                 R_Shadow_Stage_StencilShadowVolumes();
2976                 for (i = 0;i < numshadowentities;i++)
2977                         R_Shadow_DrawEntityShadow(shadowentities[i], rtlight, numsurfaces, surfacelist);
2978         }
2979
2980         if (numlightentities && !visible)
2981         {
2982                 R_Shadow_Stage_Lighting(usestencil);
2983                 for (i = 0;i < numlightentities;i++)
2984                         R_Shadow_DrawEntityLight(lightentities[i], rtlight, lightcolor, numsurfaces, surfacelist);
2985         }
2986
2987         if (numshadowentities && visible && r_shadow_visiblevolumes.integer > 0 && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
2988         {
2989                 R_Shadow_Stage_VisibleShadowVolumes();
2990                 for (i = 0;i < numshadowentities;i++)
2991                         R_Shadow_DrawEntityShadow(shadowentities[i], rtlight, numsurfaces, surfacelist);
2992         }
2993
2994         if (numlightentities && visible && r_shadow_visiblelighting.integer > 0)
2995         {
2996                 R_Shadow_Stage_VisibleLighting(usestencil);
2997                 for (i = 0;i < numlightentities;i++)
2998                         R_Shadow_DrawEntityLight(lightentities[i], rtlight, lightcolor, numsurfaces, surfacelist);
2999         }
3000 }
3001
3002 void R_ShadowVolumeLighting(qboolean visible)
3003 {
3004         int lnum, flag;
3005         dlight_t *light;
3006
3007         if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
3008                 R_Shadow_EditLights_Reload_f();
3009
3010         R_Shadow_Stage_Begin();
3011
3012         flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3013         if (r_shadow_debuglight.integer >= 0)
3014         {
3015                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
3016                         if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
3017                                 R_DrawRTLight(&light->rtlight, visible);
3018         }
3019         else
3020                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
3021                         if (light->flags & flag)
3022                                 R_DrawRTLight(&light->rtlight, visible);
3023         if (r_rtdlight)
3024                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
3025                         R_DrawRTLight(&light->rtlight, visible);
3026
3027         R_Shadow_Stage_End();
3028 }
3029
3030 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
3031 typedef struct suffixinfo_s
3032 {
3033         char *suffix;
3034         qboolean flipx, flipy, flipdiagonal;
3035 }
3036 suffixinfo_t;
3037 static suffixinfo_t suffix[3][6] =
3038 {
3039         {
3040                 {"px",   false, false, false},
3041                 {"nx",   false, false, false},
3042                 {"py",   false, false, false},
3043                 {"ny",   false, false, false},
3044                 {"pz",   false, false, false},
3045                 {"nz",   false, false, false}
3046         },
3047         {
3048                 {"posx", false, false, false},
3049                 {"negx", false, false, false},
3050                 {"posy", false, false, false},
3051                 {"negy", false, false, false},
3052                 {"posz", false, false, false},
3053                 {"negz", false, false, false}
3054         },
3055         {
3056                 {"rt",    true, false,  true},
3057                 {"lf",   false,  true,  true},
3058                 {"ft",    true,  true, false},
3059                 {"bk",   false, false, false},
3060                 {"up",    true, false,  true},
3061                 {"dn",    true, false,  true}
3062         }
3063 };
3064
3065 static int componentorder[4] = {0, 1, 2, 3};
3066
3067 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
3068 {
3069         int i, j, cubemapsize;
3070         qbyte *cubemappixels, *image_rgba;
3071         rtexture_t *cubemaptexture;
3072         char name[256];
3073         // must start 0 so the first loadimagepixels has no requested width/height
3074         cubemapsize = 0;
3075         cubemappixels = NULL;
3076         cubemaptexture = NULL;
3077         // keep trying different suffix groups (posx, px, rt) until one loads
3078         for (j = 0;j < 3 && !cubemappixels;j++)
3079         {
3080                 // load the 6 images in the suffix group
3081                 for (i = 0;i < 6;i++)
3082                 {
3083                         // generate an image name based on the base and and suffix
3084                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3085                         // load it
3086                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
3087                         {
3088                                 // an image loaded, make sure width and height are equal
3089                                 if (image_width == image_height)
3090                                 {
3091                                         // if this is the first image to load successfully, allocate the cubemap memory
3092                                         if (!cubemappixels && image_width >= 1)
3093                                         {
3094                                                 cubemapsize = image_width;
3095                                                 // note this clears to black, so unavailable sides are black
3096                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3097                                         }
3098                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3099                                         if (cubemappixels)
3100                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_rgba, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
3101                                 }
3102                                 else
3103                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3104                                 // free the image
3105                                 Mem_Free(image_rgba);
3106                         }
3107                 }
3108         }
3109         // if a cubemap loaded, upload it
3110         if (cubemappixels)
3111         {
3112                 if (!r_shadow_filters_texturepool)
3113                         r_shadow_filters_texturepool = R_AllocTexturePool();
3114                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
3115                 Mem_Free(cubemappixels);
3116         }
3117         else
3118         {
3119                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3120                 for (j = 0;j < 3;j++)
3121                         for (i = 0;i < 6;i++)
3122                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3123                 Con_Print(" and was unable to find any of them.\n");
3124         }
3125         return cubemaptexture;
3126 }
3127
3128 rtexture_t *R_Shadow_Cubemap(const char *basename)
3129 {
3130         int i;
3131         for (i = 0;i < numcubemaps;i++)
3132                 if (!strcasecmp(cubemaps[i].basename, basename))
3133                         return cubemaps[i].texture;
3134         if (i >= MAX_CUBEMAPS)
3135                 return r_texture_whitecube;
3136         numcubemaps++;
3137         strcpy(cubemaps[i].basename, basename);
3138         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3139         return cubemaps[i].texture;
3140 }
3141
3142 void R_Shadow_FreeCubemaps(void)
3143 {
3144         numcubemaps = 0;
3145         R_FreeTexturePool(&r_shadow_filters_texturepool);
3146 }
3147
3148 dlight_t *R_Shadow_NewWorldLight(void)
3149 {
3150         dlight_t *light;
3151         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
3152         light->next = r_shadow_worldlightchain;
3153         r_shadow_worldlightchain = light;
3154         return light;
3155 }
3156
3157 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)
3158 {
3159         VectorCopy(origin, light->origin);
3160         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3161         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3162         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3163         light->color[0] = max(color[0], 0);
3164         light->color[1] = max(color[1], 0);
3165         light->color[2] = max(color[2], 0);
3166         light->radius = max(radius, 0);
3167         light->style = style;
3168         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
3169         {
3170                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3171                 light->style = 0;
3172         }
3173         light->shadow = shadowenable;
3174         light->corona = corona;
3175         if (!cubemapname)
3176                 cubemapname = "";
3177         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3178         light->coronasizescale = coronasizescale;
3179         light->ambientscale = ambientscale;
3180         light->diffusescale = diffusescale;
3181         light->specularscale = specularscale;
3182         light->flags = flags;
3183         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
3184
3185         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
3186 }
3187
3188 void R_Shadow_FreeWorldLight(dlight_t *light)
3189 {
3190         dlight_t **lightpointer;
3191         R_RTLight_Uncompile(&light->rtlight);
3192         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
3193         if (*lightpointer != light)
3194                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
3195         *lightpointer = light->next;
3196         Mem_Free(light);
3197 }
3198
3199 void R_Shadow_ClearWorldLights(void)
3200 {
3201         while (r_shadow_worldlightchain)
3202                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
3203         r_shadow_selectedlight = NULL;
3204         R_Shadow_FreeCubemaps();
3205 }
3206
3207 void R_Shadow_SelectLight(dlight_t *light)
3208 {
3209         if (r_shadow_selectedlight)
3210                 r_shadow_selectedlight->selected = false;
3211         r_shadow_selectedlight = light;
3212         if (r_shadow_selectedlight)
3213                 r_shadow_selectedlight->selected = true;
3214 }
3215
3216 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
3217 {
3218         float scale = r_editlights_cursorgrid.value * 0.5f;
3219         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
3220 }
3221
3222 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
3223 {
3224         float intensity;
3225         const dlight_t *light;
3226         light = calldata1;
3227         intensity = 0.5;
3228         if (light->selected)
3229                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
3230         if (!light->shadow)
3231                 intensity *= 0.5f;
3232         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
3233 }
3234
3235 void R_Shadow_DrawLightSprites(void)
3236 {
3237         int i;
3238         cachepic_t *pic;
3239         dlight_t *light;
3240
3241         for (i = 0;i < 5;i++)
3242         {
3243                 lighttextures[i] = NULL;
3244                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1), true)))
3245                         lighttextures[i] = pic->tex;
3246         }
3247
3248         for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
3249                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, i % 5);
3250         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
3251 }
3252
3253 void R_Shadow_SelectLightInView(void)
3254 {
3255         float bestrating, rating, temp[3];
3256         dlight_t *best, *light;
3257         best = NULL;
3258         bestrating = 0;
3259         for (light = r_shadow_worldlightchain;light;light = light->next)
3260         {
3261                 VectorSubtract(light->origin, r_vieworigin, temp);
3262                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
3263                 if (rating >= 0.95)
3264                 {
3265                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3266                         if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_vieworigin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
3267                         {
3268                                 bestrating = rating;
3269                                 best = light;
3270                         }
3271                 }
3272         }
3273         R_Shadow_SelectLight(best);
3274 }
3275
3276 void R_Shadow_LoadWorldLights(void)
3277 {
3278         int n, a, style, shadow, flags;
3279         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3280         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3281         if (r_refdef.worldmodel == NULL)
3282         {
3283                 Con_Print("No map loaded.\n");
3284                 return;
3285         }
3286         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3287         strlcat (name, ".rtlights", sizeof (name));
3288         lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
3289         if (lightsstring)
3290         {
3291                 s = lightsstring;
3292                 n = 0;
3293                 while (*s)
3294                 {
3295                         t = s;
3296                         /*
3297                         shadow = true;
3298                         for (;COM_Parse(t, true) && strcmp(
3299                         if (COM_Parse(t, true))
3300                         {
3301                                 if (com_token[0] == '!')
3302                                 {
3303                                         shadow = false;
3304                                         origin[0] = atof(com_token+1);
3305                                 }
3306                                 else
3307                                         origin[0] = atof(com_token);
3308                                 if (Com_Parse(t
3309                         }
3310                         */
3311                         t = s;
3312                         while (*s && *s != '\n' && *s != '\r')
3313                                 s++;
3314                         if (!*s)
3315                                 break;
3316                         tempchar = *s;
3317                         shadow = true;
3318                         // check for modifier flags
3319                         if (*t == '!')
3320                         {
3321                                 shadow = false;
3322                                 t++;
3323                         }
3324                         *s = 0;
3325                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f %f %f %f %f %i", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2], &coronasizescale, &ambientscale, &diffusescale, &specularscale, &flags);
3326                         *s = tempchar;
3327                         if (a < 18)
3328                                 flags = LIGHTFLAG_REALTIMEMODE;
3329                         if (a < 17)
3330                                 specularscale = 1;
3331                         if (a < 16)
3332                                 diffusescale = 1;
3333                         if (a < 15)
3334                                 ambientscale = 0;
3335                         if (a < 14)
3336                                 coronasizescale = 0.25f;
3337                         if (a < 13)
3338                                 VectorClear(angles);
3339                         if (a < 10)
3340                                 corona = 0;
3341                         if (a < 9 || !strcmp(cubemapname, "\"\""))
3342                                 cubemapname[0] = 0;
3343                         // remove quotes on cubemapname
3344                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3345                         {
3346                                 cubemapname[strlen(cubemapname)-1] = 0;
3347                                 strcpy(cubemapname, cubemapname + 1);
3348                         }
3349                         if (a < 8)
3350                         {
3351                                 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);
3352                                 break;
3353                         }
3354                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
3355                         radius *= r_editlights_rtlightssizescale.value;
3356                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3357                         if (*s == '\r')
3358                                 s++;
3359                         if (*s == '\n')
3360                                 s++;
3361                         n++;
3362                 }
3363                 if (*s)
3364                         Con_Printf("invalid rtlights file \"%s\"\n", name);
3365                 Mem_Free(lightsstring);
3366         }
3367 }
3368
3369 void R_Shadow_SaveWorldLights(void)
3370 {
3371         dlight_t *light;
3372         int bufchars, bufmaxchars;
3373         char *buf, *oldbuf;
3374         char name[MAX_QPATH];
3375         char line[1024];
3376         if (!r_shadow_worldlightchain)
3377                 return;
3378         if (r_refdef.worldmodel == NULL)
3379         {
3380                 Con_Print("No map loaded.\n");
3381                 return;
3382         }
3383         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3384         strlcat (name, ".rtlights", sizeof (name));
3385         bufchars = bufmaxchars = 0;
3386         buf = NULL;
3387         for (light = r_shadow_worldlightchain;light;light = light->next)
3388         {
3389                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3390                         sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, 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);
3391                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3392                         sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
3393                 else
3394                         sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style);
3395                 if (bufchars + (int) strlen(line) > bufmaxchars)
3396                 {
3397                         bufmaxchars = bufchars + strlen(line) + 2048;
3398                         oldbuf = buf;
3399                         buf = Mem_Alloc(tempmempool, bufmaxchars);
3400                         if (oldbuf)
3401                         {
3402                                 if (bufchars)
3403                                         memcpy(buf, oldbuf, bufchars);
3404                                 Mem_Free(oldbuf);
3405                         }
3406                 }
3407                 if (strlen(line))
3408                 {
3409                         memcpy(buf + bufchars, line, strlen(line));
3410                         bufchars += strlen(line);
3411                 }
3412         }
3413         if (bufchars)
3414                 FS_WriteFile(name, buf, bufchars);
3415         if (buf)
3416                 Mem_Free(buf);
3417 }
3418
3419 void R_Shadow_LoadLightsFile(void)
3420 {
3421         int n, a, style;
3422         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3423         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3424         if (r_refdef.worldmodel == NULL)
3425         {
3426                 Con_Print("No map loaded.\n");
3427                 return;
3428         }
3429         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3430         strlcat (name, ".lights", sizeof (name));
3431         lightsstring = (char *)FS_LoadFile(name, tempmempool, false);
3432         if (lightsstring)
3433         {
3434                 s = lightsstring;
3435                 n = 0;
3436                 while (*s)
3437                 {
3438                         t = s;
3439                         while (*s && *s != '\n' && *s != '\r')
3440                                 s++;
3441                         if (!*s)
3442                                 break;
3443                         tempchar = *s;
3444                         *s = 0;
3445                         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);
3446                         *s = tempchar;
3447                         if (a < 14)
3448                         {
3449                                 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);
3450                                 break;
3451                         }
3452                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3453                         radius = bound(15, radius, 4096);
3454                         VectorScale(color, (2.0f / (8388608.0f)), color);
3455                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3456                         if (*s == '\r')
3457                                 s++;
3458                         if (*s == '\n')
3459                                 s++;
3460                         n++;
3461                 }
3462                 if (*s)
3463                         Con_Printf("invalid lights file \"%s\"\n", name);
3464                 Mem_Free(lightsstring);
3465         }
3466 }
3467
3468 // tyrlite/hmap2 light types in the delay field
3469 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3470
3471 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3472 {
3473         int entnum, style, islight, skin, pflags, effects, type, n;
3474         char *entfiledata;
3475         const char *data;
3476         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3477         char key[256], value[1024];
3478
3479         if (r_refdef.worldmodel == NULL)
3480         {
3481                 Con_Print("No map loaded.\n");
3482                 return;
3483         }
3484         // try to load a .ent file first
3485         FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3486         strlcat (key, ".ent", sizeof (key));
3487         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true);
3488         // and if that is not found, fall back to the bsp file entity string
3489         if (!data)
3490                 data = r_refdef.worldmodel->brush.entities;
3491         if (!data)
3492                 return;
3493         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
3494         {
3495                 type = LIGHTTYPE_MINUSX;
3496                 origin[0] = origin[1] = origin[2] = 0;
3497                 originhack[0] = originhack[1] = originhack[2] = 0;
3498                 angles[0] = angles[1] = angles[2] = 0;
3499                 color[0] = color[1] = color[2] = 1;
3500                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3501                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3502                 fadescale = 1;
3503                 lightscale = 1;
3504                 style = 0;
3505                 skin = 0;
3506                 pflags = 0;
3507                 effects = 0;
3508                 islight = false;
3509                 while (1)
3510                 {
3511                         if (!COM_ParseToken(&data, false))
3512                                 break; // error
3513                         if (com_token[0] == '}')
3514                                 break; // end of entity
3515                         if (com_token[0] == '_')
3516                                 strcpy(key, com_token + 1);
3517                         else
3518                                 strcpy(key, com_token);
3519                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3520                                 key[strlen(key)-1] = 0;
3521                         if (!COM_ParseToken(&data, false))
3522                                 break; // error
3523                         strcpy(value, com_token);
3524
3525                         // now that we have the key pair worked out...
3526                         if (!strcmp("light", key))
3527                         {
3528                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3529                                 if (n == 1)
3530                                 {
3531                                         // quake
3532                                         light[0] = vec[0] * (1.0f / 256.0f);
3533                                         light[1] = vec[0] * (1.0f / 256.0f);
3534                                         light[2] = vec[0] * (1.0f / 256.0f);
3535                                         light[3] = vec[0];
3536                                 }
3537                                 else if (n == 4)
3538                                 {
3539                                         // halflife
3540                                         light[0] = vec[0] * (1.0f / 255.0f);
3541                                         light[1] = vec[1] * (1.0f / 255.0f);
3542                                         light[2] = vec[2] * (1.0f / 255.0f);
3543                                         light[3] = vec[3];
3544                                 }
3545                         }
3546                         else if (!strcmp("delay", key))
3547                                 type = atoi(value);
3548                         else if (!strcmp("origin", key))
3549                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3550                         else if (!strcmp("angle", key))
3551                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3552                         else if (!strcmp("angles", key))
3553                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3554                         else if (!strcmp("color", key))
3555                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3556                         else if (!strcmp("wait", key))
3557                                 fadescale = atof(value);
3558                         else if (!strcmp("classname", key))
3559                         {
3560                                 if (!strncmp(value, "light", 5))
3561                                 {
3562                                         islight = true;
3563                                         if (!strcmp(value, "light_fluoro"))
3564                                         {
3565                                                 originhack[0] = 0;
3566                                                 originhack[1] = 0;
3567                                                 originhack[2] = 0;
3568                                                 overridecolor[0] = 1;
3569                                                 overridecolor[1] = 1;
3570                                                 overridecolor[2] = 1;
3571                                         }
3572                                         if (!strcmp(value, "light_fluorospark"))
3573                                         {
3574                                                 originhack[0] = 0;
3575                                                 originhack[1] = 0;
3576                                                 originhack[2] = 0;
3577                                                 overridecolor[0] = 1;
3578                                                 overridecolor[1] = 1;
3579                                                 overridecolor[2] = 1;
3580                                         }
3581                                         if (!strcmp(value, "light_globe"))
3582                                         {
3583                                                 originhack[0] = 0;
3584                                                 originhack[1] = 0;
3585                                                 originhack[2] = 0;
3586                                                 overridecolor[0] = 1;
3587                                                 overridecolor[1] = 0.8;
3588                                                 overridecolor[2] = 0.4;
3589                                         }
3590                                         if (!strcmp(value, "light_flame_large_yellow"))
3591                                         {
3592                                                 originhack[0] = 0;
3593                                                 originhack[1] = 0;
3594                                                 originhack[2] = 48;
3595                                                 overridecolor[0] = 1;
3596                                                 overridecolor[1] = 0.5;
3597                                                 overridecolor[2] = 0.1;
3598                                         }
3599                                         if (!strcmp(value, "light_flame_small_yellow"))
3600                                         {
3601                                                 originhack[0] = 0;
3602                                                 originhack[1] = 0;
3603                                                 originhack[2] = 40;
3604                                                 overridecolor[0] = 1;
3605                                                 overridecolor[1] = 0.5;
3606                                                 overridecolor[2] = 0.1;
3607                                         }
3608                                         if (!strcmp(value, "light_torch_small_white"))
3609                                         {
3610                                                 originhack[0] = 0;
3611                                                 originhack[1] = 0;
3612                                                 originhack[2] = 40;
3613                                                 overridecolor[0] = 1;
3614                                                 overridecolor[1] = 0.5;
3615                                                 overridecolor[2] = 0.1;
3616                                         }
3617                                         if (!strcmp(value, "light_torch_small_walltorch"))
3618                                         {
3619                                                 originhack[0] = 0;
3620                                                 originhack[1] = 0;
3621                                                 originhack[2] = 40;
3622                                                 overridecolor[0] = 1;
3623                                                 overridecolor[1] = 0.5;
3624                                                 overridecolor[2] = 0.1;
3625                                         }
3626                                 }
3627                         }
3628                         else if (!strcmp("style", key))
3629                                 style = atoi(value);
3630                         else if (r_refdef.worldmodel->type == mod_brushq3)
3631                         {
3632                                 if (!strcmp("scale", key))
3633                                         lightscale = atof(value);
3634                                 if (!strcmp("fade", key))
3635                                         fadescale = atof(value);
3636                         }
3637                         else if (!strcmp("skin", key))
3638                                 skin = (int)atof(value);
3639                         else if (!strcmp("pflags", key))
3640                                 pflags = (int)atof(value);
3641                         else if (!strcmp("effects", key))
3642                                 effects = (int)atof(value);
3643                 }
3644                 if (!islight)
3645                         continue;
3646                 if (lightscale <= 0)
3647                         lightscale = 1;
3648                 if (fadescale <= 0)
3649                         fadescale = 1;
3650                 if (color[0] == color[1] && color[0] == color[2])
3651                 {
3652                         color[0] *= overridecolor[0];
3653                         color[1] *= overridecolor[1];
3654                         color[2] *= overridecolor[2];
3655                 }
3656                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3657                 color[0] = color[0] * light[0];
3658                 color[1] = color[1] * light[1];
3659                 color[2] = color[2] * light[2];
3660                 switch (type)
3661                 {
3662                 case LIGHTTYPE_MINUSX:
3663                         break;
3664                 case LIGHTTYPE_RECIPX:
3665                         radius *= 2;
3666                         VectorScale(color, (1.0f / 16.0f), color);
3667                         break;
3668                 case LIGHTTYPE_RECIPXX:
3669                         radius *= 2;
3670                         VectorScale(color, (1.0f / 16.0f), color);
3671                         break;
3672                 default:
3673                 case LIGHTTYPE_NONE:
3674                         break;
3675                 case LIGHTTYPE_SUN:
3676                         break;
3677                 case LIGHTTYPE_MINUSXX:
3678                         break;
3679                 }
3680                 VectorAdd(origin, originhack, origin);
3681                 if (radius >= 1)
3682                         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);
3683         }
3684         if (entfiledata)
3685                 Mem_Free(entfiledata);
3686 }
3687
3688
3689 void R_Shadow_SetCursorLocationForView(void)
3690 {
3691         vec_t dist, push;
3692         vec3_t dest, endpos;
3693         trace_t trace;
3694         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
3695         trace = CL_TraceBox(r_vieworigin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
3696         if (trace.fraction < 1)
3697         {
3698                 dist = trace.fraction * r_editlights_cursordistance.value;
3699                 push = r_editlights_cursorpushback.value;
3700                 if (push > dist)
3701                         push = dist;
3702                 push = -push;
3703                 VectorMA(trace.endpos, push, r_viewforward, endpos);
3704                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
3705         }
3706         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3707         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3708         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3709 }
3710
3711 void R_Shadow_UpdateWorldLightSelection(void)
3712 {
3713         if (r_editlights.integer)
3714         {
3715                 R_Shadow_SetCursorLocationForView();
3716                 R_Shadow_SelectLightInView();
3717                 R_Shadow_DrawLightSprites();
3718         }
3719         else
3720                 R_Shadow_SelectLight(NULL);
3721 }
3722
3723 void R_Shadow_EditLights_Clear_f(void)
3724 {
3725         R_Shadow_ClearWorldLights();
3726 }
3727
3728 void R_Shadow_EditLights_Reload_f(void)
3729 {
3730         if (!r_refdef.worldmodel)
3731                 return;
3732         strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
3733         R_Shadow_ClearWorldLights();
3734         R_Shadow_LoadWorldLights();
3735         if (r_shadow_worldlightchain == NULL)
3736         {
3737                 R_Shadow_LoadLightsFile();
3738                 if (r_shadow_worldlightchain == NULL)
3739                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3740         }
3741 }
3742
3743 void R_Shadow_EditLights_Save_f(void)
3744 {
3745         if (!r_refdef.worldmodel)
3746                 return;
3747         R_Shadow_SaveWorldLights();
3748 }
3749
3750 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3751 {
3752         R_Shadow_ClearWorldLights();
3753         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3754 }
3755
3756 void R_Shadow_EditLights_ImportLightsFile_f(void)
3757 {
3758         R_Shadow_ClearWorldLights();
3759         R_Shadow_LoadLightsFile();
3760 }
3761
3762 void R_Shadow_EditLights_Spawn_f(void)
3763 {
3764         vec3_t color;
3765         if (!r_editlights.integer)
3766         {
3767                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3768                 return;
3769         }
3770         if (Cmd_Argc() != 1)
3771         {
3772                 Con_Print("r_editlights_spawn does not take parameters\n");
3773                 return;
3774         }
3775         color[0] = color[1] = color[2] = 1;
3776         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3777 }
3778
3779 void R_Shadow_EditLights_Edit_f(void)
3780 {
3781         vec3_t origin, angles, color;
3782         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3783         int style, shadows, flags, normalmode, realtimemode;
3784         char cubemapname[1024];
3785         if (!r_editlights.integer)
3786         {
3787                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3788                 return;
3789         }
3790         if (!r_shadow_selectedlight)
3791         {
3792                 Con_Print("No selected light.\n");
3793                 return;
3794         }
3795         VectorCopy(r_shadow_selectedlight->origin, origin);
3796         VectorCopy(r_shadow_selectedlight->angles, angles);
3797         VectorCopy(r_shadow_selectedlight->color, color);
3798         radius = r_shadow_selectedlight->radius;
3799         style = r_shadow_selectedlight->style;
3800         if (r_shadow_selectedlight->cubemapname)
3801                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
3802         else
3803                 cubemapname[0] = 0;
3804         shadows = r_shadow_selectedlight->shadow;
3805         corona = r_shadow_selectedlight->corona;
3806         coronasizescale = r_shadow_selectedlight->coronasizescale;
3807         ambientscale = r_shadow_selectedlight->ambientscale;
3808         diffusescale = r_shadow_selectedlight->diffusescale;
3809         specularscale = r_shadow_selectedlight->specularscale;
3810         flags = r_shadow_selectedlight->flags;
3811         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3812         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3813         if (!strcmp(Cmd_Argv(1), "origin"))
3814         {
3815                 if (Cmd_Argc() != 5)
3816                 {
3817                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3818                         return;
3819                 }
3820                 origin[0] = atof(Cmd_Argv(2));
3821                 origin[1] = atof(Cmd_Argv(3));
3822                 origin[2] = atof(Cmd_Argv(4));
3823         }
3824         else if (!strcmp(Cmd_Argv(1), "originx"))
3825         {
3826                 if (Cmd_Argc() != 3)
3827                 {
3828                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3829                         return;
3830                 }
3831                 origin[0] = atof(Cmd_Argv(2));
3832         }
3833         else if (!strcmp(Cmd_Argv(1), "originy"))
3834         {
3835                 if (Cmd_Argc() != 3)
3836                 {
3837                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3838                         return;
3839                 }
3840                 origin[1] = atof(Cmd_Argv(2));
3841         }
3842         else if (!strcmp(Cmd_Argv(1), "originz"))
3843         {
3844                 if (Cmd_Argc() != 3)
3845                 {
3846                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3847                         return;
3848                 }
3849                 origin[2] = atof(Cmd_Argv(2));
3850         }
3851         else if (!strcmp(Cmd_Argv(1), "move"))
3852         {
3853                 if (Cmd_Argc() != 5)
3854                 {
3855                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3856                         return;
3857                 }
3858                 origin[0] += atof(Cmd_Argv(2));
3859                 origin[1] += atof(Cmd_Argv(3));
3860                 origin[2] += atof(Cmd_Argv(4));
3861         }
3862         else if (!strcmp(Cmd_Argv(1), "movex"))
3863         {
3864                 if (Cmd_Argc() != 3)
3865                 {
3866                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3867                         return;
3868                 }
3869                 origin[0] += atof(Cmd_Argv(2));
3870         }
3871         else if (!strcmp(Cmd_Argv(1), "movey"))
3872         {
3873                 if (Cmd_Argc() != 3)
3874                 {
3875                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3876                         return;
3877                 }
3878                 origin[1] += atof(Cmd_Argv(2));
3879         }
3880         else if (!strcmp(Cmd_Argv(1), "movez"))
3881         {
3882                 if (Cmd_Argc() != 3)
3883                 {
3884                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3885                         return;
3886                 }
3887                 origin[2] += atof(Cmd_Argv(2));
3888         }
3889         else if (!strcmp(Cmd_Argv(1), "angles"))
3890         {
3891                 if (Cmd_Argc() != 5)
3892                 {
3893                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3894                         return;
3895                 }
3896                 angles[0] = atof(Cmd_Argv(2));
3897                 angles[1] = atof(Cmd_Argv(3));
3898                 angles[2] = atof(Cmd_Argv(4));
3899         }
3900         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3901         {
3902                 if (Cmd_Argc() != 3)
3903                 {
3904                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3905                         return;
3906                 }
3907                 angles[0] = atof(Cmd_Argv(2));
3908         }
3909         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3910         {
3911                 if (Cmd_Argc() != 3)
3912                 {
3913                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3914                         return;
3915                 }
3916                 angles[1] = atof(Cmd_Argv(2));
3917         }
3918         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3919         {
3920                 if (Cmd_Argc() != 3)
3921                 {
3922                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3923                         return;
3924                 }
3925                 angles[2] = atof(Cmd_Argv(2));
3926         }
3927         else if (!strcmp(Cmd_Argv(1), "color"))
3928         {
3929                 if (Cmd_Argc() != 5)
3930                 {
3931                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3932                         return;
3933                 }
3934                 color[0] = atof(Cmd_Argv(2));
3935                 color[1] = atof(Cmd_Argv(3));
3936                 color[2] = atof(Cmd_Argv(4));
3937         }
3938         else if (!strcmp(Cmd_Argv(1), "radius"))
3939         {
3940                 if (Cmd_Argc() != 3)
3941                 {
3942                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3943                         return;
3944                 }
3945                 radius = atof(Cmd_Argv(2));
3946         }
3947         else if (!strcmp(Cmd_Argv(1), "style"))
3948         {
3949                 if (Cmd_Argc() != 3)
3950                 {
3951                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3952                         return;
3953                 }
3954                 style = atoi(Cmd_Argv(2));
3955         }
3956         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3957         {
3958                 if (Cmd_Argc() > 3)
3959                 {
3960                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3961                         return;
3962                 }
3963                 if (Cmd_Argc() == 3)
3964                         strcpy(cubemapname, Cmd_Argv(2));
3965                 else
3966                         cubemapname[0] = 0;
3967         }
3968         else if (!strcmp(Cmd_Argv(1), "shadows"))
3969         {
3970                 if (Cmd_Argc() != 3)
3971                 {
3972                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3973                         return;
3974                 }
3975                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3976         }
3977         else if (!strcmp(Cmd_Argv(1), "corona"))
3978         {
3979                 if (Cmd_Argc() != 3)
3980                 {
3981                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3982                         return;
3983                 }
3984                 corona = atof(Cmd_Argv(2));
3985         }
3986         else if (!strcmp(Cmd_Argv(1), "coronasize"))
3987         {
3988                 if (Cmd_Argc() != 3)
3989                 {
3990                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3991                         return;
3992                 }
3993                 coronasizescale = atof(Cmd_Argv(2));
3994         }
3995         else if (!strcmp(Cmd_Argv(1), "ambient"))
3996         {
3997                 if (Cmd_Argc() != 3)
3998                 {
3999                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4000                         return;
4001                 }
4002                 ambientscale = atof(Cmd_Argv(2));
4003         }
4004         else if (!strcmp(Cmd_Argv(1), "diffuse"))
4005         {
4006                 if (Cmd_Argc() != 3)
4007                 {
4008                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4009                         return;
4010                 }
4011                 diffusescale = atof(Cmd_Argv(2));
4012         }
4013         else if (!strcmp(Cmd_Argv(1), "specular"))
4014         {
4015                 if (Cmd_Argc() != 3)
4016                 {
4017                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4018                         return;
4019                 }
4020                 specularscale = atof(Cmd_Argv(2));
4021         }
4022         else if (!strcmp(Cmd_Argv(1), "normalmode"))
4023         {
4024                 if (Cmd_Argc() != 3)
4025                 {
4026                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4027                         return;
4028                 }
4029                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4030         }
4031         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
4032         {
4033                 if (Cmd_Argc() != 3)
4034                 {
4035                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4036                         return;
4037                 }
4038                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4039         }
4040         else
4041         {
4042                 Con_Print("usage: r_editlights_edit [property] [value]\n");
4043                 Con_Print("Selected light's properties:\n");
4044                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4045                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4046                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4047                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
4048                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
4049                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
4050                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4051                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
4052                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
4053                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
4054                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
4055                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
4056                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4057                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4058                 return;
4059         }
4060         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4061         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4062 }
4063
4064 void R_Shadow_EditLights_EditAll_f(void)
4065 {
4066         dlight_t *light;
4067
4068         if (!r_editlights.integer)
4069         {
4070                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4071                 return;
4072         }
4073
4074         for (light = r_shadow_worldlightchain;light;light = light->next)
4075         {
4076                 R_Shadow_SelectLight(light);
4077                 R_Shadow_EditLights_Edit_f();
4078         }
4079 }
4080
4081 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4082 {
4083         int lightnumber, lightcount;
4084         dlight_t *light;
4085         float x, y;
4086         char temp[256];
4087         if (!r_editlights.integer)
4088                 return;
4089         x = 0;
4090         y = con_vislines;
4091         lightnumber = -1;
4092         lightcount = 0;
4093         for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
4094                 if (light == r_shadow_selectedlight)
4095                         lightnumber = lightcount;
4096         sprintf(temp, "Cursor  %f %f %f  Total Lights %i", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2], lightcount);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4097         if (r_shadow_selectedlight == NULL)
4098                 return;
4099         sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4100         sprintf(temp, "Origin       : %f %f %f\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);y += 8;
4101         sprintf(temp, "Angles       : %f %f %f\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);y += 8;
4102         sprintf(temp, "Color        : %f %f %f\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);y += 8;
4103         sprintf(temp, "Radius       : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4104         sprintf(temp, "Corona       : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4105         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4106         sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4107         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4108         sprintf(temp, "CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4109         sprintf(temp, "Ambient      : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4110         sprintf(temp, "Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4111         sprintf(temp, "Specular     : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4112         sprintf(temp, "NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4113         sprintf(temp, "RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
4114 }
4115
4116 void R_Shadow_EditLights_ToggleShadow_f(void)
4117 {
4118         if (!r_editlights.integer)
4119         {
4120                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4121                 return;
4122         }
4123         if (!r_shadow_selectedlight)
4124         {
4125                 Con_Print("No selected light.\n");
4126                 return;
4127         }
4128         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);
4129 }
4130
4131 void R_Shadow_EditLights_ToggleCorona_f(void)
4132 {
4133         if (!r_editlights.integer)
4134         {
4135                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4136                 return;
4137         }
4138         if (!r_shadow_selectedlight)
4139         {
4140                 Con_Print("No selected light.\n");
4141                 return;
4142         }
4143         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);
4144 }
4145
4146 void R_Shadow_EditLights_Remove_f(void)
4147 {
4148         if (!r_editlights.integer)
4149         {
4150                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
4151                 return;
4152         }
4153         if (!r_shadow_selectedlight)
4154         {
4155                 Con_Print("No selected light.\n");
4156                 return;
4157         }
4158         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4159         r_shadow_selectedlight = NULL;
4160 }
4161
4162 void R_Shadow_EditLights_Help_f(void)
4163 {
4164         Con_Print(
4165 "Documentation on r_editlights system:\n"
4166 "Settings:\n"
4167 "r_editlights : enable/disable editing mode\n"
4168 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4169 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4170 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4171 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4172 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4173 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
4174 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
4175 "Commands:\n"
4176 "r_editlights_help : this help\n"
4177 "r_editlights_clear : remove all lights\n"
4178 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4179 "r_editlights_save : save to .rtlights file\n"
4180 "r_editlights_spawn : create a light with default settings\n"
4181 "r_editlights_edit command : edit selected light - more documentation below\n"
4182 "r_editlights_remove : remove selected light\n"
4183 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4184 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4185 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4186 "Edit commands:\n"
4187 "origin x y z : set light location\n"
4188 "originx x: set x component of light location\n"
4189 "originy y: set y component of light location\n"
4190 "originz z: set z component of light location\n"
4191 "move x y z : adjust light location\n"
4192 "movex x: adjust x component of light location\n"
4193 "movey y: adjust y component of light location\n"
4194 "movez z: adjust z component of light location\n"
4195 "angles x y z : set light angles\n"
4196 "anglesx x: set x component of light angles\n"
4197 "anglesy y: set y component of light angles\n"
4198 "anglesz z: set z component of light angles\n"
4199 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4200 "radius radius : set radius (size) of light\n"
4201 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4202 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4203 "shadows 1/0 : turn on/off shadows\n"
4204 "corona n : set corona intensity\n"
4205 "coronasize n : set corona size (0-1)\n"
4206 "ambient n : set ambient intensity (0-1)\n"
4207 "diffuse n : set diffuse intensity (0-1)\n"
4208 "specular n : set specular intensity (0-1)\n"
4209 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4210 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4211 "<nothing> : print light properties to console\n"
4212         );
4213 }
4214
4215 void R_Shadow_EditLights_CopyInfo_f(void)
4216 {
4217         if (!r_editlights.integer)
4218         {
4219                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
4220                 return;
4221         }
4222         if (!r_shadow_selectedlight)
4223         {
4224                 Con_Print("No selected light.\n");
4225                 return;
4226         }
4227         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4228         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4229         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4230         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4231         if (r_shadow_selectedlight->cubemapname)
4232                 strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
4233         else
4234                 r_shadow_bufferlight.cubemapname[0] = 0;
4235         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4236         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4237         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4238         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4239         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4240         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4241         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4242 }
4243
4244 void R_Shadow_EditLights_PasteInfo_f(void)
4245 {
4246         if (!r_editlights.integer)
4247         {
4248                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
4249                 return;
4250         }
4251         if (!r_shadow_selectedlight)
4252         {
4253                 Con_Print("No selected light.\n");
4254                 return;
4255         }
4256         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);
4257 }
4258
4259 void R_Shadow_EditLights_Init(void)
4260 {
4261         Cvar_RegisterVariable(&r_editlights);
4262         Cvar_RegisterVariable(&r_editlights_cursordistance);
4263         Cvar_RegisterVariable(&r_editlights_cursorpushback);
4264         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4265         Cvar_RegisterVariable(&r_editlights_cursorgrid);
4266         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4267         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
4268         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
4269         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
4270         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
4271         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
4272         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
4273         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
4274         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
4275         Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f);
4276         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
4277         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
4278         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
4279         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
4280         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
4281         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f);
4282         Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f);
4283 }
4284