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