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