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