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