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