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