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