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