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