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