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