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