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