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