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