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