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