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