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