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