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