]> git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
load rtlights file sooner in map change process, to avoid it showing up
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is normally rendered using Carmack's Reverse technique, in which
11 backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
12 zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
13 where shadows did not intersect the visible geometry, suitable as a stencil
14 mask for rendering lighting everywhere but shadow.
15
16 In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
17 as decrement and the frontfaces as increment, and we redefine the DepthFunc to
18 GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
19 and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
20 additionally we clear stencil to 128 to avoid the need for the unclamped
21 incr/decr extension (not related to patent).
22
23 Patent warning:
24 This algorithm may be covered by Creative's patent (US Patent #6384822),
25 however that patent is quite specific about increment on backfaces and
26 decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
27 opposite this implementation and partially opposite Carmack's Reverse paper
28 (which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
29
30
31
32 Terminology: Stencil Light Volume (sometimes called Light Volumes)
33 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
34 areas in shadow it contains the areas in light, this can only be built
35 quickly for certain limited cases (such as portal visibility from a point),
36 but is quite useful for some effects (sunlight coming from sky polygons is
37 one possible example, translucent occluders is another example).
38
39
40
41 Terminology: Optimized Stencil Shadow Volume
42 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
43 no duplicate coverage of areas (no need to shadow an area twice), often this
44 greatly improves performance but is an operation too costly to use on moving
45 lights (however completely optimal Stencil Light Volumes can be constructed
46 in some ideal cases).
47
48
49
50 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
51 Per pixel evaluation of lighting equations, at a bare minimum this involves
52 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
53 vector and surface normal, using a texture of the surface bumps, called a
54 NormalMap) if supported by hardware; in our case there is support for cards
55 which are incapable of DOT3, the quality is quite poor however.  Additionally
56 it is desirable to have specular evaluation per pixel, per vertex
57 normalization of specular halfangle vectors causes noticable distortion but
58 is unavoidable on hardware without GL_ARB_fragment_program or
59 GL_ARB_fragment_shader.
60
61
62
63 Terminology: Normalization CubeMap
64 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
65 encoded as RGB colors) for any possible direction, this technique allows per
66 pixel calculation of incidence vector for per pixel lighting purposes, which
67 would not otherwise be possible per pixel without GL_ARB_fragment_program or
68 GL_ARB_fragment_shader.
69
70
71
72 Terminology: 2D+1D Attenuation Texturing
73 A very crude approximation of light attenuation with distance which results
74 in cylindrical light shapes which fade vertically as a streak (some games
75 such as Doom3 allow this to be rotated to be less noticable in specific
76 cases), the technique is simply modulating lighting by two 2D textures (which
77 can be the same) on different axes of projection (XY and Z, typically), this
78 is the second best technique available without 3D Attenuation Texturing,
79 GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
80
81
82
83 Terminology: 2D+1D Inverse Attenuation Texturing
84 A clever method described in papers on the Abducted engine, this has a squared
85 distance texture (bright on the outside, black in the middle), which is used
86 twice using GL_ADD blending, the result of this is used in an inverse modulate
87 (GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
88 lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
89 texturing).
90
91
92
93 Terminology: 3D Attenuation Texturing
94 A slightly crude approximation of light attenuation with distance, its flaws
95 are limited radius and resolution (performance tradeoffs).
96
97
98
99 Terminology: 3D Attenuation-Normalization Texturing
100 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
101 vectors shorter the lighting becomes darker, a very effective optimization of
102 diffuse lighting if 3D Attenuation Textures are already used.
103
104
105
106 Terminology: Light Cubemap Filtering
107 A technique for modeling non-uniform light distribution according to
108 direction, for example a lantern may use a cubemap to describe the light
109 emission pattern of the cage around the lantern (as well as soot buildup
110 discoloring the light in certain areas), often also used for softened grate
111 shadows and light shining through a stained glass window (done crudely by
112 texturing the lighting with a cubemap), another good example would be a disco
113 light.  This technique is used heavily in many games (Doom3 does not support
114 this however).
115
116
117
118 Terminology: Light Projection Filtering
119 A technique for modeling shadowing of light passing through translucent
120 surfaces, allowing stained glass windows and other effects to be done more
121 elegantly than possible with Light Cubemap Filtering by applying an occluder
122 texture to the lighting combined with a stencil light volume to limit the lit
123 area, this technique is used by Doom3 for spotlights and flashlights, among
124 other things, this can also be used more generally to render light passing
125 through multiple translucent occluders in a scene (using a light volume to
126 describe the area beyond the occluder, and thus mask off rendering of all
127 other areas).
128
129
130
131 Terminology: Doom3 Lighting
132 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
133 CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
134 demonstrated by the game Doom3.
135 */
136
137 #include "quakedef.h"
138 #include "r_shadow.h"
139 #include "cl_collision.h"
140 #include "portals.h"
141 #include "image.h"
142
143 extern void R_Shadow_EditLights_Init(void);
144
145 typedef enum r_shadow_rendermode_e
146 {
147         R_SHADOW_RENDERMODE_NONE,
148         R_SHADOW_RENDERMODE_STENCIL,
149         R_SHADOW_RENDERMODE_SEPARATESTENCIL,
150         R_SHADOW_RENDERMODE_STENCILTWOSIDE,
151         R_SHADOW_RENDERMODE_LIGHT_VERTEX,
152         R_SHADOW_RENDERMODE_LIGHT_DOT3,
153         R_SHADOW_RENDERMODE_LIGHT_GLSL,
154         R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
155         R_SHADOW_RENDERMODE_VISIBLELIGHTING,
156 }
157 r_shadow_rendermode_t;
158
159 r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
160 r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
161 r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
162
163 int maxshadowtriangles;
164 int *shadowelements;
165
166 int maxshadowvertices;
167 float *shadowvertex3f;
168
169 int maxshadowmark;
170 int numshadowmark;
171 int *shadowmark;
172 int *shadowmarklist;
173 int shadowmarkcount;
174
175 int maxvertexupdate;
176 int *vertexupdate;
177 int *vertexremap;
178 int vertexupdatenum;
179
180 int r_shadow_buffer_numleafpvsbytes;
181 unsigned char *r_shadow_buffer_leafpvs;
182 int *r_shadow_buffer_leaflist;
183
184 int r_shadow_buffer_numsurfacepvsbytes;
185 unsigned char *r_shadow_buffer_surfacepvs;
186 int *r_shadow_buffer_surfacelist;
187
188 int r_shadow_buffer_numshadowtrispvsbytes;
189 unsigned char *r_shadow_buffer_shadowtrispvs;
190 int r_shadow_buffer_numlighttrispvsbytes;
191 unsigned char *r_shadow_buffer_lighttrispvs;
192
193 rtexturepool_t *r_shadow_texturepool;
194 rtexture_t *r_shadow_attenuationgradienttexture;
195 rtexture_t *r_shadow_attenuation2dtexture;
196 rtexture_t *r_shadow_attenuation3dtexture;
197 rtexture_t *r_shadow_lightcorona;
198
199 // lights are reloaded when this changes
200 char r_shadow_mapname[MAX_QPATH];
201
202 // used only for light filters (cubemaps)
203 rtexturepool_t *r_shadow_filters_texturepool;
204
205 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
206 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
207 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
208 cvar_t r_shadow_usenormalmap = {CVAR_SAVE, "r_shadow_usenormalmap", "1", "enables use of directional shading on lights"};
209 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
210 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.125", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
211 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
212 cvar_t r_shadow_glossexponent = {0, "r_shadow_glossexponent", "32", "how 'sharp' the gloss should appear (specular power)"};
213 cvar_t r_shadow_lightattenuationdividebias = {0, "r_shadow_lightattenuationdividebias", "1", "changes attenuation texture generation"};
214 cvar_t r_shadow_lightattenuationlinearscale = {0, "r_shadow_lightattenuationlinearscale", "2", "changes attenuation texture generation"};
215 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
216 cvar_t r_shadow_lightradiusscale = {0, "r_shadow_lightradiusscale", "1", "renders all world lights larger or smaller"};
217 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
218 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
219 cvar_t r_shadow_frontsidecasting = {0, "r_shadow_frontsidecasting", "1", "whether to cast shadows from illuminated triangles (front side of model) or unlit triangles (back side of model)"};
220 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
221 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
222 cvar_t r_shadow_realtime_dlight_svbspculling = {0, "r_shadow_realtime_dlight_svbspculling", "0", "enables svbsp optimization on dynamic lights (very slow!)"};
223 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal optimization on dynamic lights (slow!)"};
224 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"};
225 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"};
226 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
227 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
228 cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1", "enables compilation of shadows from world lights for higher performance rendering"};
229 cvar_t r_shadow_realtime_world_compilesvbsp = {0, "r_shadow_realtime_world_compilesvbsp", "1", "enables svbsp optimization during compilation"};
230 cvar_t r_shadow_realtime_world_compileportalculling = {0, "r_shadow_realtime_world_compileportalculling", "1", "enables portal-based culling optimization during compilation"};
231 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
232 cvar_t r_shadow_culltriangles = {0, "r_shadow_culltriangles", "1", "performs more expensive tests to remove unnecessary triangles of lit surfaces"};
233 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
234 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
235 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
236 cvar_t r_coronas = {CVAR_SAVE, "r_coronas", "1", "brightness of corona flare effects around certain lights, 0 disables corona effects"};
237 cvar_t gl_flashblend = {CVAR_SAVE, "gl_flashblend", "0", "render bright coronas for dynamic lights instead of actual lighting, fast but ugly"};
238 cvar_t gl_ext_separatestencil = {0, "gl_ext_separatestencil", "1", "make use of OpenGL 2.0 glStencilOpSeparate or GL_ATI_separate_stencil extension"};
239 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
240 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
241 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
242 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
243 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
244 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
245 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
246
247 // note the table actually includes one more value, just to avoid the need to clamp the distance index due to minor math error
248 #define ATTENTABLESIZE 256
249 // 1D gradient, 2D circle and 3D sphere attenuation textures
250 #define ATTEN1DSIZE 32
251 #define ATTEN2DSIZE 64
252 #define ATTEN3DSIZE 32
253
254 static float r_shadow_attendividebias; // r_shadow_lightattenuationdividebias
255 static float r_shadow_attenlinearscale; // r_shadow_lightattenuationlinearscale
256 static float r_shadow_attentable[ATTENTABLESIZE+1];
257
258 rtlight_t *r_shadow_compilingrtlight;
259 static memexpandablearray_t r_shadow_worldlightsarray;
260 dlight_t *r_shadow_selectedlight;
261 dlight_t r_shadow_bufferlight;
262 vec3_t r_editlights_cursorlocation;
263
264 extern int con_vislines;
265
266 typedef struct cubemapinfo_s
267 {
268         char basename[64];
269         rtexture_t *texture;
270 }
271 cubemapinfo_t;
272
273 #define MAX_CUBEMAPS 256
274 static int numcubemaps;
275 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
276
277 void R_Shadow_UncompileWorldLights(void);
278 void R_Shadow_ClearWorldLights(void);
279 void R_Shadow_SaveWorldLights(void);
280 void R_Shadow_LoadWorldLights(void);
281 void R_Shadow_LoadLightsFile(void);
282 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
283 void R_Shadow_EditLights_Reload_f(void);
284 void R_Shadow_ValidateCvars(void);
285 static void R_Shadow_MakeTextures(void);
286
287 // VorteX: custom editor light sprites
288 #define EDLIGHTSPRSIZE                  8
289 cachepic_t *r_editlights_sprcursor;
290 cachepic_t *r_editlights_sprlight;
291 cachepic_t *r_editlights_sprnoshadowlight;
292 cachepic_t *r_editlights_sprcubemaplight;
293 cachepic_t *r_editlights_sprcubemapnoshadowlight;
294 cachepic_t *r_editlights_sprselection;
295
296 void r_shadow_start(void)
297 {
298         // allocate vertex processing arrays
299         numcubemaps = 0;
300         r_shadow_attenuationgradienttexture = NULL;
301         r_shadow_attenuation2dtexture = NULL;
302         r_shadow_attenuation3dtexture = NULL;
303         r_shadow_texturepool = NULL;
304         r_shadow_filters_texturepool = NULL;
305         R_Shadow_ValidateCvars();
306         R_Shadow_MakeTextures();
307         maxshadowtriangles = 0;
308         shadowelements = NULL;
309         maxshadowvertices = 0;
310         shadowvertex3f = NULL;
311         maxvertexupdate = 0;
312         vertexupdate = NULL;
313         vertexremap = NULL;
314         vertexupdatenum = 0;
315         maxshadowmark = 0;
316         numshadowmark = 0;
317         shadowmark = NULL;
318         shadowmarklist = NULL;
319         shadowmarkcount = 0;
320         r_shadow_buffer_numleafpvsbytes = 0;
321         r_shadow_buffer_leafpvs = NULL;
322         r_shadow_buffer_leaflist = NULL;
323         r_shadow_buffer_numsurfacepvsbytes = 0;
324         r_shadow_buffer_surfacepvs = NULL;
325         r_shadow_buffer_surfacelist = NULL;
326         r_shadow_buffer_numshadowtrispvsbytes = 0;
327         r_shadow_buffer_shadowtrispvs = NULL;
328         r_shadow_buffer_numlighttrispvsbytes = 0;
329         r_shadow_buffer_lighttrispvs = NULL;
330 }
331
332 void r_shadow_shutdown(void)
333 {
334         R_Shadow_UncompileWorldLights();
335         numcubemaps = 0;
336         r_shadow_attenuationgradienttexture = NULL;
337         r_shadow_attenuation2dtexture = NULL;
338         r_shadow_attenuation3dtexture = NULL;
339         R_FreeTexturePool(&r_shadow_texturepool);
340         R_FreeTexturePool(&r_shadow_filters_texturepool);
341         maxshadowtriangles = 0;
342         if (shadowelements)
343                 Mem_Free(shadowelements);
344         shadowelements = NULL;
345         if (shadowvertex3f)
346                 Mem_Free(shadowvertex3f);
347         shadowvertex3f = NULL;
348         maxvertexupdate = 0;
349         if (vertexupdate)
350                 Mem_Free(vertexupdate);
351         vertexupdate = NULL;
352         if (vertexremap)
353                 Mem_Free(vertexremap);
354         vertexremap = NULL;
355         vertexupdatenum = 0;
356         maxshadowmark = 0;
357         numshadowmark = 0;
358         if (shadowmark)
359                 Mem_Free(shadowmark);
360         shadowmark = NULL;
361         if (shadowmarklist)
362                 Mem_Free(shadowmarklist);
363         shadowmarklist = NULL;
364         shadowmarkcount = 0;
365         r_shadow_buffer_numleafpvsbytes = 0;
366         if (r_shadow_buffer_leafpvs)
367                 Mem_Free(r_shadow_buffer_leafpvs);
368         r_shadow_buffer_leafpvs = NULL;
369         if (r_shadow_buffer_leaflist)
370                 Mem_Free(r_shadow_buffer_leaflist);
371         r_shadow_buffer_leaflist = NULL;
372         r_shadow_buffer_numsurfacepvsbytes = 0;
373         if (r_shadow_buffer_surfacepvs)
374                 Mem_Free(r_shadow_buffer_surfacepvs);
375         r_shadow_buffer_surfacepvs = NULL;
376         if (r_shadow_buffer_surfacelist)
377                 Mem_Free(r_shadow_buffer_surfacelist);
378         r_shadow_buffer_surfacelist = NULL;
379         r_shadow_buffer_numshadowtrispvsbytes = 0;
380         if (r_shadow_buffer_shadowtrispvs)
381                 Mem_Free(r_shadow_buffer_shadowtrispvs);
382         r_shadow_buffer_numlighttrispvsbytes = 0;
383         if (r_shadow_buffer_lighttrispvs)
384                 Mem_Free(r_shadow_buffer_lighttrispvs);
385 }
386
387 void r_shadow_newmap(void)
388 {
389         if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
390                 R_Shadow_EditLights_Reload_f();
391 }
392
393 void R_Shadow_Help_f(void)
394 {
395         Con_Printf(
396 "Documentation on r_shadow system:\n"
397 "Settings:\n"
398 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
399 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
400 "r_shadow_debuglight : render only this light number (-1 = all)\n"
401 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
402 "r_shadow_gloss2intensity : brightness of forced gloss\n"
403 "r_shadow_glossintensity : brightness of textured gloss\n"
404 "r_shadow_lightattenuationlinearscale : used to generate attenuation texture\n"
405 "r_shadow_lightattenuationdividebias : used to generate attenuation texture\n"
406 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
407 "r_shadow_lightradiusscale : scale rendering radius of all lights\n"
408 "r_shadow_portallight : use portal visibility for static light precomputation\n"
409 "r_shadow_projectdistance : shadow volume projection distance\n"
410 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
411 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
412 "r_shadow_realtime_world : use high quality world lighting mode\n"
413 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
414 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
415 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
416 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
417 "r_shadow_scissor : use scissor optimization\n"
418 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
419 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
420 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
421 "r_showlighting : useful for performance testing; bright = slow!\n"
422 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
423 "Commands:\n"
424 "r_shadow_help : this help\n"
425         );
426 }
427
428 void R_Shadow_Init(void)
429 {
430         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
431         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
432         Cvar_RegisterVariable(&r_shadow_usenormalmap);
433         Cvar_RegisterVariable(&r_shadow_debuglight);
434         Cvar_RegisterVariable(&r_shadow_gloss);
435         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
436         Cvar_RegisterVariable(&r_shadow_glossintensity);
437         Cvar_RegisterVariable(&r_shadow_glossexponent);
438         Cvar_RegisterVariable(&r_shadow_lightattenuationdividebias);
439         Cvar_RegisterVariable(&r_shadow_lightattenuationlinearscale);
440         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
441         Cvar_RegisterVariable(&r_shadow_lightradiusscale);
442         Cvar_RegisterVariable(&r_shadow_portallight);
443         Cvar_RegisterVariable(&r_shadow_projectdistance);
444         Cvar_RegisterVariable(&r_shadow_frontsidecasting);
445         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
446         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
447         Cvar_RegisterVariable(&r_shadow_realtime_dlight_svbspculling);
448         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
449         Cvar_RegisterVariable(&r_shadow_realtime_world);
450         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
451         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
452         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
453         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
454         Cvar_RegisterVariable(&r_shadow_realtime_world_compilesvbsp);
455         Cvar_RegisterVariable(&r_shadow_realtime_world_compileportalculling);
456         Cvar_RegisterVariable(&r_shadow_scissor);
457         Cvar_RegisterVariable(&r_shadow_culltriangles);
458         Cvar_RegisterVariable(&r_shadow_polygonfactor);
459         Cvar_RegisterVariable(&r_shadow_polygonoffset);
460         Cvar_RegisterVariable(&r_shadow_texture3d);
461         Cvar_RegisterVariable(&r_coronas);
462         Cvar_RegisterVariable(&gl_flashblend);
463         Cvar_RegisterVariable(&gl_ext_separatestencil);
464         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
465         if (gamemode == GAME_TENEBRAE)
466         {
467                 Cvar_SetValue("r_shadow_gloss", 2);
468                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
469         }
470         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
471         R_Shadow_EditLights_Init();
472         Mem_ExpandableArray_NewArray(&r_shadow_worldlightsarray, r_main_mempool, sizeof(dlight_t), 128);
473         maxshadowtriangles = 0;
474         shadowelements = NULL;
475         maxshadowvertices = 0;
476         shadowvertex3f = NULL;
477         maxvertexupdate = 0;
478         vertexupdate = NULL;
479         vertexremap = NULL;
480         vertexupdatenum = 0;
481         maxshadowmark = 0;
482         numshadowmark = 0;
483         shadowmark = NULL;
484         shadowmarklist = NULL;
485         shadowmarkcount = 0;
486         r_shadow_buffer_numleafpvsbytes = 0;
487         r_shadow_buffer_leafpvs = NULL;
488         r_shadow_buffer_leaflist = NULL;
489         r_shadow_buffer_numsurfacepvsbytes = 0;
490         r_shadow_buffer_surfacepvs = NULL;
491         r_shadow_buffer_surfacelist = NULL;
492         r_shadow_buffer_shadowtrispvs = NULL;
493         r_shadow_buffer_lighttrispvs = NULL;
494         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
495 }
496
497 matrix4x4_t matrix_attenuationxyz =
498 {
499         {
500                 {0.5, 0.0, 0.0, 0.5},
501                 {0.0, 0.5, 0.0, 0.5},
502                 {0.0, 0.0, 0.5, 0.5},
503                 {0.0, 0.0, 0.0, 1.0}
504         }
505 };
506
507 matrix4x4_t matrix_attenuationz =
508 {
509         {
510                 {0.0, 0.0, 0.5, 0.5},
511                 {0.0, 0.0, 0.0, 0.5},
512                 {0.0, 0.0, 0.0, 0.5},
513                 {0.0, 0.0, 0.0, 1.0}
514         }
515 };
516
517 void R_Shadow_ResizeShadowArrays(int numvertices, int numtriangles)
518 {
519         // make sure shadowelements is big enough for this volume
520         if (maxshadowtriangles < numtriangles)
521         {
522                 maxshadowtriangles = numtriangles;
523                 if (shadowelements)
524                         Mem_Free(shadowelements);
525                 shadowelements = (int *)Mem_Alloc(r_main_mempool, maxshadowtriangles * sizeof(int[24]));
526         }
527         // make sure shadowvertex3f is big enough for this volume
528         if (maxshadowvertices < numvertices)
529         {
530                 maxshadowvertices = numvertices;
531                 if (shadowvertex3f)
532                         Mem_Free(shadowvertex3f);
533                 shadowvertex3f = (float *)Mem_Alloc(r_main_mempool, maxshadowvertices * sizeof(float[6]));
534         }
535 }
536
537 static void R_Shadow_EnlargeLeafSurfaceTrisBuffer(int numleafs, int numsurfaces, int numshadowtriangles, int numlighttriangles)
538 {
539         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
540         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
541         int numshadowtrispvsbytes = (((numshadowtriangles + 7) >> 3) + 255) & ~255;
542         int numlighttrispvsbytes = (((numlighttriangles + 7) >> 3) + 255) & ~255;
543         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
544         {
545                 if (r_shadow_buffer_leafpvs)
546                         Mem_Free(r_shadow_buffer_leafpvs);
547                 if (r_shadow_buffer_leaflist)
548                         Mem_Free(r_shadow_buffer_leaflist);
549                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
550                 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes);
551                 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
552         }
553         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
554         {
555                 if (r_shadow_buffer_surfacepvs)
556                         Mem_Free(r_shadow_buffer_surfacepvs);
557                 if (r_shadow_buffer_surfacelist)
558                         Mem_Free(r_shadow_buffer_surfacelist);
559                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
560                 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes);
561                 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
562         }
563         if (r_shadow_buffer_numshadowtrispvsbytes < numshadowtrispvsbytes)
564         {
565                 if (r_shadow_buffer_shadowtrispvs)
566                         Mem_Free(r_shadow_buffer_shadowtrispvs);
567                 r_shadow_buffer_numshadowtrispvsbytes = numshadowtrispvsbytes;
568                 r_shadow_buffer_shadowtrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numshadowtrispvsbytes);
569         }
570         if (r_shadow_buffer_numlighttrispvsbytes < numlighttrispvsbytes)
571         {
572                 if (r_shadow_buffer_lighttrispvs)
573                         Mem_Free(r_shadow_buffer_lighttrispvs);
574                 r_shadow_buffer_numlighttrispvsbytes = numlighttrispvsbytes;
575                 r_shadow_buffer_lighttrispvs = (unsigned char *)Mem_Alloc(r_main_mempool, r_shadow_buffer_numlighttrispvsbytes);
576         }
577 }
578
579 void R_Shadow_PrepareShadowMark(int numtris)
580 {
581         // make sure shadowmark is big enough for this volume
582         if (maxshadowmark < numtris)
583         {
584                 maxshadowmark = numtris;
585                 if (shadowmark)
586                         Mem_Free(shadowmark);
587                 if (shadowmarklist)
588                         Mem_Free(shadowmarklist);
589                 shadowmark = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmark));
590                 shadowmarklist = (int *)Mem_Alloc(r_main_mempool, maxshadowmark * sizeof(*shadowmarklist));
591                 shadowmarkcount = 0;
592         }
593         shadowmarkcount++;
594         // if shadowmarkcount wrapped we clear the array and adjust accordingly
595         if (shadowmarkcount == 0)
596         {
597                 shadowmarkcount = 1;
598                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
599         }
600         numshadowmark = 0;
601 }
602
603 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, const float *projectdirection, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
604 {
605         int i, j;
606         int outtriangles = 0, outvertices = 0;
607         const int *element;
608         const float *vertex;
609         float ratio, direction[3], projectvector[3];
610
611         if (projectdirection)
612                 VectorScale(projectdirection, projectdistance, projectvector);
613         else
614                 VectorClear(projectvector);
615
616         if (maxvertexupdate < innumvertices)
617         {
618                 maxvertexupdate = innumvertices;
619                 if (vertexupdate)
620                         Mem_Free(vertexupdate);
621                 if (vertexremap)
622                         Mem_Free(vertexremap);
623                 vertexupdate = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
624                 vertexremap = (int *)Mem_Alloc(r_main_mempool, maxvertexupdate * sizeof(int));
625                 vertexupdatenum = 0;
626         }
627         vertexupdatenum++;
628         if (vertexupdatenum == 0)
629         {
630                 vertexupdatenum = 1;
631                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
632                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
633         }
634
635         for (i = 0;i < numshadowmarktris;i++)
636                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
637
638         // create the vertices
639         if (projectdirection)
640         {
641                 for (i = 0;i < numshadowmarktris;i++)
642                 {
643                         element = inelement3i + shadowmarktris[i] * 3;
644                         for (j = 0;j < 3;j++)
645                         {
646                                 if (vertexupdate[element[j]] != vertexupdatenum)
647                                 {
648                                         vertexupdate[element[j]] = vertexupdatenum;
649                                         vertexremap[element[j]] = outvertices;
650                                         vertex = invertex3f + element[j] * 3;
651                                         // project one copy of the vertex according to projectvector
652                                         VectorCopy(vertex, outvertex3f);
653                                         VectorAdd(vertex, projectvector, (outvertex3f + 3));
654                                         outvertex3f += 6;
655                                         outvertices += 2;
656                                 }
657                         }
658                 }
659         }
660         else
661         {
662                 for (i = 0;i < numshadowmarktris;i++)
663                 {
664                         element = inelement3i + shadowmarktris[i] * 3;
665                         for (j = 0;j < 3;j++)
666                         {
667                                 if (vertexupdate[element[j]] != vertexupdatenum)
668                                 {
669                                         vertexupdate[element[j]] = vertexupdatenum;
670                                         vertexremap[element[j]] = outvertices;
671                                         vertex = invertex3f + element[j] * 3;
672                                         // project one copy of the vertex to the sphere radius of the light
673                                         // (FIXME: would projecting it to the light box be better?)
674                                         VectorSubtract(vertex, projectorigin, direction);
675                                         ratio = projectdistance / VectorLength(direction);
676                                         VectorCopy(vertex, outvertex3f);
677                                         VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
678                                         outvertex3f += 6;
679                                         outvertices += 2;
680                                 }
681                         }
682                 }
683         }
684
685         if (r_shadow_frontsidecasting.integer)
686         {
687                 for (i = 0;i < numshadowmarktris;i++)
688                 {
689                         int remappedelement[3];
690                         int markindex;
691                         const int *neighbortriangle;
692
693                         markindex = shadowmarktris[i] * 3;
694                         element = inelement3i + markindex;
695                         neighbortriangle = inneighbor3i + markindex;
696                         // output the front and back triangles
697                         outelement3i[0] = vertexremap[element[0]];
698                         outelement3i[1] = vertexremap[element[1]];
699                         outelement3i[2] = vertexremap[element[2]];
700                         outelement3i[3] = vertexremap[element[2]] + 1;
701                         outelement3i[4] = vertexremap[element[1]] + 1;
702                         outelement3i[5] = vertexremap[element[0]] + 1;
703
704                         outelement3i += 6;
705                         outtriangles += 2;
706                         // output the sides (facing outward from this triangle)
707                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
708                         {
709                                 remappedelement[0] = vertexremap[element[0]];
710                                 remappedelement[1] = vertexremap[element[1]];
711                                 outelement3i[0] = remappedelement[1];
712                                 outelement3i[1] = remappedelement[0];
713                                 outelement3i[2] = remappedelement[0] + 1;
714                                 outelement3i[3] = remappedelement[1];
715                                 outelement3i[4] = remappedelement[0] + 1;
716                                 outelement3i[5] = remappedelement[1] + 1;
717
718                                 outelement3i += 6;
719                                 outtriangles += 2;
720                         }
721                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
722                         {
723                                 remappedelement[1] = vertexremap[element[1]];
724                                 remappedelement[2] = vertexremap[element[2]];
725                                 outelement3i[0] = remappedelement[2];
726                                 outelement3i[1] = remappedelement[1];
727                                 outelement3i[2] = remappedelement[1] + 1;
728                                 outelement3i[3] = remappedelement[2];
729                                 outelement3i[4] = remappedelement[1] + 1;
730                                 outelement3i[5] = remappedelement[2] + 1;
731
732                                 outelement3i += 6;
733                                 outtriangles += 2;
734                         }
735                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
736                         {
737                                 remappedelement[0] = vertexremap[element[0]];
738                                 remappedelement[2] = vertexremap[element[2]];
739                                 outelement3i[0] = remappedelement[0];
740                                 outelement3i[1] = remappedelement[2];
741                                 outelement3i[2] = remappedelement[2] + 1;
742                                 outelement3i[3] = remappedelement[0];
743                                 outelement3i[4] = remappedelement[2] + 1;
744                                 outelement3i[5] = remappedelement[0] + 1;
745
746                                 outelement3i += 6;
747                                 outtriangles += 2;
748                         }
749                 }
750         }
751         else
752         {
753                 for (i = 0;i < numshadowmarktris;i++)
754                 {
755                         int remappedelement[3];
756                         int markindex;
757                         const int *neighbortriangle;
758
759                         markindex = shadowmarktris[i] * 3;
760                         element = inelement3i + markindex;
761                         neighbortriangle = inneighbor3i + markindex;
762                         // output the front and back triangles
763                         outelement3i[0] = vertexremap[element[2]];
764                         outelement3i[1] = vertexremap[element[1]];
765                         outelement3i[2] = vertexremap[element[0]];
766                         outelement3i[3] = vertexremap[element[0]] + 1;
767                         outelement3i[4] = vertexremap[element[1]] + 1;
768                         outelement3i[5] = vertexremap[element[2]] + 1;
769
770                         outelement3i += 6;
771                         outtriangles += 2;
772                         // output the sides (facing outward from this triangle)
773                         if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
774                         {
775                                 remappedelement[0] = vertexremap[element[0]];
776                                 remappedelement[1] = vertexremap[element[1]];
777                                 outelement3i[0] = remappedelement[0];
778                                 outelement3i[1] = remappedelement[1];
779                                 outelement3i[2] = remappedelement[1] + 1;
780                                 outelement3i[3] = remappedelement[0];
781                                 outelement3i[4] = remappedelement[1] + 1;
782                                 outelement3i[5] = remappedelement[0] + 1;
783
784                                 outelement3i += 6;
785                                 outtriangles += 2;
786                         }
787                         if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
788                         {
789                                 remappedelement[1] = vertexremap[element[1]];
790                                 remappedelement[2] = vertexremap[element[2]];
791                                 outelement3i[0] = remappedelement[1];
792                                 outelement3i[1] = remappedelement[2];
793                                 outelement3i[2] = remappedelement[2] + 1;
794                                 outelement3i[3] = remappedelement[1];
795                                 outelement3i[4] = remappedelement[2] + 1;
796                                 outelement3i[5] = remappedelement[1] + 1;
797
798                                 outelement3i += 6;
799                                 outtriangles += 2;
800                         }
801                         if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
802                         {
803                                 remappedelement[0] = vertexremap[element[0]];
804                                 remappedelement[2] = vertexremap[element[2]];
805                                 outelement3i[0] = remappedelement[2];
806                                 outelement3i[1] = remappedelement[0];
807                                 outelement3i[2] = remappedelement[0] + 1;
808                                 outelement3i[3] = remappedelement[2];
809                                 outelement3i[4] = remappedelement[0] + 1;
810                                 outelement3i[5] = remappedelement[2] + 1;
811
812                                 outelement3i += 6;
813                                 outtriangles += 2;
814                         }
815                 }
816         }
817         if (outnumvertices)
818                 *outnumvertices = outvertices;
819         return outtriangles;
820 }
821
822 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, const vec3_t projectdirection, float projectdistance, int nummarktris, const int *marktris)
823 {
824         int tris, outverts;
825         if (projectdistance < 0.1)
826         {
827                 Con_Printf("R_Shadow_Volume: projectdistance %f\n", projectdistance);
828                 return;
829         }
830         if (!numverts || !nummarktris)
831                 return;
832         // make sure shadowelements is big enough for this volume
833         if (maxshadowtriangles < nummarktris || maxshadowvertices < numverts)
834                 R_Shadow_ResizeShadowArrays((numverts + 255) & ~255, (nummarktris + 255) & ~255);
835         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, shadowvertex3f, projectorigin, projectdirection, projectdistance, nummarktris, marktris);
836         r_refdef.stats.lights_dynamicshadowtriangles += tris;
837         R_Shadow_RenderVolume(outverts, tris, shadowvertex3f, shadowelements);
838 }
839
840 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t projectdirection, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
841 {
842         int t, tend;
843         const int *e;
844         const float *v[3];
845         float normal[3];
846         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
847                 return;
848         tend = firsttriangle + numtris;
849         if (BoxInsideBox(surfacemins, surfacemaxs, lightmins, lightmaxs))
850         {
851                 // surface box entirely inside light box, no box cull
852                 if (projectdirection)
853                 {
854                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
855                         {
856                                 TriangleNormal(invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3, normal);
857                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0))
858                                         shadowmarklist[numshadowmark++] = t;
859                         }
860                 }
861                 else
862                 {
863                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
864                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
865                                         shadowmarklist[numshadowmark++] = t;
866                 }
867         }
868         else
869         {
870                 // surface box not entirely inside light box, cull each triangle
871                 if (projectdirection)
872                 {
873                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
874                         {
875                                 v[0] = invertex3f + e[0] * 3;
876                                 v[1] = invertex3f + e[1] * 3;
877                                 v[2] = invertex3f + e[2] * 3;
878                                 TriangleNormal(v[0], v[1], v[2], normal);
879                                 if (r_shadow_frontsidecasting.integer == (DotProduct(normal, projectdirection) < 0)
880                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
881                                         shadowmarklist[numshadowmark++] = t;
882                         }
883                 }
884                 else
885                 {
886                         for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
887                         {
888                                 v[0] = invertex3f + e[0] * 3;
889                                 v[1] = invertex3f + e[1] * 3;
890                                 v[2] = invertex3f + e[2] * 3;
891                                 if (r_shadow_frontsidecasting.integer == PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
892                                  && TriangleOverlapsBox(v[0], v[1], v[2], lightmins, lightmaxs))
893                                         shadowmarklist[numshadowmark++] = t;
894                         }
895                 }
896         }
897 }
898
899 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
900 {
901         if (r_shadow_compilingrtlight)
902         {
903                 // if we're compiling an rtlight, capture the mesh
904                 Mod_ShadowMesh_AddMesh(r_main_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
905                 return;
906         }
907         r_refdef.stats.lights_shadowtriangles += numtriangles;
908         CHECKGLERROR
909         R_Mesh_VertexPointer(vertex3f, 0, 0);
910         GL_LockArrays(0, numvertices);
911         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
912         {
913                 // decrement stencil if backface is behind depthbuffer
914                 GL_CullFace(r_refdef.view.cullface_front);
915                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
916                 R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
917                 // increment stencil if frontface is behind depthbuffer
918                 GL_CullFace(r_refdef.view.cullface_back);
919                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
920         }
921         R_Mesh_Draw(0, numvertices, numtriangles, element3i, 0, 0);
922         GL_LockArrays(0, 0);
923         CHECKGLERROR
924 }
925
926 static void R_Shadow_MakeTextures_MakeCorona(void)
927 {
928         float dx, dy;
929         int x, y, a;
930         unsigned char pixels[32][32][4];
931         for (y = 0;y < 32;y++)
932         {
933                 dy = (y - 15.5f) * (1.0f / 16.0f);
934                 for (x = 0;x < 32;x++)
935                 {
936                         dx = (x - 15.5f) * (1.0f / 16.0f);
937                         a = (int)(((1.0f / (dx * dx + dy * dy + 0.2f)) - (1.0f / (1.0f + 0.2))) * 32.0f / (1.0f / (1.0f + 0.2)));
938                         a = bound(0, a, 255);
939                         pixels[y][x][0] = a;
940                         pixels[y][x][1] = a;
941                         pixels[y][x][2] = a;
942                         pixels[y][x][3] = 255;
943                 }
944         }
945         r_shadow_lightcorona = R_LoadTexture2D(r_shadow_texturepool, "lightcorona", 32, 32, &pixels[0][0][0], TEXTYPE_BGRA, TEXF_PRECACHE, NULL);
946 }
947
948 static unsigned int R_Shadow_MakeTextures_SamplePoint(float x, float y, float z)
949 {
950         float dist = sqrt(x*x+y*y+z*z);
951         float intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
952         // note this code could suffer byte order issues except that it is multiplying by an integer that reads the same both ways
953         return (unsigned char)bound(0, intensity * 256.0f, 255) * 0x01010101;
954 }
955
956 static void R_Shadow_MakeTextures(void)
957 {
958         int x, y, z;
959         float intensity, dist;
960         unsigned int *data;
961         R_FreeTexturePool(&r_shadow_texturepool);
962         r_shadow_texturepool = R_AllocTexturePool();
963         r_shadow_attenlinearscale = r_shadow_lightattenuationlinearscale.value;
964         r_shadow_attendividebias = r_shadow_lightattenuationdividebias.value;
965         data = (unsigned int *)Mem_Alloc(tempmempool, max(max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE, ATTEN2DSIZE*ATTEN2DSIZE), ATTEN1DSIZE) * 4);
966         // the table includes one additional value to avoid the need to clamp indexing due to minor math errors
967         for (x = 0;x <= ATTENTABLESIZE;x++)
968         {
969                 dist = (x + 0.5f) * (1.0f / ATTENTABLESIZE) * (1.0f / 0.9375);
970                 intensity = dist < 1 ? ((1.0f - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist)) : 0;
971                 r_shadow_attentable[x] = bound(0, intensity, 1);
972         }
973         // 1D gradient texture
974         for (x = 0;x < ATTEN1DSIZE;x++)
975                 data[x] = R_Shadow_MakeTextures_SamplePoint((x + 0.5f) * (1.0f / ATTEN1DSIZE) * (1.0f / 0.9375), 0, 0);
976         r_shadow_attenuationgradienttexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation1d", ATTEN1DSIZE, 1, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
977         // 2D circle texture
978         for (y = 0;y < ATTEN2DSIZE;y++)
979                 for (x = 0;x < ATTEN2DSIZE;x++)
980                         data[y*ATTEN2DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375), 0);
981         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
982         // 3D sphere texture
983         if (r_shadow_texture3d.integer && gl_texture3d)
984         {
985                 for (z = 0;z < ATTEN3DSIZE;z++)
986                         for (y = 0;y < ATTEN3DSIZE;y++)
987                                 for (x = 0;x < ATTEN3DSIZE;x++)
988                                         data[(z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x] = R_Shadow_MakeTextures_SamplePoint(((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375), ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375));
989                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, (unsigned char *)data, TEXTYPE_BGRA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
990         }
991         else
992                 r_shadow_attenuation3dtexture = NULL;
993         Mem_Free(data);
994
995         R_Shadow_MakeTextures_MakeCorona();
996
997         // Editor light sprites
998         r_editlights_sprcursor = Draw_CachePic("gfx/editlights/cursor", true);
999         r_editlights_sprlight = Draw_CachePic("gfx/editlights/light", true);
1000         r_editlights_sprnoshadowlight = Draw_CachePic("gfx/editlights/noshadow", true);
1001         r_editlights_sprcubemaplight = Draw_CachePic("gfx/editlights/cubemaplight", true);
1002         r_editlights_sprcubemapnoshadowlight = Draw_CachePic("gfx/editlights/cubemapnoshadowlight", true);
1003         r_editlights_sprselection = Draw_CachePic("gfx/editlights/selection", true);
1004 }
1005
1006 void R_Shadow_ValidateCvars(void)
1007 {
1008         if (r_shadow_texture3d.integer && !gl_texture3d)
1009                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
1010         if (gl_ext_separatestencil.integer && !gl_support_separatestencil)
1011                 Cvar_SetValueQuick(&gl_ext_separatestencil, 0);
1012         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
1013                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
1014 }
1015
1016 void R_Shadow_RenderMode_Begin(void)
1017 {
1018         R_Shadow_ValidateCvars();
1019
1020         if (!r_shadow_attenuation2dtexture
1021          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
1022          || r_shadow_lightattenuationdividebias.value != r_shadow_attendividebias
1023          || r_shadow_lightattenuationlinearscale.value != r_shadow_attenlinearscale)
1024                 R_Shadow_MakeTextures();
1025
1026         CHECKGLERROR
1027         R_Mesh_ColorPointer(NULL, 0, 0);
1028         R_Mesh_ResetTextureState();
1029         GL_BlendFunc(GL_ONE, GL_ZERO);
1030         GL_DepthRange(0, 1);
1031         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);
1032         GL_DepthTest(true);
1033         GL_DepthMask(false);
1034         GL_Color(0, 0, 0, 1);
1035         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1036
1037         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1038
1039         if (gl_ext_separatestencil.integer)
1040                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
1041         else if (gl_ext_stenciltwoside.integer)
1042                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
1043         else
1044                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
1045
1046         if (r_glsl.integer && gl_support_fragment_shader)
1047                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
1048         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
1049                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
1050         else
1051                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
1052 }
1053
1054 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
1055 {
1056         rsurface.rtlight = rtlight;
1057 }
1058
1059 void R_Shadow_RenderMode_Reset(void)
1060 {
1061         CHECKGLERROR
1062         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1063         {
1064                 qglUseProgramObjectARB(0);CHECKGLERROR
1065         }
1066         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1067         {
1068                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1069         }
1070         R_Mesh_ColorPointer(NULL, 0, 0);
1071         R_Mesh_ResetTextureState();
1072         GL_DepthRange(0, 1);
1073         GL_DepthTest(true);
1074         GL_DepthMask(false);
1075         qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1076         GL_PolygonOffset(r_refdef.polygonfactor, r_refdef.polygonoffset);CHECKGLERROR
1077         qglDisable(GL_STENCIL_TEST);CHECKGLERROR
1078         qglStencilMask(~0);CHECKGLERROR
1079         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
1080         qglStencilFunc(GL_ALWAYS, 128, ~0);CHECKGLERROR
1081         GL_CullFace(r_refdef.view.cullface_back);
1082         GL_Color(1, 1, 1, 1);
1083         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
1084         GL_BlendFunc(GL_ONE, GL_ZERO);
1085 }
1086
1087 void R_Shadow_RenderMode_StencilShadowVolumes(qboolean clearstencil)
1088 {
1089         CHECKGLERROR
1090         R_Shadow_RenderMode_Reset();
1091         GL_ColorMask(0, 0, 0, 0);
1092         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1093         qglDepthFunc(GL_LESS);CHECKGLERROR
1094         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1095         r_shadow_rendermode = r_shadow_shadowingrendermode;
1096         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_SEPARATESTENCIL)
1097         {
1098                 GL_CullFace(GL_NONE);
1099                 qglStencilOpSeparate(r_refdef.view.cullface_front, GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1100                 qglStencilOpSeparate(r_refdef.view.cullface_back, GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1101         }
1102         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
1103         {
1104                 GL_CullFace(GL_NONE);
1105                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);CHECKGLERROR
1106                 qglActiveStencilFaceEXT(r_refdef.view.cullface_front);CHECKGLERROR
1107                 qglStencilMask(~0);CHECKGLERROR
1108                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
1109                 qglActiveStencilFaceEXT(r_refdef.view.cullface_back);CHECKGLERROR
1110                 qglStencilMask(~0);CHECKGLERROR
1111                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
1112         }
1113         if (clearstencil)
1114                 GL_Clear(GL_STENCIL_BUFFER_BIT);
1115         r_refdef.stats.lights_clears++;
1116 }
1117
1118 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
1119 {
1120         CHECKGLERROR
1121         R_Shadow_RenderMode_Reset();
1122         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1123         if (!transparent)
1124         {
1125                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1126         }
1127         if (stenciltest)
1128         {
1129                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1130                 // only draw light where this geometry was already rendered AND the
1131                 // stencil is 128 (values other than this mean shadow)
1132                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1133         }
1134         r_shadow_rendermode = r_shadow_lightingrendermode;
1135         // do global setup needed for the chosen lighting mode
1136         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
1137         {
1138                 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
1139                 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
1140                 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
1141                 R_Mesh_TexBindCubeMap(3, R_GetTexture(rsurface.rtlight->currentcubemap)); // light filter
1142                 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
1143                 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
1144                 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
1145                 R_Mesh_TexBind(7, R_GetTexture(r_texture_white)); // lightmap
1146                 R_Mesh_TexBind(8, R_GetTexture(r_texture_blanknormalmap)); // deluxemap
1147                 R_Mesh_TexBind(9, R_GetTexture(r_texture_black)); // glow
1148                 //R_Mesh_TexMatrix(3, rsurface.entitytolight); // light filter matrix
1149                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1150                 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1151                 CHECKGLERROR
1152         }
1153 }
1154
1155 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1156 {
1157         CHECKGLERROR
1158         R_Shadow_RenderMode_Reset();
1159         GL_BlendFunc(GL_ONE, GL_ONE);
1160         GL_DepthRange(0, 1);
1161         GL_DepthTest(r_showshadowvolumes.integer < 2);
1162         GL_Color(0.0, 0.0125 * r_refdef.view.colorscale, 0.1 * r_refdef.view.colorscale, 1);
1163         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1164         GL_CullFace(GL_NONE);
1165         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1166 }
1167
1168 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1169 {
1170         CHECKGLERROR
1171         R_Shadow_RenderMode_Reset();
1172         GL_BlendFunc(GL_ONE, GL_ONE);
1173         GL_DepthRange(0, 1);
1174         GL_DepthTest(r_showlighting.integer < 2);
1175         GL_Color(0.1 * r_refdef.view.colorscale, 0.0125 * r_refdef.view.colorscale, 0, 1);
1176         if (!transparent)
1177         {
1178                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1179         }
1180         if (stenciltest)
1181         {
1182                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1183                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1184         }
1185         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1186 }
1187
1188 void R_Shadow_RenderMode_End(void)
1189 {
1190         CHECKGLERROR
1191         R_Shadow_RenderMode_Reset();
1192         R_Shadow_RenderMode_ActiveLight(NULL);
1193         GL_DepthMask(true);
1194         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1195         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1196 }
1197
1198 int bboxedges[12][2] =
1199 {
1200         // top
1201         {0, 1}, // +X
1202         {0, 2}, // +Y
1203         {1, 3}, // Y, +X
1204         {2, 3}, // X, +Y
1205         // bottom
1206         {4, 5}, // +X
1207         {4, 6}, // +Y
1208         {5, 7}, // Y, +X
1209         {6, 7}, // X, +Y
1210         // verticals
1211         {0, 4}, // +Z
1212         {1, 5}, // X, +Z
1213         {2, 6}, // Y, +Z
1214         {3, 7}, // XY, +Z
1215 };
1216
1217 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1218 {
1219         int i, ix1, iy1, ix2, iy2;
1220         float x1, y1, x2, y2;
1221         vec4_t v, v2;
1222         float vertex[20][3];
1223         int j, k;
1224         vec4_t plane4f;
1225         int numvertices;
1226         float corner[8][4];
1227         float dist[8];
1228         int sign[8];
1229         float f;
1230
1231         if (!r_shadow_scissor.integer)
1232                 return false;
1233
1234         // if view is inside the light box, just say yes it's visible
1235         if (BoxesOverlap(r_refdef.view.origin, r_refdef.view.origin, mins, maxs))
1236         {
1237                 GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1238                 return false;
1239         }
1240
1241         x1 = y1 = x2 = y2 = 0;
1242
1243         // transform all corners that are infront of the nearclip plane
1244         VectorNegate(r_refdef.view.frustum[4].normal, plane4f);
1245         plane4f[3] = r_refdef.view.frustum[4].dist;
1246         numvertices = 0;
1247         for (i = 0;i < 8;i++)
1248         {
1249                 Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1);
1250                 dist[i] = DotProduct4(corner[i], plane4f);
1251                 sign[i] = dist[i] > 0;
1252                 if (!sign[i])
1253                 {
1254                         VectorCopy(corner[i], vertex[numvertices]);
1255                         numvertices++;
1256                 }
1257         }
1258         // if some points are behind the nearclip, add clipped edge points to make
1259         // sure that the scissor boundary is complete
1260         if (numvertices > 0 && numvertices < 8)
1261         {
1262                 // add clipped edge points
1263                 for (i = 0;i < 12;i++)
1264                 {
1265                         j = bboxedges[i][0];
1266                         k = bboxedges[i][1];
1267                         if (sign[j] != sign[k])
1268                         {
1269                                 f = dist[j] / (dist[j] - dist[k]);
1270                                 VectorLerp(corner[j], f, corner[k], vertex[numvertices]);
1271                                 numvertices++;
1272                         }
1273                 }
1274         }
1275
1276         // if we have no points to check, the light is behind the view plane
1277         if (!numvertices)
1278                 return true;
1279
1280         // if we have some points to transform, check what screen area is covered
1281         x1 = y1 = x2 = y2 = 0;
1282         v[3] = 1.0f;
1283         //Con_Printf("%i vertices to transform...\n", numvertices);
1284         for (i = 0;i < numvertices;i++)
1285         {
1286                 VectorCopy(vertex[i], v);
1287                 GL_TransformToScreen(v, v2);
1288                 //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]);
1289                 if (i)
1290                 {
1291                         if (x1 > v2[0]) x1 = v2[0];
1292                         if (x2 < v2[0]) x2 = v2[0];
1293                         if (y1 > v2[1]) y1 = v2[1];
1294                         if (y2 < v2[1]) y2 = v2[1];
1295                 }
1296                 else
1297                 {
1298                         x1 = x2 = v2[0];
1299                         y1 = y2 = v2[1];
1300                 }
1301         }
1302
1303         // now convert the scissor rectangle to integer screen coordinates
1304         ix1 = (int)(x1 - 1.0f);
1305         iy1 = (int)(y1 - 1.0f);
1306         ix2 = (int)(x2 + 1.0f);
1307         iy2 = (int)(y2 + 1.0f);
1308         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1309
1310         // clamp it to the screen
1311         if (ix1 < r_refdef.view.x) ix1 = r_refdef.view.x;
1312         if (iy1 < r_refdef.view.y) iy1 = r_refdef.view.y;
1313         if (ix2 > r_refdef.view.x + r_refdef.view.width) ix2 = r_refdef.view.x + r_refdef.view.width;
1314         if (iy2 > r_refdef.view.y + r_refdef.view.height) iy2 = r_refdef.view.y + r_refdef.view.height;
1315
1316         // if it is inside out, it's not visible
1317         if (ix2 <= ix1 || iy2 <= iy1)
1318                 return true;
1319
1320         // the light area is visible, set up the scissor rectangle
1321         GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1322         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1323         //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1324         r_refdef.stats.lights_scissored++;
1325         return false;
1326 }
1327
1328 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1329 {
1330         float *vertex3f = rsurface.vertex3f + 3 * firstvertex;
1331         float *normal3f = rsurface.normal3f + 3 * firstvertex;
1332         float *color4f = rsurface.array_color4f + 4 * firstvertex;
1333         float dist, dot, distintensity, shadeintensity, v[3], n[3];
1334         if (r_textureunits.integer >= 3)
1335         {
1336                 if (VectorLength2(diffusecolor) > 0)
1337                 {
1338                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1339                         {
1340                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1341                                 Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1342                                 if ((dot = DotProduct(n, v)) < 0)
1343                                 {
1344                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1345                                         VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
1346                                 }
1347                                 else
1348                                         VectorCopy(ambientcolor, color4f);
1349                                 if (r_refdef.fogenabled)
1350                                 {
1351                                         float f;
1352                                         f = FogPoint_Model(vertex3f);
1353                                         VectorScale(color4f, f, color4f);
1354                                 }
1355                                 color4f[3] = 1;
1356                         }
1357                 }
1358                 else
1359                 {
1360                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1361                         {
1362                                 VectorCopy(ambientcolor, color4f);
1363                                 if (r_refdef.fogenabled)
1364                                 {
1365                                         float f;
1366                                         Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1367                                         f = FogPoint_Model(vertex3f);
1368                                         VectorScale(color4f, f, color4f);
1369                                 }
1370                                 color4f[3] = 1;
1371                         }
1372                 }
1373         }
1374         else if (r_textureunits.integer >= 2)
1375         {
1376                 if (VectorLength2(diffusecolor) > 0)
1377                 {
1378                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1379                         {
1380                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1381                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1382                                 {
1383                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1384                                         if ((dot = DotProduct(n, v)) < 0)
1385                                         {
1386                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1387                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1388                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1389                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1390                                         }
1391                                         else
1392                                         {
1393                                                 color4f[0] = ambientcolor[0] * distintensity;
1394                                                 color4f[1] = ambientcolor[1] * distintensity;
1395                                                 color4f[2] = ambientcolor[2] * distintensity;
1396                                         }
1397                                         if (r_refdef.fogenabled)
1398                                         {
1399                                                 float f;
1400                                                 f = FogPoint_Model(vertex3f);
1401                                                 VectorScale(color4f, f, color4f);
1402                                         }
1403                                 }
1404                                 else
1405                                         VectorClear(color4f);
1406                                 color4f[3] = 1;
1407                         }
1408                 }
1409                 else
1410                 {
1411                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1412                         {
1413                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1414                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1415                                 {
1416                                         color4f[0] = ambientcolor[0] * distintensity;
1417                                         color4f[1] = ambientcolor[1] * distintensity;
1418                                         color4f[2] = ambientcolor[2] * distintensity;
1419                                         if (r_refdef.fogenabled)
1420                                         {
1421                                                 float f;
1422                                                 f = FogPoint_Model(vertex3f);
1423                                                 VectorScale(color4f, f, color4f);
1424                                         }
1425                                 }
1426                                 else
1427                                         VectorClear(color4f);
1428                                 color4f[3] = 1;
1429                         }
1430                 }
1431         }
1432         else
1433         {
1434                 if (VectorLength2(diffusecolor) > 0)
1435                 {
1436                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1437                         {
1438                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1439                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1440                                 {
1441                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1442                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1443                                         if ((dot = DotProduct(n, v)) < 0)
1444                                         {
1445                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1446                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1447                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1448                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1449                                         }
1450                                         else
1451                                         {
1452                                                 color4f[0] = ambientcolor[0] * distintensity;
1453                                                 color4f[1] = ambientcolor[1] * distintensity;
1454                                                 color4f[2] = ambientcolor[2] * distintensity;
1455                                         }
1456                                         if (r_refdef.fogenabled)
1457                                         {
1458                                                 float f;
1459                                                 f = FogPoint_Model(vertex3f);
1460                                                 VectorScale(color4f, f, color4f);
1461                                         }
1462                                 }
1463                                 else
1464                                         VectorClear(color4f);
1465                                 color4f[3] = 1;
1466                         }
1467                 }
1468                 else
1469                 {
1470                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1471                         {
1472                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1473                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1474                                 {
1475                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1476                                         color4f[0] = ambientcolor[0] * distintensity;
1477                                         color4f[1] = ambientcolor[1] * distintensity;
1478                                         color4f[2] = ambientcolor[2] * distintensity;
1479                                         if (r_refdef.fogenabled)
1480                                         {
1481                                                 float f;
1482                                                 f = FogPoint_Model(vertex3f);
1483                                                 VectorScale(color4f, f, color4f);
1484                                         }
1485                                 }
1486                                 else
1487                                         VectorClear(color4f);
1488                                 color4f[3] = 1;
1489                         }
1490                 }
1491         }
1492 }
1493
1494 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1495
1496 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1497 {
1498         int i;
1499         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1500         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1501         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1502         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1503         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1504         float lightdir[3];
1505         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1506         {
1507                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1508                 // the cubemap normalizes this for us
1509                 out3f[0] = DotProduct(svector3f, lightdir);
1510                 out3f[1] = DotProduct(tvector3f, lightdir);
1511                 out3f[2] = DotProduct(normal3f, lightdir);
1512         }
1513 }
1514
1515 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1516 {
1517         int i;
1518         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1519         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1520         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1521         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1522         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1523         float lightdir[3], eyedir[3], halfdir[3];
1524         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1525         {
1526                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1527                 VectorNormalize(lightdir);
1528                 VectorSubtract(rsurface.modelorg, vertex3f, eyedir);
1529                 VectorNormalize(eyedir);
1530                 VectorAdd(lightdir, eyedir, halfdir);
1531                 // the cubemap normalizes this for us
1532                 out3f[0] = DotProduct(svector3f, halfdir);
1533                 out3f[1] = DotProduct(tvector3f, halfdir);
1534                 out3f[2] = DotProduct(normal3f, halfdir);
1535         }
1536 }
1537
1538 static void R_Shadow_RenderLighting_VisibleLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1539 {
1540         // used to display how many times a surface is lit for level design purposes
1541         GL_Color(0.1 * r_refdef.view.colorscale, 0.025 * r_refdef.view.colorscale, 0, 1);
1542         R_Mesh_ColorPointer(NULL, 0, 0);
1543         R_Mesh_ResetTextureState();
1544         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1545 }
1546
1547 static void R_Shadow_RenderLighting_Light_GLSL(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
1548 {
1549         // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1550         R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
1551         R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
1552         R_Mesh_TexBind(0, R_GetTexture(rsurface.texture->currentskinframe->nmap));
1553         R_Mesh_TexBind(1, R_GetTexture(rsurface.texture->basetexture));
1554         R_Mesh_TexBind(2, R_GetTexture(rsurface.texture->glosstexture));
1555         R_Mesh_TexBindCubeMap(3, R_GetTexture(rsurface.rtlight->currentcubemap));
1556         R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation));
1557         R_Mesh_TexBind(5, R_GetTexture(rsurface.texture->currentskinframe->pants));
1558         R_Mesh_TexBind(6, R_GetTexture(rsurface.texture->currentskinframe->shirt));
1559         R_Mesh_TexBind(10, R_GetTexture(r_shadow_attenuationgradienttexture));
1560         R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
1561         R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
1562         R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
1563         R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
1564         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1565         {
1566                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1567         }
1568         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1569         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1570         {
1571                 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1572         }
1573 }
1574
1575 static void R_Shadow_RenderLighting_Light_Dot3_Finalize(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, float r, float g, float b)
1576 {
1577         // shared final code for all the dot3 layers
1578         int renders;
1579         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1580         for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1581         {
1582                 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1583                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1584         }
1585 }
1586
1587 static void R_Shadow_RenderLighting_Light_Dot3_AmbientPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1588 {
1589         rmeshstate_t m;
1590         // colorscale accounts for how much we multiply the brightness
1591         // during combine.
1592         //
1593         // mult is how many times the final pass of the lighting will be
1594         // performed to get more brightness than otherwise possible.
1595         //
1596         // Limit mult to 64 for sanity sake.
1597         GL_Color(1,1,1,1);
1598         if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1599         {
1600                 // 3 3D combine path (Geforce3, Radeon 8500)
1601                 memset(&m, 0, sizeof(m));
1602                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1603                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1604                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1605                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1606                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1607                 m.tex[1] = R_GetTexture(basetexture);
1608                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1609                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1610                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1611                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1612                 m.texcubemap[2] = R_GetTexture(rsurface.rtlight->currentcubemap);
1613                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1614                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1615                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1616                 m.texmatrix[2] = rsurface.entitytolight;
1617                 GL_BlendFunc(GL_ONE, GL_ONE);
1618         }
1619         else if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1620         {
1621                 // 2 3D combine path (Geforce3, original Radeon)
1622                 memset(&m, 0, sizeof(m));
1623                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1624                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1625                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1626                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1627                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1628                 m.tex[1] = R_GetTexture(basetexture);
1629                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1630                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1631                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1632                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1633                 GL_BlendFunc(GL_ONE, GL_ONE);
1634         }
1635         else if (r_textureunits.integer >= 4 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1636         {
1637                 // 4 2D combine path (Geforce3, Radeon 8500)
1638                 memset(&m, 0, sizeof(m));
1639                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1640                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1641                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1642                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1643                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1644                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1645                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1646                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1647                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1648                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1649                 m.tex[2] = R_GetTexture(basetexture);
1650                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1651                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1652                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1653                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1654                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1655                 {
1656                         m.texcubemap[3] = R_GetTexture(rsurface.rtlight->currentcubemap);
1657                         m.pointer_texcoord3f[3] = rsurface.vertex3f;
1658                         m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1659                         m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1660                         m.texmatrix[3] = rsurface.entitytolight;
1661                 }
1662                 GL_BlendFunc(GL_ONE, GL_ONE);
1663         }
1664         else if (r_textureunits.integer >= 3 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1665         {
1666                 // 3 2D combine path (Geforce3, original Radeon)
1667                 memset(&m, 0, sizeof(m));
1668                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1669                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1670                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1671                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1672                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1673                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1674                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1675                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1676                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1677                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1678                 m.tex[2] = R_GetTexture(basetexture);
1679                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1680                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1681                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1682                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1683                 GL_BlendFunc(GL_ONE, GL_ONE);
1684         }
1685         else
1686         {
1687                 // 2/2/2 2D combine path (any dot3 card)
1688                 memset(&m, 0, sizeof(m));
1689                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1690                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1691                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1692                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1693                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1694                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1695                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1696                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1697                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1698                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1699                 R_Mesh_TextureState(&m);
1700                 GL_ColorMask(0,0,0,1);
1701                 GL_BlendFunc(GL_ONE, GL_ZERO);
1702                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1703
1704                 // second pass
1705                 memset(&m, 0, sizeof(m));
1706                 m.tex[0] = R_GetTexture(basetexture);
1707                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1708                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1709                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1710                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1711                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1712                 {
1713                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1714                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1715                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1716                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1717                         m.texmatrix[1] = rsurface.entitytolight;
1718                 }
1719                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1720         }
1721         // this final code is shared
1722         R_Mesh_TextureState(&m);
1723         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1724 }
1725
1726 static void R_Shadow_RenderLighting_Light_Dot3_DiffusePass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1727 {
1728         rmeshstate_t m;
1729         // colorscale accounts for how much we multiply the brightness
1730         // during combine.
1731         //
1732         // mult is how many times the final pass of the lighting will be
1733         // performed to get more brightness than otherwise possible.
1734         //
1735         // Limit mult to 64 for sanity sake.
1736         GL_Color(1,1,1,1);
1737         // generate normalization cubemap texcoords
1738         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1739         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1740         {
1741                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1742                 memset(&m, 0, sizeof(m));
1743                 m.tex[0] = R_GetTexture(normalmaptexture);
1744                 m.texcombinergb[0] = GL_REPLACE;
1745                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1746                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1747                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1748                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1749                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1750                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1751                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1752                 m.pointer_texcoord_bufferobject[1] = 0;
1753                 m.pointer_texcoord_bufferoffset[1] = 0;
1754                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1755                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1756                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1757                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1758                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1759                 R_Mesh_TextureState(&m);
1760                 GL_ColorMask(0,0,0,1);
1761                 GL_BlendFunc(GL_ONE, GL_ZERO);
1762                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1763
1764                 // second pass
1765                 memset(&m, 0, sizeof(m));
1766                 m.tex[0] = R_GetTexture(basetexture);
1767                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1768                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1769                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1770                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1771                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1772                 {
1773                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1774                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1775                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1776                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1777                         m.texmatrix[1] = rsurface.entitytolight;
1778                 }
1779                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1780         }
1781         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1782         {
1783                 // 1/2/2 3D combine path (original Radeon)
1784                 memset(&m, 0, sizeof(m));
1785                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1786                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1787                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1788                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1789                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1790                 R_Mesh_TextureState(&m);
1791                 GL_ColorMask(0,0,0,1);
1792                 GL_BlendFunc(GL_ONE, GL_ZERO);
1793                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1794
1795                 // second pass
1796                 memset(&m, 0, sizeof(m));
1797                 m.tex[0] = R_GetTexture(normalmaptexture);
1798                 m.texcombinergb[0] = GL_REPLACE;
1799                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1800                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1801                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1802                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1803                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1804                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1805                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1806                 m.pointer_texcoord_bufferobject[1] = 0;
1807                 m.pointer_texcoord_bufferoffset[1] = 0;
1808                 R_Mesh_TextureState(&m);
1809                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1810                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1811
1812                 // second pass
1813                 memset(&m, 0, sizeof(m));
1814                 m.tex[0] = R_GetTexture(basetexture);
1815                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1816                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1817                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1818                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1819                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1820                 {
1821                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1822                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1823                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1824                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1825                         m.texmatrix[1] = rsurface.entitytolight;
1826                 }
1827                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1828         }
1829         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1830         {
1831                 // 2/2 3D combine path (original Radeon)
1832                 memset(&m, 0, sizeof(m));
1833                 m.tex[0] = R_GetTexture(normalmaptexture);
1834                 m.texcombinergb[0] = GL_REPLACE;
1835                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1836                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1837                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1838                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1839                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1840                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1841                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1842                 m.pointer_texcoord_bufferobject[1] = 0;
1843                 m.pointer_texcoord_bufferoffset[1] = 0;
1844                 R_Mesh_TextureState(&m);
1845                 GL_ColorMask(0,0,0,1);
1846                 GL_BlendFunc(GL_ONE, GL_ZERO);
1847                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1848
1849                 // second pass
1850                 memset(&m, 0, sizeof(m));
1851                 m.tex[0] = R_GetTexture(basetexture);
1852                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1853                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1854                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1855                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1856                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1857                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1858                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1859                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1860                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
1861                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1862         }
1863         else if (r_textureunits.integer >= 4)
1864         {
1865                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1866                 memset(&m, 0, sizeof(m));
1867                 m.tex[0] = R_GetTexture(normalmaptexture);
1868                 m.texcombinergb[0] = GL_REPLACE;
1869                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1870                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1871                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1872                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1873                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1874                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1875                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1876                 m.pointer_texcoord_bufferobject[1] = 0;
1877                 m.pointer_texcoord_bufferoffset[1] = 0;
1878                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1879                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1880                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1881                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1882                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1883                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1884                 m.pointer_texcoord3f[3] = rsurface.vertex3f;
1885                 m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1886                 m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1887                 m.texmatrix[3] = rsurface.entitytoattenuationz;
1888                 R_Mesh_TextureState(&m);
1889                 GL_ColorMask(0,0,0,1);
1890                 GL_BlendFunc(GL_ONE, GL_ZERO);
1891                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1892
1893                 // second pass
1894                 memset(&m, 0, sizeof(m));
1895                 m.tex[0] = R_GetTexture(basetexture);
1896                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1897                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1898                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1899                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1900                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1901                 {
1902                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1903                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1904                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1905                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1906                         m.texmatrix[1] = rsurface.entitytolight;
1907                 }
1908                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1909         }
1910         else
1911         {
1912                 // 2/2/2 2D combine path (any dot3 card)
1913                 memset(&m, 0, sizeof(m));
1914                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1915                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1916                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1917                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1918                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1919                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1920                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1921                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1922                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1923                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1924                 R_Mesh_TextureState(&m);
1925                 GL_ColorMask(0,0,0,1);
1926                 GL_BlendFunc(GL_ONE, GL_ZERO);
1927                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1928
1929                 // second pass
1930                 memset(&m, 0, sizeof(m));
1931                 m.tex[0] = R_GetTexture(normalmaptexture);
1932                 m.texcombinergb[0] = GL_REPLACE;
1933                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1934                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1935                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1936                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1937                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1938                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1939                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1940                 m.pointer_texcoord_bufferobject[1] = 0;
1941                 m.pointer_texcoord_bufferoffset[1] = 0;
1942                 R_Mesh_TextureState(&m);
1943                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1944                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1945
1946                 // second pass
1947                 memset(&m, 0, sizeof(m));
1948                 m.tex[0] = R_GetTexture(basetexture);
1949                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1950                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1951                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1952                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1953                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1954                 {
1955                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1956                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1957                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1958                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1959                         m.texmatrix[1] = rsurface.entitytolight;
1960                 }
1961                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1962         }
1963         // this final code is shared
1964         R_Mesh_TextureState(&m);
1965         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1966 }
1967
1968 static void R_Shadow_RenderLighting_Light_Dot3_SpecularPass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1969 {
1970         float glossexponent;
1971         rmeshstate_t m;
1972         // FIXME: detect blendsquare!
1973         //if (!gl_support_blendsquare)
1974         //      return;
1975         GL_Color(1,1,1,1);
1976         // generate normalization cubemap texcoords
1977         R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1978         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1979         {
1980                 // 2/0/0/1/2 3D combine blendsquare path
1981                 memset(&m, 0, sizeof(m));
1982                 m.tex[0] = R_GetTexture(normalmaptexture);
1983                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1984                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1985                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1986                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1987                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1988                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1989                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1990                 m.pointer_texcoord_bufferobject[1] = 0;
1991                 m.pointer_texcoord_bufferoffset[1] = 0;
1992                 R_Mesh_TextureState(&m);
1993                 GL_ColorMask(0,0,0,1);
1994                 // this squares the result
1995                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1996                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1997
1998                 // second and third pass
1999                 R_Mesh_ResetTextureState();
2000                 // square alpha in framebuffer a few times to make it shiny
2001                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2002                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2003                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2004
2005                 // fourth pass
2006                 memset(&m, 0, sizeof(m));
2007                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
2008                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2009                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2010                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2011                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2012                 R_Mesh_TextureState(&m);
2013                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2014                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2015
2016                 // fifth pass
2017                 memset(&m, 0, sizeof(m));
2018                 m.tex[0] = R_GetTexture(glosstexture);
2019                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2020                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2021                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2022                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2023                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2024                 {
2025                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2026                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2027                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2028                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2029                         m.texmatrix[1] = rsurface.entitytolight;
2030                 }
2031                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2032         }
2033         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2034         {
2035                 // 2/0/0/2 3D combine blendsquare path
2036                 memset(&m, 0, sizeof(m));
2037                 m.tex[0] = R_GetTexture(normalmaptexture);
2038                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2039                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2040                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2041                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2042                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2043                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2044                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2045                 m.pointer_texcoord_bufferobject[1] = 0;
2046                 m.pointer_texcoord_bufferoffset[1] = 0;
2047                 R_Mesh_TextureState(&m);
2048                 GL_ColorMask(0,0,0,1);
2049                 // this squares the result
2050                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2051                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2052
2053                 // second and third pass
2054                 R_Mesh_ResetTextureState();
2055                 // square alpha in framebuffer a few times to make it shiny
2056                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2057                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2058                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2059
2060                 // fourth pass
2061                 memset(&m, 0, sizeof(m));
2062                 m.tex[0] = R_GetTexture(glosstexture);
2063                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2064                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2065                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2066                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2067                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2068                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2069                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2070                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2071                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2072                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2073         }
2074         else
2075         {
2076                 // 2/0/0/2/2 2D combine blendsquare path
2077                 memset(&m, 0, sizeof(m));
2078                 m.tex[0] = R_GetTexture(normalmaptexture);
2079                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2080                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2081                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2082                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2083                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2084                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2085                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2086                 m.pointer_texcoord_bufferobject[1] = 0;
2087                 m.pointer_texcoord_bufferoffset[1] = 0;
2088                 R_Mesh_TextureState(&m);
2089                 GL_ColorMask(0,0,0,1);
2090                 // this squares the result
2091                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2092                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2093
2094                 // second and third pass
2095                 R_Mesh_ResetTextureState();
2096                 // square alpha in framebuffer a few times to make it shiny
2097                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2098                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2099                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2100
2101                 // fourth pass
2102                 memset(&m, 0, sizeof(m));
2103                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2104                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2105                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2106                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2107                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2108                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2109                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2110                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2111                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2112                 m.texmatrix[1] = rsurface.entitytoattenuationz;
2113                 R_Mesh_TextureState(&m);
2114                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2115                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2116
2117                 // fifth pass
2118                 memset(&m, 0, sizeof(m));
2119                 m.tex[0] = R_GetTexture(glosstexture);
2120                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2121                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2122                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2123                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2124                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2125                 {
2126                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2127                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2128                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2129                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2130                         m.texmatrix[1] = rsurface.entitytolight;
2131                 }
2132                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2133         }
2134         // this final code is shared
2135         R_Mesh_TextureState(&m);
2136         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
2137 }
2138
2139 static void R_Shadow_RenderLighting_Light_Dot3(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
2140 {
2141         // ARB path (any Geforce, any Radeon)
2142         qboolean doambient = ambientscale > 0;
2143         qboolean dodiffuse = diffusescale > 0;
2144         qboolean dospecular = specularscale > 0;
2145         if (!doambient && !dodiffuse && !dospecular)
2146                 return;
2147         R_Mesh_ColorPointer(NULL, 0, 0);
2148         if (doambient)
2149                 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, ambientscale * r_refdef.view.colorscale);
2150         if (dodiffuse)
2151                 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2152         if (dopants)
2153         {
2154                 if (doambient)
2155                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, ambientscale * r_refdef.view.colorscale);
2156                 if (dodiffuse)
2157                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2158         }
2159         if (doshirt)
2160         {
2161                 if (doambient)
2162                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, ambientscale * r_refdef.view.colorscale);
2163                 if (dodiffuse)
2164                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2165         }
2166         if (dospecular)
2167                 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_refdef.view.colorscale);
2168 }
2169
2170 void R_Shadow_RenderLighting_Light_Vertex_Pass(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, vec3_t diffusecolor2, vec3_t ambientcolor2)
2171 {
2172         int renders;
2173         int i;
2174         int stop;
2175         int newfirstvertex;
2176         int newlastvertex;
2177         int newnumtriangles;
2178         int *newe;
2179         const int *e;
2180         float *c;
2181         int newelements[4096*3];
2182         R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
2183         for (renders = 0;renders < 64;renders++)
2184         {
2185                 stop = true;
2186                 newfirstvertex = 0;
2187                 newlastvertex = 0;
2188                 newnumtriangles = 0;
2189                 newe = newelements;
2190                 // due to low fillrate on the cards this vertex lighting path is
2191                 // designed for, we manually cull all triangles that do not
2192                 // contain a lit vertex
2193                 // this builds batches of triangles from multiple surfaces and
2194                 // renders them at once
2195                 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
2196                 {
2197                         if (VectorLength2(rsurface.array_color4f + e[0] * 4) + VectorLength2(rsurface.array_color4f + e[1] * 4) + VectorLength2(rsurface.array_color4f + e[2] * 4) >= 0.01)
2198                         {
2199                                 if (newnumtriangles)
2200                                 {
2201                                         newfirstvertex = min(newfirstvertex, e[0]);
2202                                         newlastvertex  = max(newlastvertex, e[0]);
2203                                 }
2204                                 else
2205                                 {
2206                                         newfirstvertex = e[0];
2207                                         newlastvertex = e[0];
2208                                 }
2209                                 newfirstvertex = min(newfirstvertex, e[1]);
2210                                 newlastvertex  = max(newlastvertex, e[1]);
2211                                 newfirstvertex = min(newfirstvertex, e[2]);
2212                                 newlastvertex  = max(newlastvertex, e[2]);
2213                                 newe[0] = e[0];
2214                                 newe[1] = e[1];
2215                                 newe[2] = e[2];
2216                                 newnumtriangles++;
2217                                 newe += 3;
2218                                 if (newnumtriangles >= (int)(sizeof(newelements)/sizeof(float[3])))
2219                                 {
2220                                         R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2221                                         newnumtriangles = 0;
2222                                         newe = newelements;
2223                                         stop = false;
2224                                 }
2225                         }
2226                 }
2227                 if (newnumtriangles >= 1)
2228                 {
2229                         // if all triangles are included, use the original array to take advantage of the bufferobject if possible
2230                         if (newnumtriangles == numtriangles)
2231                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2232                         else
2233                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2234                         stop = false;
2235                 }
2236                 // if we couldn't find any lit triangles, exit early
2237                 if (stop)
2238                         break;
2239                 // now reduce the intensity for the next overbright pass
2240                 // we have to clamp to 0 here incase the drivers have improper
2241                 // handling of negative colors
2242                 // (some old drivers even have improper handling of >1 color)
2243                 stop = true;
2244                 for (i = 0, c = rsurface.array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
2245                 {
2246                         if (c[0] > 1 || c[1] > 1 || c[2] > 1)
2247                         {
2248                                 c[0] = max(0, c[0] - 1);
2249                                 c[1] = max(0, c[1] - 1);
2250                                 c[2] = max(0, c[2] - 1);
2251                                 stop = false;
2252                         }
2253                         else
2254                                 VectorClear(c);
2255                 }
2256                 // another check...
2257                 if (stop)
2258                         break;
2259         }
2260 }
2261
2262 static void R_Shadow_RenderLighting_Light_Vertex(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset, const vec3_t lightcolorbase, const vec3_t lightcolorpants, const vec3_t lightcolorshirt, rtexture_t *basetexture, rtexture_t *pantstexture, rtexture_t *shirttexture, rtexture_t *normalmaptexture, rtexture_t *glosstexture, float ambientscale, float diffusescale, float specularscale, qboolean dopants, qboolean doshirt)
2263 {
2264         // OpenGL 1.1 path (anything)
2265         float ambientcolorbase[3], diffusecolorbase[3];
2266         float ambientcolorpants[3], diffusecolorpants[3];
2267         float ambientcolorshirt[3], diffusecolorshirt[3];
2268         rmeshstate_t m;
2269         VectorScale(lightcolorbase, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorbase);
2270         VectorScale(lightcolorbase, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorbase);
2271         VectorScale(lightcolorpants, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorpants);
2272         VectorScale(lightcolorpants, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorpants);
2273         VectorScale(lightcolorshirt, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorshirt);
2274         VectorScale(lightcolorshirt, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorshirt);
2275         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2276         R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
2277         memset(&m, 0, sizeof(m));
2278         m.tex[0] = R_GetTexture(basetexture);
2279         m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2280         m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2281         m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2282         m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2283         if (r_textureunits.integer >= 2)
2284         {
2285                 // voodoo2 or TNT
2286                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2287                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2288                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2289                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2290                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2291                 if (r_textureunits.integer >= 3)
2292                 {
2293                         // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2294                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2295                         m.texmatrix[2] = rsurface.entitytoattenuationz;
2296                         m.pointer_texcoord3f[2] = rsurface.vertex3f;
2297                         m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
2298                         m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
2299                 }
2300         }
2301         R_Mesh_TextureState(&m);
2302         //R_Mesh_TexBind(0, R_GetTexture(basetexture));
2303         R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorbase, ambientcolorbase);
2304         if (dopants)
2305         {
2306                 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2307                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorpants, ambientcolorpants);
2308         }
2309         if (doshirt)
2310         {
2311                 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2312                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorshirt, ambientcolorshirt);
2313         }
2314 }
2315
2316 extern cvar_t gl_lightmaps;
2317 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset)
2318 {
2319         float ambientscale, diffusescale, specularscale;
2320         vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2321         rtexture_t *nmap;
2322         // calculate colors to render this texture with
2323         lightcolorbase[0] = rsurface.rtlight->currentcolor[0] * rsurface.texture->dlightcolor[0];
2324         lightcolorbase[1] = rsurface.rtlight->currentcolor[1] * rsurface.texture->dlightcolor[1];
2325         lightcolorbase[2] = rsurface.rtlight->currentcolor[2] * rsurface.texture->dlightcolor[2];
2326         ambientscale = rsurface.rtlight->ambientscale;
2327         diffusescale = rsurface.rtlight->diffusescale;
2328         specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
2329         if (!r_shadow_usenormalmap.integer)
2330         {
2331                 ambientscale += 1.0f * diffusescale;
2332                 diffusescale = 0;
2333                 specularscale = 0;
2334         }
2335         if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2336                 return;
2337         RSurf_SetupDepthAndCulling();
2338         nmap = rsurface.texture->currentskinframe->nmap;
2339         if (gl_lightmaps.integer)
2340                 nmap = r_texture_blanknormalmap;
2341         if (rsurface.texture->colormapping && !gl_lightmaps.integer)
2342         {
2343                 qboolean dopants = rsurface.texture->currentskinframe->pants != NULL && VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f);
2344                 qboolean doshirt = rsurface.texture->currentskinframe->shirt != NULL && VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
2345                 if (dopants)
2346                 {
2347                         lightcolorpants[0] = lightcolorbase[0] * rsurface.colormap_pantscolor[0];
2348                         lightcolorpants[1] = lightcolorbase[1] * rsurface.colormap_pantscolor[1];
2349                         lightcolorpants[2] = lightcolorbase[2] * rsurface.colormap_pantscolor[2];
2350                 }
2351                 else
2352                         VectorClear(lightcolorpants);
2353                 if (doshirt)
2354                 {
2355                         lightcolorshirt[0] = lightcolorbase[0] * rsurface.colormap_shirtcolor[0];
2356                         lightcolorshirt[1] = lightcolorbase[1] * rsurface.colormap_shirtcolor[1];
2357                         lightcolorshirt[2] = lightcolorbase[2] * rsurface.colormap_shirtcolor[2];
2358                 }
2359                 else
2360                         VectorClear(lightcolorshirt);
2361                 switch (r_shadow_rendermode)
2362                 {
2363                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2364                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2365                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2366                         break;
2367                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2368                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2369                         break;
2370                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2371                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2372                         break;
2373                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2374                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, lightcolorpants, lightcolorshirt, rsurface.texture->basetexture, rsurface.texture->currentskinframe->pants, rsurface.texture->currentskinframe->shirt, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, dopants, doshirt);
2375                         break;
2376                 default:
2377                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2378                         break;
2379                 }
2380         }
2381         else
2382         {
2383                 switch (r_shadow_rendermode)
2384                 {
2385                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2386                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2387                         R_Shadow_RenderLighting_VisibleLighting(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2388                         break;
2389                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2390                         R_Shadow_RenderLighting_Light_GLSL(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2391                         break;
2392                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2393                         R_Shadow_RenderLighting_Light_Dot3(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2394                         break;
2395                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2396                         R_Shadow_RenderLighting_Light_Vertex(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, vec3_origin, vec3_origin, rsurface.texture->basetexture, r_texture_black, r_texture_black, nmap, rsurface.texture->glosstexture, ambientscale, diffusescale, specularscale, false, false);
2397                         break;
2398                 default:
2399                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2400                         break;
2401                 }
2402         }
2403 }
2404
2405 void R_RTLight_Update(rtlight_t *rtlight, int isstatic, matrix4x4_t *matrix, vec3_t color, int style, const char *cubemapname, qboolean shadow, vec_t corona, vec_t coronasizescale, vec_t ambientscale, vec_t diffusescale, vec_t specularscale, int flags)
2406 {
2407         matrix4x4_t tempmatrix = *matrix;
2408         Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2409
2410         // if this light has been compiled before, free the associated data
2411         R_RTLight_Uncompile(rtlight);
2412
2413         // clear it completely to avoid any lingering data
2414         memset(rtlight, 0, sizeof(*rtlight));
2415
2416         // copy the properties
2417         rtlight->matrix_lighttoworld = tempmatrix;
2418         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2419         Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2420         rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2421         VectorCopy(color, rtlight->color);
2422         rtlight->cubemapname[0] = 0;
2423         if (cubemapname && cubemapname[0])
2424                 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2425         rtlight->shadow = shadow;
2426         rtlight->corona = corona;
2427         rtlight->style = style;
2428         rtlight->isstatic = isstatic;
2429         rtlight->coronasizescale = coronasizescale;
2430         rtlight->ambientscale = ambientscale;
2431         rtlight->diffusescale = diffusescale;
2432         rtlight->specularscale = specularscale;
2433         rtlight->flags = flags;
2434
2435         // compute derived data
2436         //rtlight->cullradius = rtlight->radius;
2437         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2438         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2439         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2440         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2441         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2442         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2443         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2444 }
2445
2446 // compiles rtlight geometry
2447 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2448 void R_RTLight_Compile(rtlight_t *rtlight)
2449 {
2450         int i;
2451         int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2452         int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2453         entity_render_t *ent = r_refdef.worldentity;
2454         model_t *model = r_refdef.worldmodel;
2455         unsigned char *data;
2456
2457         // compile the light
2458         rtlight->compiled = true;
2459         rtlight->static_numleafs = 0;
2460         rtlight->static_numleafpvsbytes = 0;
2461         rtlight->static_leaflist = NULL;
2462         rtlight->static_leafpvs = NULL;
2463         rtlight->static_numsurfaces = 0;
2464         rtlight->static_surfacelist = NULL;
2465         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2466         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2467         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2468         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2469         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2470         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2471
2472         if (model && model->GetLightInfo)
2473         {
2474                 // this variable must be set for the CompileShadowVolume code
2475                 r_shadow_compilingrtlight = rtlight;
2476                 R_Shadow_EnlargeLeafSurfaceTrisBuffer(model->brush.num_leafs, model->num_surfaces, model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles, model->surfmesh.num_triangles);
2477                 model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2478                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2479                 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2480                 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2481                 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2482                 rtlight->static_numsurfaces = numsurfaces;
2483                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2484                 rtlight->static_numleafs = numleafs;
2485                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2486                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2487                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2488                 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2489                 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2490                 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2491                 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2492                 if (rtlight->static_numsurfaces)
2493                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2494                 if (rtlight->static_numleafs)
2495                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2496                 if (rtlight->static_numleafpvsbytes)
2497                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2498                 if (rtlight->static_numshadowtrispvsbytes)
2499                         memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2500                 if (rtlight->static_numlighttrispvsbytes)
2501                         memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2502                 if (model->CompileShadowVolume && rtlight->shadow)
2503                         model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2504                 // now we're done compiling the rtlight
2505                 r_shadow_compilingrtlight = NULL;
2506         }
2507
2508
2509         // use smallest available cullradius - box radius or light radius
2510         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2511         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2512
2513         shadowmeshes = 0;
2514         shadowmeshtris = 0;
2515         if (rtlight->static_meshchain_shadow)
2516         {
2517                 shadowmesh_t *mesh;
2518                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2519                 {
2520                         shadowmeshes++;
2521                         shadowmeshtris += mesh->numtriangles;
2522                 }
2523         }
2524
2525         lighttris = 0;
2526         if (rtlight->static_numlighttrispvsbytes)
2527                 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2528                         if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2529                                 lighttris++;
2530
2531         shadowtris = 0;
2532         if (rtlight->static_numlighttrispvsbytes)
2533                 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2534                         if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2535                                 shadowtris++;
2536
2537         if (developer.integer >= 10)
2538                 Con_Printf("static light built: %f %f %f : %f %f %f box, %i light triangles, %i shadow triangles, %i compiled shadow volume triangles (in %i meshes)\n", rtlight->cullmins[0], rtlight->cullmins[1], rtlight->cullmins[2], rtlight->cullmaxs[0], rtlight->cullmaxs[1], rtlight->cullmaxs[2], lighttris, shadowtris, shadowmeshtris, shadowmeshes);
2539 }
2540
2541 void R_RTLight_Uncompile(rtlight_t *rtlight)
2542 {
2543         if (rtlight->compiled)
2544         {
2545                 if (rtlight->static_meshchain_shadow)
2546                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2547                 rtlight->static_meshchain_shadow = NULL;
2548                 // these allocations are grouped
2549                 if (rtlight->static_surfacelist)
2550                         Mem_Free(rtlight->static_surfacelist);
2551                 rtlight->static_numleafs = 0;
2552                 rtlight->static_numleafpvsbytes = 0;
2553                 rtlight->static_leaflist = NULL;
2554                 rtlight->static_leafpvs = NULL;
2555                 rtlight->static_numsurfaces = 0;
2556                 rtlight->static_surfacelist = NULL;
2557                 rtlight->static_numshadowtrispvsbytes = 0;
2558                 rtlight->static_shadowtrispvs = NULL;
2559                 rtlight->static_numlighttrispvsbytes = 0;
2560                 rtlight->static_lighttrispvs = NULL;
2561                 rtlight->compiled = false;
2562         }
2563 }
2564
2565 void R_Shadow_UncompileWorldLights(void)
2566 {
2567         size_t lightindex;
2568         dlight_t *light;
2569         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
2570         {
2571                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
2572                 if (!light)
2573                         continue;
2574                 R_RTLight_Uncompile(&light->rtlight);
2575         }
2576 }
2577
2578 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
2579 {
2580         int i, j;
2581         mplane_t plane;
2582         // reset the count of frustum planes
2583         // see rsurface.rtlight_frustumplanes definition for how much this array
2584         // can hold
2585         rsurface.rtlight_numfrustumplanes = 0;
2586
2587         // haven't implemented a culling path for ortho rendering
2588         if (!r_refdef.view.useperspective)
2589         {
2590                 // check if the light is on screen and copy the 4 planes if it is
2591                 for (i = 0;i < 4;i++)
2592                         if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2593                                 break;
2594                 if (i == 4)
2595                         for (i = 0;i < 4;i++)
2596                                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2597                 return;
2598         }
2599
2600 #if 1
2601         // generate a deformed frustum that includes the light origin, this is
2602         // used to cull shadow casting surfaces that can not possibly cast a
2603         // shadow onto the visible light-receiving surfaces, which can be a
2604         // performance gain
2605         //
2606         // if the light origin is onscreen the result will be 4 planes exactly
2607         // if the light origin is offscreen on only one axis the result will
2608         // be exactly 5 planes (split-side case)
2609         // if the light origin is offscreen on two axes the result will be
2610         // exactly 4 planes (stretched corner case)
2611         for (i = 0;i < 4;i++)
2612         {
2613                 // quickly reject standard frustum planes that put the light
2614                 // origin outside the frustum
2615                 if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2616                         continue;
2617                 // copy the plane
2618                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2619         }
2620         // if all the standard frustum planes were accepted, the light is onscreen
2621         // otherwise we need to generate some more planes below...
2622         if (rsurface.rtlight_numfrustumplanes < 4)
2623         {
2624                 // at least one of the stock frustum planes failed, so we need to
2625                 // create one or two custom planes to enclose the light origin
2626                 for (i = 0;i < 4;i++)
2627                 {
2628                         // create a plane using the view origin and light origin, and a
2629                         // single point from the frustum corner set
2630                         TriangleNormal(r_refdef.view.origin, r_refdef.view.frustumcorner[i], rtlight->shadoworigin, plane.normal);
2631                         VectorNormalize(plane.normal);
2632                         plane.dist = DotProduct(r_refdef.view.origin, plane.normal);
2633                         // see if this plane is backwards and flip it if so
2634                         for (j = 0;j < 4;j++)
2635                                 if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2636                                         break;
2637                         if (j < 4)
2638                         {
2639                                 VectorNegate(plane.normal, plane.normal);
2640                                 plane.dist *= -1;
2641                                 // flipped plane, test again to see if it is now valid
2642                                 for (j = 0;j < 4;j++)
2643                                         if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2644                                                 break;
2645                                 // if the plane is still not valid, then it is dividing the
2646                                 // frustum and has to be rejected
2647                                 if (j < 4)
2648                                         continue;
2649                         }
2650                         // we have created a valid plane, compute extra info
2651                         PlaneClassify(&plane);
2652                         // copy the plane
2653                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2654 #if 1
2655                         // if we've found 5 frustum planes then we have constructed a
2656                         // proper split-side case and do not need to keep searching for
2657                         // planes to enclose the light origin
2658                         if (rsurface.rtlight_numfrustumplanes == 5)
2659                                 break;
2660 #endif
2661                 }
2662         }
2663 #endif
2664
2665 #if 0
2666         for (i = 0;i < rsurface.rtlight_numfrustumplanes;i++)
2667         {
2668                 plane = rsurface.rtlight_frustumplanes[i];
2669                 Con_Printf("light %p plane #%i %f %f %f : %f (%f %f %f %f %f)\n", rtlight, i, plane.normal[0], plane.normal[1], plane.normal[2], plane.dist, PlaneDiff(r_refdef.view.frustumcorner[0], &plane), PlaneDiff(r_refdef.view.frustumcorner[1], &plane), PlaneDiff(r_refdef.view.frustumcorner[2], &plane), PlaneDiff(r_refdef.view.frustumcorner[3], &plane), PlaneDiff(rtlight->shadoworigin, &plane));
2670         }
2671 #endif
2672
2673 #if 0
2674         // now add the light-space box planes if the light box is rotated, as any
2675         // caster outside the oriented light box is irrelevant (even if it passed
2676         // the worldspace light box, which is axial)
2677         if (rtlight->matrix_lighttoworld.m[0][0] != 1 || rtlight->matrix_lighttoworld.m[1][1] != 1 || rtlight->matrix_lighttoworld.m[2][2] != 1)
2678         {
2679                 for (i = 0;i < 6;i++)
2680                 {
2681                         vec3_t v;
2682                         VectorClear(v);
2683                         v[i >> 1] = (i & 1) ? -1 : 1;
2684                         Matrix4x4_Transform(&rtlight->matrix_lighttoworld, v, plane.normal);
2685                         VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
2686                         plane.dist = VectorNormalizeLength(plane.normal);
2687                         plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
2688                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2689                 }
2690         }
2691 #endif
2692
2693 #if 0
2694         // add the world-space reduced box planes
2695         for (i = 0;i < 6;i++)
2696         {
2697                 VectorClear(plane.normal);
2698                 plane.normal[i >> 1] = (i & 1) ? -1 : 1;
2699                 plane.dist = (i & 1) ? -rsurface.rtlight_cullmaxs[i >> 1] : rsurface.rtlight_cullmins[i >> 1];
2700                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2701         }
2702 #endif
2703
2704 #if 0
2705         {
2706         int j, oldnum;
2707         vec3_t points[8];
2708         vec_t bestdist;
2709         // reduce all plane distances to tightly fit the rtlight cull box, which
2710         // is in worldspace
2711         VectorSet(points[0], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2712         VectorSet(points[1], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2713         VectorSet(points[2], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2714         VectorSet(points[3], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2715         VectorSet(points[4], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2716         VectorSet(points[5], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2717         VectorSet(points[6], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2718         VectorSet(points[7], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2719         oldnum = rsurface.rtlight_numfrustumplanes;
2720         rsurface.rtlight_numfrustumplanes = 0;
2721         for (j = 0;j < oldnum;j++)
2722         {
2723                 // find the nearest point on the box to this plane
2724                 bestdist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[0]);
2725                 for (i = 1;i < 8;i++)
2726                 {
2727                         dist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[i]);
2728                         if (bestdist > dist)
2729                                 bestdist = dist;
2730                 }
2731                 Con_Printf("light %p %splane #%i %f %f %f : %f < %f\n", rtlight, rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125 ? "^2" : "^1", j, rsurface.rtlight_frustumplanes[j].normal[0], rsurface.rtlight_frustumplanes[j].normal[1], rsurface.rtlight_frustumplanes[j].normal[2], rsurface.rtlight_frustumplanes[j].dist, bestdist);
2732                 // if the nearest point is near or behind the plane, we want this
2733                 // plane, otherwise the plane is useless as it won't cull anything
2734                 if (rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125)
2735                 {
2736                         PlaneClassify(&rsurface.rtlight_frustumplanes[j]);
2737                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = rsurface.rtlight_frustumplanes[j];
2738                 }
2739         }
2740         }
2741 #endif
2742 }
2743
2744 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2745 {
2746         RSurf_ActiveWorldEntity();
2747         if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2748         {
2749                 shadowmesh_t *mesh;
2750                 CHECKGLERROR
2751                 for (mesh = rsurface.rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2752                 {
2753                         r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2754                         R_Mesh_VertexPointer(mesh->vertex3f, mesh->vbo, mesh->vbooffset_vertex3f);
2755                         GL_LockArrays(0, mesh->numverts);
2756                         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2757                         {
2758                                 // decrement stencil if backface is behind depthbuffer
2759                                 GL_CullFace(r_refdef.view.cullface_front);
2760                                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2761                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2762                                 // increment stencil if frontface is behind depthbuffer
2763                                 GL_CullFace(r_refdef.view.cullface_back);
2764                                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2765                         }
2766                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2767                         GL_LockArrays(0, 0);
2768                 }
2769                 CHECKGLERROR
2770         }
2771         else if (numsurfaces && r_refdef.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2772         {
2773                 int t, tend;
2774                 int surfacelistindex;
2775                 msurface_t *surface;
2776                 R_Shadow_PrepareShadowMark(r_refdef.worldmodel->brush.shadowmesh->numtriangles);
2777                 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2778                 {
2779                         surface = r_refdef.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2780                         for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2781                                 if (CHECKPVSBIT(trispvs, t))
2782                                         shadowmarklist[numshadowmark++] = t;
2783                 }
2784                 R_Shadow_VolumeFromList(r_refdef.worldmodel->brush.shadowmesh->numverts, r_refdef.worldmodel->brush.shadowmesh->numtriangles, r_refdef.worldmodel->brush.shadowmesh->vertex3f, r_refdef.worldmodel->brush.shadowmesh->element3i, r_refdef.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
2785         }
2786         else if (numsurfaces)
2787                 r_refdef.worldmodel->DrawShadowVolume(r_refdef.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
2788 }
2789
2790 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2791 {
2792         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2793         vec_t relativeshadowradius;
2794         RSurf_ActiveModelEntity(ent, false, false);
2795         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, relativeshadoworigin);
2796         relativeshadowradius = rsurface.rtlight->radius / ent->scale;
2797         relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2798         relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2799         relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2800         relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2801         relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2802         relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2803         ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2804 }
2805
2806 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2807 {
2808         // set up properties for rendering light onto this entity
2809         RSurf_ActiveModelEntity(ent, true, true);
2810         Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
2811         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2812         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2813         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2814         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2815                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2816 }
2817
2818 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2819 {
2820         if (!r_refdef.worldmodel->DrawLight)
2821                 return;
2822
2823         // set up properties for rendering light onto this entity
2824         RSurf_ActiveWorldEntity();
2825         rsurface.entitytolight = rsurface.rtlight->matrix_worldtolight;
2826         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2827         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2828         VectorCopy(rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2829         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2830                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2831
2832         r_refdef.worldmodel->DrawLight(r_refdef.worldentity, numsurfaces, surfacelist, trispvs);
2833 }
2834
2835 void R_Shadow_DrawEntityLight(entity_render_t *ent)
2836 {
2837         model_t *model = ent->model;
2838         if (!model->DrawLight)
2839                 return;
2840
2841         R_Shadow_SetupEntityLight(ent);
2842
2843         model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2844 }
2845
2846 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2847 {
2848         int i;
2849         float f;
2850         int numleafs, numsurfaces;
2851         int *leaflist, *surfacelist;
2852         unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2853         int numlightentities;
2854         int numlightentities_noselfshadow;
2855         int numshadowentities;
2856         int numshadowentities_noselfshadow;
2857         entity_render_t *lightentities[MAX_EDICTS];
2858         entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
2859         entity_render_t *shadowentities[MAX_EDICTS];
2860         entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
2861
2862         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2863         // skip lights that are basically invisible (color 0 0 0)
2864         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2865                 return;
2866
2867         // loading is done before visibility checks because loading should happen
2868         // all at once at the start of a level, not when it stalls gameplay.
2869         // (especially important to benchmarks)
2870         // compile light
2871         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2872                 R_RTLight_Compile(rtlight);
2873         // load cubemap
2874         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2875
2876         // look up the light style value at this time
2877         f = (rtlight->style >= 0 ? r_refdef.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
2878         VectorScale(rtlight->color, f, rtlight->currentcolor);
2879         /*
2880         if (rtlight->selected)
2881         {
2882                 f = 2 + sin(realtime * M_PI * 4.0);
2883                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2884         }
2885         */
2886
2887         // if lightstyle is currently off, don't draw the light
2888         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2889                 return;
2890
2891         // if the light box is offscreen, skip it
2892         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2893                 return;
2894
2895         VectorCopy(rtlight->cullmins, rsurface.rtlight_cullmins);
2896         VectorCopy(rtlight->cullmaxs, rsurface.rtlight_cullmaxs);
2897
2898         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2899         {
2900                 // compiled light, world available and can receive realtime lighting
2901                 // retrieve leaf information
2902                 numleafs = rtlight->static_numleafs;
2903                 leaflist = rtlight->static_leaflist;
2904                 leafpvs = rtlight->static_leafpvs;
2905                 numsurfaces = rtlight->static_numsurfaces;
2906                 surfacelist = rtlight->static_surfacelist;
2907                 shadowtrispvs = rtlight->static_shadowtrispvs;
2908                 lighttrispvs = rtlight->static_lighttrispvs;
2909         }
2910         else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2911         {
2912                 // dynamic light, world available and can receive realtime lighting
2913                 // calculate lit surfaces and leafs
2914                 R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces, r_refdef.worldmodel->brush.shadowmesh ? r_refdef.worldmodel->brush.shadowmesh->numtriangles : r_refdef.worldmodel->surfmesh.num_triangles, r_refdef.worldmodel->surfmesh.num_triangles);
2915                 r_refdef.worldmodel->GetLightInfo(r_refdef.worldentity, rtlight->shadoworigin, rtlight->radius, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs, r_shadow_buffer_leaflist, r_shadow_buffer_leafpvs, &numleafs, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces, r_shadow_buffer_shadowtrispvs, r_shadow_buffer_lighttrispvs);
2916                 leaflist = r_shadow_buffer_leaflist;
2917                 leafpvs = r_shadow_buffer_leafpvs;
2918                 surfacelist = r_shadow_buffer_surfacelist;
2919                 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2920                 lighttrispvs = r_shadow_buffer_lighttrispvs;
2921                 // if the reduced leaf bounds are offscreen, skip it
2922                 if (R_CullBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2923                         return;
2924         }
2925         else
2926         {
2927                 // no world
2928                 numleafs = 0;
2929                 leaflist = NULL;
2930                 leafpvs = NULL;
2931                 numsurfaces = 0;
2932                 surfacelist = NULL;
2933                 shadowtrispvs = NULL;
2934                 lighttrispvs = NULL;
2935         }
2936         // check if light is illuminating any visible leafs
2937         if (numleafs)
2938         {
2939                 for (i = 0;i < numleafs;i++)
2940                         if (r_refdef.viewcache.world_leafvisible[leaflist[i]])
2941                                 break;
2942                 if (i == numleafs)
2943                         return;
2944         }
2945         // set up a scissor rectangle for this light
2946         if (R_Shadow_ScissorForBBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2947                 return;
2948
2949         R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
2950
2951         // make a list of lit entities and shadow casting entities
2952         numlightentities = 0;
2953         numlightentities_noselfshadow = 0;
2954         numshadowentities = 0;
2955         numshadowentities_noselfshadow = 0;
2956         // add dynamic entities that are lit by the light
2957         if (r_drawentities.integer)
2958         {
2959                 for (i = 0;i < r_refdef.numentities;i++)
2960                 {
2961                         model_t *model;
2962                         entity_render_t *ent = r_refdef.entities[i];
2963                         vec3_t org;
2964                         if (!BoxesOverlap(ent->mins, ent->maxs, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2965                                 continue;
2966                         // skip the object entirely if it is not within the valid
2967                         // shadow-casting region (which includes the lit region)
2968                         if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rsurface.rtlight_numfrustumplanes, rsurface.rtlight_frustumplanes))
2969                                 continue;
2970                         if (!(model = ent->model))
2971                                 continue;
2972                         if (r_refdef.viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2973                         {
2974                                 // this entity wants to receive light, is visible, and is
2975                                 // inside the light box
2976                                 // TODO: check if the surfaces in the model can receive light
2977                                 // so now check if it's in a leaf seen by the light
2978                                 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
2979                                         continue;
2980                                 if (ent->flags & RENDER_NOSELFSHADOW)
2981                                         lightentities_noselfshadow[numlightentities_noselfshadow++] = ent;
2982                                 else
2983                                         lightentities[numlightentities++] = ent;
2984                                 // since it is lit, it probably also casts a shadow...
2985                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2986                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2987                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2988                                 {
2989                                         // note: exterior models without the RENDER_NOSELFSHADOW
2990                                         // flag still create a RENDER_NOSELFSHADOW shadow but
2991                                         // are lit normally, this means that they are
2992                                         // self-shadowing but do not shadow other
2993                                         // RENDER_NOSELFSHADOW entities such as the gun
2994                                         // (very weird, but keeps the player shadow off the gun)
2995                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
2996                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
2997                                         else
2998                                                 shadowentities[numshadowentities++] = ent;
2999                                 }
3000                         }
3001                         else if (ent->flags & RENDER_SHADOW)
3002                         {
3003                                 // this entity is not receiving light, but may still need to
3004                                 // cast a shadow...
3005                                 // TODO: check if the surfaces in the model can cast shadow
3006                                 // now check if it is in a leaf seen by the light
3007                                 if (r_refdef.worldmodel && r_refdef.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs))
3008                                         continue;
3009                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
3010                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
3011                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
3012                                 {
3013                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
3014                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
3015                                         else
3016                                                 shadowentities[numshadowentities++] = ent;
3017                                 }
3018                         }
3019                 }
3020         }
3021
3022         // return if there's nothing at all to light
3023         if (!numlightentities && !numsurfaces)
3024                 return;
3025
3026         // don't let sound skip if going slow
3027         if (r_refdef.extraupdate)
3028                 S_ExtraUpdate ();
3029
3030         // make this the active rtlight for rendering purposes
3031         R_Shadow_RenderMode_ActiveLight(rtlight);
3032         // count this light in the r_speeds
3033         r_refdef.stats.lights++;
3034
3035         if (r_showshadowvolumes.integer && r_refdef.view.showdebug && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3036         {
3037                 // optionally draw visible shape of the shadow volumes
3038                 // for performance analysis by level designers
3039                 R_Shadow_RenderMode_VisibleShadowVolumes();
3040                 if (numsurfaces)
3041                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3042                 for (i = 0;i < numshadowentities;i++)
3043                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3044                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3045                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3046         }
3047
3048         if (gl_stencil && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3049         {
3050                 // draw stencil shadow volumes to mask off pixels that are in shadow
3051                 // so that they won't receive lighting
3052                 R_Shadow_RenderMode_StencilShadowVolumes(true);
3053                 if (numsurfaces)
3054                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3055                 for (i = 0;i < numshadowentities;i++)
3056                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3057                 if (numlightentities_noselfshadow)
3058                 {
3059                         // draw lighting in the unmasked areas
3060                         R_Shadow_RenderMode_Lighting(true, false);
3061                         for (i = 0;i < numlightentities_noselfshadow;i++)
3062                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3063
3064                         // optionally draw the illuminated areas
3065                         // for performance analysis by level designers
3066                         if (r_showlighting.integer && r_refdef.view.showdebug)
3067                         {
3068                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3069                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3070                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3071                         }
3072
3073                         R_Shadow_RenderMode_StencilShadowVolumes(false);
3074                 }
3075                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3076                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3077
3078                 if (numsurfaces + numlightentities)
3079                 {
3080                         // draw lighting in the unmasked areas
3081                         R_Shadow_RenderMode_Lighting(true, false);
3082                         if (numsurfaces)
3083                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3084                         for (i = 0;i < numlightentities;i++)
3085                                 R_Shadow_DrawEntityLight(lightentities[i]);
3086
3087                         // optionally draw the illuminated areas
3088                         // for performance analysis by level designers
3089                         if (r_showlighting.integer && r_refdef.view.showdebug)
3090                         {
3091                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3092                                 if (numsurfaces)
3093                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3094                                 for (i = 0;i < numlightentities;i++)
3095                                         R_Shadow_DrawEntityLight(lightentities[i]);
3096                         }
3097                 }
3098         }
3099         else
3100         {
3101                 if (numsurfaces + numlightentities)
3102                 {
3103                         // draw lighting in the unmasked areas
3104                         R_Shadow_RenderMode_Lighting(false, false);
3105                         if (numsurfaces)
3106                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3107                         for (i = 0;i < numlightentities;i++)
3108                                 R_Shadow_DrawEntityLight(lightentities[i]);
3109                         for (i = 0;i < numlightentities_noselfshadow;i++)
3110                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3111
3112                         // optionally draw the illuminated areas
3113                         // for performance analysis by level designers
3114                         if (r_showlighting.integer && r_refdef.view.showdebug)
3115                         {
3116                                 R_Shadow_RenderMode_VisibleLighting(false, false);
3117                                 if (numsurfaces)
3118                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3119                                 for (i = 0;i < numlightentities;i++)
3120                                         R_Shadow_DrawEntityLight(lightentities[i]);
3121                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3122                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3123                         }
3124                 }
3125         }
3126 }
3127
3128 void R_Shadow_DrawLightSprites(void);
3129 void R_ShadowVolumeLighting(qboolean visible)
3130 {
3131         int flag;
3132         int lnum;
3133         size_t lightindex;
3134         dlight_t *light;
3135
3136         if (r_editlights.integer)
3137                 R_Shadow_DrawLightSprites();
3138
3139         R_Shadow_RenderMode_Begin();
3140
3141         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3142         if (r_shadow_debuglight.integer >= 0)
3143         {
3144                 lightindex = r_shadow_debuglight.integer;
3145                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3146                 if (light && (light->flags & flag))
3147                         R_DrawRTLight(&light->rtlight, visible);
3148         }
3149         else
3150         {
3151                 for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3152                 {
3153                         light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3154                         if (light && (light->flags & flag))
3155                                 R_DrawRTLight(&light->rtlight, visible);
3156                 }
3157         }
3158         if (r_refdef.rtdlight)
3159                 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
3160                         R_DrawRTLight(&r_refdef.lights[lnum], visible);
3161
3162         R_Shadow_RenderMode_End();
3163 }
3164
3165 extern void R_SetupView(void);
3166 extern cvar_t r_shadows_throwdistance;
3167 void R_DrawModelShadows(void)
3168 {
3169         int i;
3170         float relativethrowdistance;
3171         entity_render_t *ent;
3172         vec3_t relativelightorigin;
3173         vec3_t relativelightdirection;
3174         vec3_t relativeshadowmins, relativeshadowmaxs;
3175         float vertex3f[12];
3176
3177         if (!r_drawentities.integer || !gl_stencil)
3178                 return;
3179
3180         CHECKGLERROR
3181         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3182
3183         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
3184
3185         if (gl_ext_separatestencil.integer)
3186                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
3187         else if (gl_ext_stenciltwoside.integer)
3188                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
3189         else
3190                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
3191
3192         R_Shadow_RenderMode_StencilShadowVolumes(true);
3193
3194         for (i = 0;i < r_refdef.numentities;i++)
3195         {
3196                 ent = r_refdef.entities[i];
3197                 // cast shadows from anything that is not a submodel of the map
3198                 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
3199                 {
3200                         relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
3201                         VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
3202                         VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
3203                         VectorNegate(ent->modellight_lightdir, relativelightdirection);
3204                         VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
3205                         RSurf_ActiveModelEntity(ent, false, false);
3206                         ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
3207                 }
3208         }
3209
3210         // not really the right mode, but this will disable any silly stencil features
3211         R_Shadow_RenderMode_VisibleLighting(true, true);
3212
3213         // vertex coordinates for a quad that covers the screen exactly
3214         vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
3215         vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
3216         vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
3217         vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
3218
3219         // set up ortho view for rendering this pass
3220         GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
3221         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3222         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3223         GL_ScissorTest(true);
3224         R_Mesh_Matrix(&identitymatrix);
3225         R_Mesh_ResetTextureState();
3226         R_Mesh_VertexPointer(vertex3f, 0, 0);
3227         R_Mesh_ColorPointer(NULL, 0, 0);
3228
3229         // set up a 50% darkening blend on shadowed areas
3230         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3231         GL_DepthRange(0, 1);
3232         GL_DepthTest(false);
3233         GL_DepthMask(false);
3234         GL_PolygonOffset(0, 0);CHECKGLERROR
3235         GL_Color(0, 0, 0, 0.5);
3236         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3237         qglDepthFunc(GL_ALWAYS);CHECKGLERROR
3238         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
3239         qglStencilMask(~0);CHECKGLERROR
3240         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
3241         qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
3242
3243         // apply the blend to the shadowed areas
3244         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
3245
3246         // restoring the perspective view is done by R_RenderScene
3247         //R_SetupView();
3248
3249         // restore other state to normal
3250         R_Shadow_RenderMode_End();
3251 }
3252
3253 void R_DrawCoronas(void)
3254 {
3255         int i, flag;
3256         float cscale, scale;
3257         size_t lightindex;
3258         dlight_t *light;
3259         rtlight_t *rtlight;
3260         if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
3261                 return;
3262         R_Mesh_Matrix(&identitymatrix);
3263         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3264         // FIXME: these traces should scan all render entities instead of cl.world
3265         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3266         {
3267                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3268                 if (!light)
3269                         continue;
3270                 rtlight = &light->rtlight;
3271                 if (!(rtlight->flags & flag))
3272                         continue;
3273                 if (rtlight->corona * r_coronas.value <= 0)
3274                         continue;
3275                 if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex)
3276                         continue;
3277                 cscale = rtlight->corona * r_coronas.value* 0.25f;
3278                 scale = rtlight->radius * rtlight->coronasizescale;
3279                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 16.0f * 16.0f)
3280                         continue;
3281                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3282                         continue;
3283                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3284         }
3285         for (i = 0;i < r_refdef.numlights;i++)
3286         {
3287                 rtlight = &r_refdef.lights[i];
3288                 if (!(rtlight->flags & flag))
3289                         continue;
3290                 if (rtlight->corona <= 0)
3291                         continue;
3292                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f)
3293                         continue;
3294                 if (gl_flashblend.integer)
3295                 {
3296                         cscale = rtlight->corona * 1.0f;
3297                         scale = rtlight->radius * rtlight->coronasizescale * 2.0f;
3298                 }
3299                 else
3300                 {
3301                         cscale = rtlight->corona * r_coronas.value* 0.25f;
3302                         scale = rtlight->radius * rtlight->coronasizescale;
3303                 }
3304                 if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f))
3305                         continue;
3306                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3307                         continue;
3308                 R_DrawSprite(GL_ONE, GL_ONE, r_shadow_lightcorona, NULL, true, false, rtlight->shadoworigin, r_refdef.view.right, r_refdef.view.up, scale, -scale, -scale, scale, rtlight->color[0] * cscale, rtlight->color[1] * cscale, rtlight->color[2] * cscale, 1);
3309         }
3310 }
3311
3312
3313
3314 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
3315 typedef struct suffixinfo_s
3316 {
3317         char *suffix;
3318         qboolean flipx, flipy, flipdiagonal;
3319 }
3320 suffixinfo_t;
3321 static suffixinfo_t suffix[3][6] =
3322 {
3323         {
3324                 {"px",   false, false, false},
3325                 {"nx",   false, false, false},
3326                 {"py",   false, false, false},
3327                 {"ny",   false, false, false},
3328                 {"pz",   false, false, false},
3329                 {"nz",   false, false, false}
3330         },
3331         {
3332                 {"posx", false, false, false},
3333                 {"negx", false, false, false},
3334                 {"posy", false, false, false},
3335                 {"negy", false, false, false},
3336                 {"posz", false, false, false},
3337                 {"negz", false, false, false}
3338         },
3339         {
3340                 {"rt",    true, false,  true},
3341                 {"lf",   false,  true,  true},
3342                 {"ft",    true,  true, false},
3343                 {"bk",   false, false, false},
3344                 {"up",    true, false,  true},
3345                 {"dn",    true, false,  true}
3346         }
3347 };
3348
3349 static int componentorder[4] = {0, 1, 2, 3};
3350
3351 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
3352 {
3353         int i, j, cubemapsize;
3354         unsigned char *cubemappixels, *image_buffer;
3355         rtexture_t *cubemaptexture;
3356         char name[256];
3357         // must start 0 so the first loadimagepixels has no requested width/height
3358         cubemapsize = 0;
3359         cubemappixels = NULL;
3360         cubemaptexture = NULL;
3361         // keep trying different suffix groups (posx, px, rt) until one loads
3362         for (j = 0;j < 3 && !cubemappixels;j++)
3363         {
3364                 // load the 6 images in the suffix group
3365                 for (i = 0;i < 6;i++)
3366                 {
3367                         // generate an image name based on the base and and suffix
3368                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3369                         // load it
3370                         if ((image_buffer = loadimagepixelsbgra(name, false, false)))
3371                         {
3372                                 // an image loaded, make sure width and height are equal
3373                                 if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
3374                                 {
3375                                         // if this is the first image to load successfully, allocate the cubemap memory
3376                                         if (!cubemappixels && image_width >= 1)
3377                                         {
3378                                                 cubemapsize = image_width;
3379                                                 // note this clears to black, so unavailable sides are black
3380                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3381                                         }
3382                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3383                                         if (cubemappixels)
3384                                                 Image_CopyMux(cubemappixels+i*cubemapsize*cubemapsize*4, image_buffer, cubemapsize, cubemapsize, suffix[j][i].flipx, suffix[j][i].flipy, suffix[j][i].flipdiagonal, 4, 4, componentorder);
3385                                 }
3386                                 else
3387                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3388                                 // free the image
3389                                 Mem_Free(image_buffer);
3390                         }
3391                 }
3392         }
3393         // if a cubemap loaded, upload it
3394         if (cubemappixels)
3395         {
3396                 if (!r_shadow_filters_texturepool)
3397                         r_shadow_filters_texturepool = R_AllocTexturePool();
3398                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
3399                 Mem_Free(cubemappixels);
3400         }
3401         else
3402         {
3403                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3404                 for (j = 0;j < 3;j++)
3405                         for (i = 0;i < 6;i++)
3406                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3407                 Con_Print(" and was unable to find any of them.\n");
3408         }
3409         return cubemaptexture;
3410 }
3411
3412 rtexture_t *R_Shadow_Cubemap(const char *basename)
3413 {
3414         int i;
3415         for (i = 0;i < numcubemaps;i++)
3416                 if (!strcasecmp(cubemaps[i].basename, basename))
3417                         return cubemaps[i].texture;
3418         if (i >= MAX_CUBEMAPS)
3419                 return r_texture_whitecube;
3420         numcubemaps++;
3421         strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
3422         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3423         if (!cubemaps[i].texture)
3424                 cubemaps[i].texture = r_texture_whitecube;
3425         return cubemaps[i].texture;
3426 }
3427
3428 void R_Shadow_FreeCubemaps(void)
3429 {
3430         numcubemaps = 0;
3431         R_FreeTexturePool(&r_shadow_filters_texturepool);
3432 }
3433
3434 dlight_t *R_Shadow_NewWorldLight(void)
3435 {
3436         return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
3437 }
3438
3439 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)
3440 {
3441         matrix4x4_t matrix;
3442         // validate parameters
3443         if (style < 0 || style >= MAX_LIGHTSTYLES)
3444         {
3445                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3446                 style = 0;
3447         }
3448         if (!cubemapname)
3449                 cubemapname = "";
3450
3451         // copy to light properties
3452         VectorCopy(origin, light->origin);
3453         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3454         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3455         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3456         light->color[0] = max(color[0], 0);
3457         light->color[1] = max(color[1], 0);
3458         light->color[2] = max(color[2], 0);
3459         light->radius = max(radius, 0);
3460         light->style = style;
3461         light->shadow = shadowenable;
3462         light->corona = corona;
3463         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3464         light->coronasizescale = coronasizescale;
3465         light->ambientscale = ambientscale;
3466         light->diffusescale = diffusescale;
3467         light->specularscale = specularscale;
3468         light->flags = flags;
3469
3470         // update renderable light data
3471         Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
3472         R_RTLight_Update(&light->rtlight, true, &matrix, light->color, light->style, light->cubemapname[0] ? light->cubemapname : NULL, light->shadow, light->corona, light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
3473 }
3474
3475 void R_Shadow_FreeWorldLight(dlight_t *light)
3476 {
3477         if (r_shadow_selectedlight == light)
3478                 r_shadow_selectedlight = NULL;
3479         R_RTLight_Uncompile(&light->rtlight);
3480         Mem_ExpandableArray_FreeRecord(&r_shadow_worldlightsarray, light);
3481 }
3482
3483 void R_Shadow_ClearWorldLights(void)
3484 {
3485         size_t lightindex;
3486         dlight_t *light;
3487         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3488         {
3489                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3490                 if (light)
3491                         R_Shadow_FreeWorldLight(light);
3492         }
3493         r_shadow_selectedlight = NULL;
3494         R_Shadow_FreeCubemaps();
3495 }
3496
3497 void R_Shadow_SelectLight(dlight_t *light)
3498 {
3499         if (r_shadow_selectedlight)
3500                 r_shadow_selectedlight->selected = false;
3501         r_shadow_selectedlight = light;
3502         if (r_shadow_selectedlight)
3503                 r_shadow_selectedlight->selected = true;
3504 }
3505
3506 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3507 {
3508         // this is never batched (there can be only one)
3509         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprcursor->tex, r_editlights_sprcursor->tex, false, false, r_editlights_cursorlocation, r_refdef.view.right, r_refdef.view.up, EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, -EDLIGHTSPRSIZE, EDLIGHTSPRSIZE, 1, 1, 1, 1);
3510 }
3511
3512 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3513 {
3514         float intensity;
3515         float s;
3516         vec3_t spritecolor;
3517         cachepic_t *pic;
3518
3519         // this is never batched (due to the ent parameter changing every time)
3520         // so numsurfaces == 1 and surfacelist[0] == lightnumber
3521         const dlight_t *light = (dlight_t *)ent;
3522         s = EDLIGHTSPRSIZE;
3523         intensity = 0.5f;
3524         VectorScale(light->color, intensity, spritecolor);
3525         if (VectorLength(spritecolor) < 0.1732f)
3526                 VectorSet(spritecolor, 0.1f, 0.1f, 0.1f);
3527         if (VectorLength(spritecolor) > 1.0f)
3528                 VectorNormalize(spritecolor);
3529
3530         // draw light sprite
3531         if (light->cubemapname[0] && !light->shadow)
3532                 pic = r_editlights_sprcubemapnoshadowlight;
3533         else if (light->cubemapname[0])
3534                 pic = r_editlights_sprcubemaplight;
3535         else if (!light->shadow)
3536                 pic = r_editlights_sprnoshadowlight;
3537         else
3538                 pic = r_editlights_sprlight;
3539         R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, pic->tex, pic->tex, false, false, light->origin, r_refdef.view.right, r_refdef.view.up, s, -s, -s, s, spritecolor[0], spritecolor[1], spritecolor[2], 1);
3540         // draw selection sprite if light is selected
3541         if (light->selected)
3542                 R_DrawSprite(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, r_editlights_sprselection->tex, r_editlights_sprselection->tex, false, false, light->origin, r_refdef.view.right, r_refdef.view.up, s, -s, -s, s, 1, 1, 1, 1);
3543         // VorteX todo: add normalmode/realtime mode light overlay sprites?
3544 }
3545
3546 void R_Shadow_DrawLightSprites(void)
3547 {
3548         size_t lightindex;
3549         dlight_t *light;
3550         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3551         {
3552                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3553                 if (light)
3554                         R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
3555         }
3556         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
3557 }
3558
3559 void R_Shadow_SelectLightInView(void)
3560 {
3561         float bestrating, rating, temp[3];
3562         dlight_t *best;
3563         size_t lightindex;
3564         dlight_t *light;
3565         best = NULL;
3566         bestrating = 0;
3567         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3568         {
3569                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3570                 if (!light)
3571                         continue;
3572                 VectorSubtract(light->origin, r_refdef.view.origin, temp);
3573                 rating = (DotProduct(temp, r_refdef.view.forward) / sqrt(DotProduct(temp, temp)));
3574                 if (rating >= 0.95)
3575                 {
3576                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3577                         if (bestrating < rating && CL_Move(light->origin, vec3_origin, vec3_origin, r_refdef.view.origin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1.0f)
3578                         {
3579                                 bestrating = rating;
3580                                 best = light;
3581                         }
3582                 }
3583         }
3584         R_Shadow_SelectLight(best);
3585 }
3586
3587 void R_Shadow_LoadWorldLights(void)
3588 {
3589         int n, a, style, shadow, flags;
3590         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3591         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3592         if (r_refdef.worldmodel == NULL)
3593         {
3594                 Con_Print("No map loaded.\n");
3595                 return;
3596         }
3597         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3598         strlcat (name, ".rtlights", sizeof (name));
3599         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3600         if (lightsstring)
3601         {
3602                 s = lightsstring;
3603                 n = 0;
3604                 while (*s)
3605                 {
3606                         t = s;
3607                         /*
3608                         shadow = true;
3609                         for (;COM_Parse(t, true) && strcmp(
3610                         if (COM_Parse(t, true))
3611                         {
3612                                 if (com_token[0] == '!')
3613                                 {
3614                                         shadow = false;
3615                                         origin[0] = atof(com_token+1);
3616                                 }
3617                                 else
3618                                         origin[0] = atof(com_token);
3619                                 if (Com_Parse(t
3620                         }
3621                         */
3622                         t = s;
3623                         while (*s && *s != '\n' && *s != '\r')
3624                                 s++;
3625                         if (!*s)
3626                                 break;
3627                         tempchar = *s;
3628                         shadow = true;
3629                         // check for modifier flags
3630                         if (*t == '!')
3631                         {
3632                                 shadow = false;
3633                                 t++;
3634                         }
3635                         *s = 0;
3636                         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);
3637                         *s = tempchar;
3638                         if (a < 18)
3639                                 flags = LIGHTFLAG_REALTIMEMODE;
3640                         if (a < 17)
3641                                 specularscale = 1;
3642                         if (a < 16)
3643                                 diffusescale = 1;
3644                         if (a < 15)
3645                                 ambientscale = 0;
3646                         if (a < 14)
3647                                 coronasizescale = 0.25f;
3648                         if (a < 13)
3649                                 VectorClear(angles);
3650                         if (a < 10)
3651                                 corona = 0;
3652                         if (a < 9 || !strcmp(cubemapname, "\"\""))
3653                                 cubemapname[0] = 0;
3654                         // remove quotes on cubemapname
3655                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3656                         {
3657                                 size_t namelen;
3658                                 namelen = strlen(cubemapname) - 2;
3659                                 memmove(cubemapname, cubemapname + 1, namelen);
3660                                 cubemapname[namelen] = '\0';
3661                         }
3662                         if (a < 8)
3663                         {
3664                                 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);
3665                                 break;
3666                         }
3667                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3668                         if (*s == '\r')
3669                                 s++;
3670                         if (*s == '\n')
3671                                 s++;
3672                         n++;
3673                 }
3674                 if (*s)
3675                         Con_Printf("invalid rtlights file \"%s\"\n", name);
3676                 Mem_Free(lightsstring);
3677         }
3678 }
3679
3680 void R_Shadow_SaveWorldLights(void)
3681 {
3682         size_t lightindex;
3683         dlight_t *light;
3684         size_t bufchars, bufmaxchars;
3685         char *buf, *oldbuf;
3686         char name[MAX_QPATH];
3687         char line[MAX_INPUTLINE];
3688         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
3689                 return;
3690         if (r_refdef.worldmodel == NULL)
3691         {
3692                 Con_Print("No map loaded.\n");
3693                 return;
3694         }
3695         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3696         strlcat (name, ".rtlights", sizeof (name));
3697         bufchars = bufmaxchars = 0;
3698         buf = NULL;
3699         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3700         {
3701                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3702                 if (!light)
3703                         continue;
3704                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3705                         sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f %f %f %f %f %i\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2], light->coronasizescale, light->ambientscale, light->diffusescale, light->specularscale, light->flags);
3706                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3707                         sprintf(line, "%s%f %f %f %f %f %f %f %d \"%s\" %f %f %f %f\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style, light->cubemapname, light->corona, light->angles[0], light->angles[1], light->angles[2]);
3708                 else
3709                         sprintf(line, "%s%f %f %f %f %f %f %f %d\n", light->shadow ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->radius, light->color[0], light->color[1], light->color[2], light->style);
3710                 if (bufchars + strlen(line) > bufmaxchars)
3711                 {
3712                         bufmaxchars = bufchars + strlen(line) + 2048;
3713                         oldbuf = buf;
3714                         buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3715                         if (oldbuf)
3716                         {
3717                                 if (bufchars)
3718                                         memcpy(buf, oldbuf, bufchars);
3719                                 Mem_Free(oldbuf);
3720                         }
3721                 }
3722                 if (strlen(line))
3723                 {
3724                         memcpy(buf + bufchars, line, strlen(line));
3725                         bufchars += strlen(line);
3726                 }
3727         }
3728         if (bufchars)
3729                 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3730         if (buf)
3731                 Mem_Free(buf);
3732 }
3733
3734 void R_Shadow_LoadLightsFile(void)
3735 {
3736         int n, a, style;
3737         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3738         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3739         if (r_refdef.worldmodel == NULL)
3740         {
3741                 Con_Print("No map loaded.\n");
3742                 return;
3743         }
3744         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3745         strlcat (name, ".lights", sizeof (name));
3746         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3747         if (lightsstring)
3748         {
3749                 s = lightsstring;
3750                 n = 0;
3751                 while (*s)
3752                 {
3753                         t = s;
3754                         while (*s && *s != '\n' && *s != '\r')
3755                                 s++;
3756                         if (!*s)
3757                                 break;
3758                         tempchar = *s;
3759                         *s = 0;
3760                         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);
3761                         *s = tempchar;
3762                         if (a < 14)
3763                         {
3764                                 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);
3765                                 break;
3766                         }
3767                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3768                         radius = bound(15, radius, 4096);
3769                         VectorScale(color, (2.0f / (8388608.0f)), color);
3770                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3771                         if (*s == '\r')
3772                                 s++;
3773                         if (*s == '\n')
3774                                 s++;
3775                         n++;
3776                 }
3777                 if (*s)
3778                         Con_Printf("invalid lights file \"%s\"\n", name);
3779                 Mem_Free(lightsstring);
3780         }
3781 }
3782
3783 // tyrlite/hmap2 light types in the delay field
3784 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3785
3786 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3787 {
3788         int entnum, style, islight, skin, pflags, effects, type, n;
3789         char *entfiledata;
3790         const char *data;
3791         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3792         char key[256], value[MAX_INPUTLINE];
3793
3794         if (r_refdef.worldmodel == NULL)
3795         {
3796                 Con_Print("No map loaded.\n");
3797                 return;
3798         }
3799         // try to load a .ent file first
3800         FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3801         strlcat (key, ".ent", sizeof (key));
3802         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3803         // and if that is not found, fall back to the bsp file entity string
3804         if (!data)
3805                 data = r_refdef.worldmodel->brush.entities;
3806         if (!data)
3807                 return;
3808         for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
3809         {
3810                 type = LIGHTTYPE_MINUSX;
3811                 origin[0] = origin[1] = origin[2] = 0;
3812                 originhack[0] = originhack[1] = originhack[2] = 0;
3813                 angles[0] = angles[1] = angles[2] = 0;
3814                 color[0] = color[1] = color[2] = 1;
3815                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3816                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3817                 fadescale = 1;
3818                 lightscale = 1;
3819                 style = 0;
3820                 skin = 0;
3821                 pflags = 0;
3822                 effects = 0;
3823                 islight = false;
3824                 while (1)
3825                 {
3826                         if (!COM_ParseToken_Simple(&data, false, false))
3827                                 break; // error
3828                         if (com_token[0] == '}')
3829                                 break; // end of entity
3830                         if (com_token[0] == '_')
3831                                 strlcpy(key, com_token + 1, sizeof(key));
3832                         else
3833                                 strlcpy(key, com_token, sizeof(key));
3834                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3835                                 key[strlen(key)-1] = 0;
3836                         if (!COM_ParseToken_Simple(&data, false, false))
3837                                 break; // error
3838                         strlcpy(value, com_token, sizeof(value));
3839
3840                         // now that we have the key pair worked out...
3841                         if (!strcmp("light", key))
3842                         {
3843                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3844                                 if (n == 1)
3845                                 {
3846                                         // quake
3847                                         light[0] = vec[0] * (1.0f / 256.0f);
3848                                         light[1] = vec[0] * (1.0f / 256.0f);
3849                                         light[2] = vec[0] * (1.0f / 256.0f);
3850                                         light[3] = vec[0];
3851                                 }
3852                                 else if (n == 4)
3853                                 {
3854                                         // halflife
3855                                         light[0] = vec[0] * (1.0f / 255.0f);
3856                                         light[1] = vec[1] * (1.0f / 255.0f);
3857                                         light[2] = vec[2] * (1.0f / 255.0f);
3858                                         light[3] = vec[3];
3859                                 }
3860                         }
3861                         else if (!strcmp("delay", key))
3862                                 type = atoi(value);
3863                         else if (!strcmp("origin", key))
3864                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3865                         else if (!strcmp("angle", key))
3866                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3867                         else if (!strcmp("angles", key))
3868                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3869                         else if (!strcmp("color", key))
3870                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3871                         else if (!strcmp("wait", key))
3872                                 fadescale = atof(value);
3873                         else if (!strcmp("classname", key))
3874                         {
3875                                 if (!strncmp(value, "light", 5))
3876                                 {
3877                                         islight = true;
3878                                         if (!strcmp(value, "light_fluoro"))
3879                                         {
3880                                                 originhack[0] = 0;
3881                                                 originhack[1] = 0;
3882                                                 originhack[2] = 0;
3883                                                 overridecolor[0] = 1;
3884                                                 overridecolor[1] = 1;
3885                                                 overridecolor[2] = 1;
3886                                         }
3887                                         if (!strcmp(value, "light_fluorospark"))
3888                                         {
3889                                                 originhack[0] = 0;
3890                                                 originhack[1] = 0;
3891                                                 originhack[2] = 0;
3892                                                 overridecolor[0] = 1;
3893                                                 overridecolor[1] = 1;
3894                                                 overridecolor[2] = 1;
3895                                         }
3896                                         if (!strcmp(value, "light_globe"))
3897                                         {
3898                                                 originhack[0] = 0;
3899                                                 originhack[1] = 0;
3900                                                 originhack[2] = 0;
3901                                                 overridecolor[0] = 1;
3902                                                 overridecolor[1] = 0.8;
3903                                                 overridecolor[2] = 0.4;
3904                                         }
3905                                         if (!strcmp(value, "light_flame_large_yellow"))
3906                                         {
3907                                                 originhack[0] = 0;
3908                                                 originhack[1] = 0;
3909                                                 originhack[2] = 0;
3910                                                 overridecolor[0] = 1;
3911                                                 overridecolor[1] = 0.5;
3912                                                 overridecolor[2] = 0.1;
3913                                         }
3914                                         if (!strcmp(value, "light_flame_small_yellow"))
3915                                         {
3916                                                 originhack[0] = 0;
3917                                                 originhack[1] = 0;
3918                                                 originhack[2] = 0;
3919                                                 overridecolor[0] = 1;
3920                                                 overridecolor[1] = 0.5;
3921                                                 overridecolor[2] = 0.1;
3922                                         }
3923                                         if (!strcmp(value, "light_torch_small_white"))
3924                                         {
3925                                                 originhack[0] = 0;
3926                                                 originhack[1] = 0;
3927                                                 originhack[2] = 0;
3928                                                 overridecolor[0] = 1;
3929                                                 overridecolor[1] = 0.5;
3930                                                 overridecolor[2] = 0.1;
3931                                         }
3932                                         if (!strcmp(value, "light_torch_small_walltorch"))
3933                                         {
3934                                                 originhack[0] = 0;
3935                                                 originhack[1] = 0;
3936                                                 originhack[2] = 0;
3937                                                 overridecolor[0] = 1;
3938                                                 overridecolor[1] = 0.5;
3939                                                 overridecolor[2] = 0.1;
3940                                         }
3941                                 }
3942                         }
3943                         else if (!strcmp("style", key))
3944                                 style = atoi(value);
3945                         else if (!strcmp("skin", key))
3946                                 skin = (int)atof(value);
3947                         else if (!strcmp("pflags", key))
3948                                 pflags = (int)atof(value);
3949                         else if (!strcmp("effects", key))
3950                                 effects = (int)atof(value);
3951                         else if (r_refdef.worldmodel->type == mod_brushq3)
3952                         {
3953                                 if (!strcmp("scale", key))
3954                                         lightscale = atof(value);
3955                                 if (!strcmp("fade", key))
3956                                         fadescale = atof(value);
3957                         }
3958                 }
3959                 if (!islight)
3960                         continue;
3961                 if (lightscale <= 0)
3962                         lightscale = 1;
3963                 if (fadescale <= 0)
3964                         fadescale = 1;
3965                 if (color[0] == color[1] && color[0] == color[2])
3966                 {
3967                         color[0] *= overridecolor[0];
3968                         color[1] *= overridecolor[1];
3969                         color[2] *= overridecolor[2];
3970                 }
3971                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3972                 color[0] = color[0] * light[0];
3973                 color[1] = color[1] * light[1];
3974                 color[2] = color[2] * light[2];
3975                 switch (type)
3976                 {
3977                 case LIGHTTYPE_MINUSX:
3978                         break;
3979                 case LIGHTTYPE_RECIPX:
3980                         radius *= 2;
3981                         VectorScale(color, (1.0f / 16.0f), color);
3982                         break;
3983                 case LIGHTTYPE_RECIPXX:
3984                         radius *= 2;
3985                         VectorScale(color, (1.0f / 16.0f), color);
3986                         break;
3987                 default:
3988                 case LIGHTTYPE_NONE:
3989                         break;
3990                 case LIGHTTYPE_SUN:
3991                         break;
3992                 case LIGHTTYPE_MINUSXX:
3993                         break;
3994                 }
3995                 VectorAdd(origin, originhack, origin);
3996                 if (radius >= 1)
3997                         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);
3998         }
3999         if (entfiledata)
4000                 Mem_Free(entfiledata);
4001 }
4002
4003
4004 void R_Shadow_SetCursorLocationForView(void)
4005 {
4006         vec_t dist, push;
4007         vec3_t dest, endpos;
4008         trace_t trace;
4009         VectorMA(r_refdef.view.origin, r_editlights_cursordistance.value, r_refdef.view.forward, dest);
4010         trace = CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
4011         if (trace.fraction < 1)
4012         {
4013                 dist = trace.fraction * r_editlights_cursordistance.value;
4014                 push = r_editlights_cursorpushback.value;
4015                 if (push > dist)
4016                         push = dist;
4017                 push = -push;
4018                 VectorMA(trace.endpos, push, r_refdef.view.forward, endpos);
4019                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
4020         }
4021         else
4022         {
4023                 VectorClear( endpos );
4024         }
4025         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4026         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4027         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4028 }
4029
4030 void R_Shadow_UpdateWorldLightSelection(void)
4031 {
4032         if (r_editlights.integer)
4033         {
4034                 R_Shadow_SetCursorLocationForView();
4035                 R_Shadow_SelectLightInView();
4036         }
4037         else
4038                 R_Shadow_SelectLight(NULL);
4039 }
4040
4041 void R_Shadow_EditLights_Clear_f(void)
4042 {
4043         R_Shadow_ClearWorldLights();
4044 }
4045
4046 void R_Shadow_EditLights_Reload_f(void)
4047 {
4048         if (!r_refdef.worldmodel)
4049                 return;
4050         strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
4051         R_Shadow_ClearWorldLights();
4052         R_Shadow_LoadWorldLights();
4053         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4054         {
4055                 R_Shadow_LoadLightsFile();
4056                 if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4057                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4058         }
4059 }
4060
4061 void R_Shadow_EditLights_Save_f(void)
4062 {
4063         if (!r_refdef.worldmodel)
4064                 return;
4065         R_Shadow_SaveWorldLights();
4066 }
4067
4068 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
4069 {
4070         R_Shadow_ClearWorldLights();
4071         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4072 }
4073
4074 void R_Shadow_EditLights_ImportLightsFile_f(void)
4075 {
4076         R_Shadow_ClearWorldLights();
4077         R_Shadow_LoadLightsFile();
4078 }
4079
4080 void R_Shadow_EditLights_Spawn_f(void)
4081 {
4082         vec3_t color;
4083         if (!r_editlights.integer)
4084         {
4085                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4086                 return;
4087         }
4088         if (Cmd_Argc() != 1)
4089         {
4090                 Con_Print("r_editlights_spawn does not take parameters\n");
4091                 return;
4092         }
4093         color[0] = color[1] = color[2] = 1;
4094         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
4095 }
4096
4097 void R_Shadow_EditLights_Edit_f(void)
4098 {
4099         vec3_t origin, angles, color;
4100         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
4101         int style, shadows, flags, normalmode, realtimemode;
4102         char cubemapname[MAX_INPUTLINE];
4103         if (!r_editlights.integer)
4104         {
4105                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4106                 return;
4107         }
4108         if (!r_shadow_selectedlight)
4109         {
4110                 Con_Print("No selected light.\n");
4111                 return;
4112         }
4113         VectorCopy(r_shadow_selectedlight->origin, origin);
4114         VectorCopy(r_shadow_selectedlight->angles, angles);
4115         VectorCopy(r_shadow_selectedlight->color, color);
4116         radius = r_shadow_selectedlight->radius;
4117         style = r_shadow_selectedlight->style;
4118         if (r_shadow_selectedlight->cubemapname)
4119                 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
4120         else
4121                 cubemapname[0] = 0;
4122         shadows = r_shadow_selectedlight->shadow;
4123         corona = r_shadow_selectedlight->corona;
4124         coronasizescale = r_shadow_selectedlight->coronasizescale;
4125         ambientscale = r_shadow_selectedlight->ambientscale;
4126         diffusescale = r_shadow_selectedlight->diffusescale;
4127         specularscale = r_shadow_selectedlight->specularscale;
4128         flags = r_shadow_selectedlight->flags;
4129         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
4130         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
4131         if (!strcmp(Cmd_Argv(1), "origin"))
4132         {
4133                 if (Cmd_Argc() != 5)
4134                 {
4135                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4136                         return;
4137                 }
4138                 origin[0] = atof(Cmd_Argv(2));
4139                 origin[1] = atof(Cmd_Argv(3));
4140                 origin[2] = atof(Cmd_Argv(4));
4141         }
4142         else if (!strcmp(Cmd_Argv(1), "originx"))
4143         {
4144                 if (Cmd_Argc() != 3)
4145                 {
4146                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4147                         return;
4148                 }
4149                 origin[0] = atof(Cmd_Argv(2));
4150         }
4151         else if (!strcmp(Cmd_Argv(1), "originy"))
4152         {
4153                 if (Cmd_Argc() != 3)
4154                 {
4155                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4156                         return;
4157                 }
4158                 origin[1] = atof(Cmd_Argv(2));
4159         }
4160         else if (!strcmp(Cmd_Argv(1), "originz"))
4161         {
4162                 if (Cmd_Argc() != 3)
4163                 {
4164                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4165                         return;
4166                 }
4167                 origin[2] = atof(Cmd_Argv(2));
4168         }
4169         else if (!strcmp(Cmd_Argv(1), "move"))
4170         {
4171                 if (Cmd_Argc() != 5)
4172                 {
4173                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4174                         return;
4175                 }
4176                 origin[0] += atof(Cmd_Argv(2));
4177                 origin[1] += atof(Cmd_Argv(3));
4178                 origin[2] += atof(Cmd_Argv(4));
4179         }
4180         else if (!strcmp(Cmd_Argv(1), "movex"))
4181         {
4182                 if (Cmd_Argc() != 3)
4183                 {
4184                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4185                         return;
4186                 }
4187                 origin[0] += atof(Cmd_Argv(2));
4188         }
4189         else if (!strcmp(Cmd_Argv(1), "movey"))
4190         {
4191                 if (Cmd_Argc() != 3)
4192                 {
4193                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4194                         return;
4195                 }
4196                 origin[1] += atof(Cmd_Argv(2));
4197         }
4198         else if (!strcmp(Cmd_Argv(1), "movez"))
4199         {
4200                 if (Cmd_Argc() != 3)
4201                 {
4202                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4203                         return;
4204                 }
4205                 origin[2] += atof(Cmd_Argv(2));
4206         }
4207         else if (!strcmp(Cmd_Argv(1), "angles"))
4208         {
4209                 if (Cmd_Argc() != 5)
4210                 {
4211                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4212                         return;
4213                 }
4214                 angles[0] = atof(Cmd_Argv(2));
4215                 angles[1] = atof(Cmd_Argv(3));
4216                 angles[2] = atof(Cmd_Argv(4));
4217         }
4218         else if (!strcmp(Cmd_Argv(1), "anglesx"))
4219         {
4220                 if (Cmd_Argc() != 3)
4221                 {
4222                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4223                         return;
4224                 }
4225                 angles[0] = atof(Cmd_Argv(2));
4226         }
4227         else if (!strcmp(Cmd_Argv(1), "anglesy"))
4228         {
4229                 if (Cmd_Argc() != 3)
4230                 {
4231                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4232                         return;
4233                 }
4234                 angles[1] = atof(Cmd_Argv(2));
4235         }
4236         else if (!strcmp(Cmd_Argv(1), "anglesz"))
4237         {
4238                 if (Cmd_Argc() != 3)
4239                 {
4240                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4241                         return;
4242                 }
4243                 angles[2] = atof(Cmd_Argv(2));
4244         }
4245         else if (!strcmp(Cmd_Argv(1), "color"))
4246         {
4247                 if (Cmd_Argc() != 5)
4248                 {
4249                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
4250                         return;
4251                 }
4252                 color[0] = atof(Cmd_Argv(2));
4253                 color[1] = atof(Cmd_Argv(3));
4254                 color[2] = atof(Cmd_Argv(4));
4255         }
4256         else if (!strcmp(Cmd_Argv(1), "radius"))
4257         {
4258                 if (Cmd_Argc() != 3)
4259                 {
4260                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4261                         return;
4262                 }
4263                 radius = atof(Cmd_Argv(2));
4264         }
4265         else if (!strcmp(Cmd_Argv(1), "colorscale"))
4266         {
4267                 if (Cmd_Argc() == 3)
4268                 {
4269                         double scale = atof(Cmd_Argv(2));
4270                         color[0] *= scale;
4271                         color[1] *= scale;
4272                         color[2] *= scale;
4273                 }
4274                 else
4275                 {
4276                         if (Cmd_Argc() != 5)
4277                         {
4278                                 Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
4279                                 return;
4280                         }
4281                         color[0] *= atof(Cmd_Argv(2));
4282                         color[1] *= atof(Cmd_Argv(3));
4283                         color[2] *= atof(Cmd_Argv(4));
4284                 }
4285         }
4286         else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
4287         {
4288                 if (Cmd_Argc() != 3)
4289                 {
4290                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4291                         return;
4292                 }
4293                 radius *= atof(Cmd_Argv(2));
4294         }
4295         else if (!strcmp(Cmd_Argv(1), "style"))
4296         {
4297                 if (Cmd_Argc() != 3)
4298                 {
4299                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4300                         return;
4301                 }
4302                 style = atoi(Cmd_Argv(2));
4303         }
4304         else if (!strcmp(Cmd_Argv(1), "cubemap"))
4305         {
4306                 if (Cmd_Argc() > 3)
4307                 {
4308                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4309                         return;
4310                 }
4311                 if (Cmd_Argc() == 3)
4312                         strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
4313                 else
4314                         cubemapname[0] = 0;
4315         }
4316         else if (!strcmp(Cmd_Argv(1), "shadows"))
4317         {
4318                 if (Cmd_Argc() != 3)
4319                 {
4320                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4321                         return;
4322                 }
4323                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4324         }
4325         else if (!strcmp(Cmd_Argv(1), "corona"))
4326         {
4327                 if (Cmd_Argc() != 3)
4328                 {
4329                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4330                         return;
4331                 }
4332                 corona = atof(Cmd_Argv(2));
4333         }
4334         else if (!strcmp(Cmd_Argv(1), "coronasize"))
4335         {
4336                 if (Cmd_Argc() != 3)
4337                 {
4338                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4339                         return;
4340                 }
4341                 coronasizescale = atof(Cmd_Argv(2));
4342         }
4343         else if (!strcmp(Cmd_Argv(1), "ambient"))
4344         {
4345                 if (Cmd_Argc() != 3)
4346                 {
4347                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4348                         return;
4349                 }
4350                 ambientscale = atof(Cmd_Argv(2));
4351         }
4352         else if (!strcmp(Cmd_Argv(1), "diffuse"))
4353         {
4354                 if (Cmd_Argc() != 3)
4355                 {
4356                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4357                         return;
4358                 }
4359                 diffusescale = atof(Cmd_Argv(2));
4360         }
4361         else if (!strcmp(Cmd_Argv(1), "specular"))
4362         {
4363                 if (Cmd_Argc() != 3)
4364                 {
4365                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4366                         return;
4367                 }
4368                 specularscale = atof(Cmd_Argv(2));
4369         }
4370         else if (!strcmp(Cmd_Argv(1), "normalmode"))
4371         {
4372                 if (Cmd_Argc() != 3)
4373                 {
4374                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4375                         return;
4376                 }
4377                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4378         }
4379         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
4380         {
4381                 if (Cmd_Argc() != 3)
4382                 {
4383                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4384                         return;
4385                 }
4386                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4387         }
4388         else
4389         {
4390                 Con_Print("usage: r_editlights_edit [property] [value]\n");
4391                 Con_Print("Selected light's properties:\n");
4392                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4393                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4394                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4395                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
4396                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
4397                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
4398                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4399                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
4400                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
4401                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
4402                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
4403                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
4404                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4405                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4406                 return;
4407         }
4408         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4409         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4410 }
4411
4412 void R_Shadow_EditLights_EditAll_f(void)
4413 {
4414         size_t lightindex;
4415         dlight_t *light;
4416
4417         if (!r_editlights.integer)
4418         {
4419                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4420                 return;
4421         }
4422
4423         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4424         {
4425                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4426                 if (!light)
4427                         continue;
4428                 R_Shadow_SelectLight(light);
4429                 R_Shadow_EditLights_Edit_f();
4430         }
4431 }
4432
4433 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4434 {
4435         int lightnumber, lightcount;
4436         size_t lightindex;
4437         dlight_t *light;
4438         float x, y;
4439         char temp[256];
4440         if (!r_editlights.integer)
4441                 return;
4442         x = vid_conwidth.value - 240;
4443         y = 5;
4444         DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
4445         lightnumber = -1;
4446         lightcount = 0;
4447         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4448         {
4449                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4450                 if (!light)
4451                         continue;
4452                 if (light == r_shadow_selectedlight)
4453                         lightnumber = lightindex;
4454                 lightcount++;
4455         }
4456         sprintf(temp, "Cursor origin: %.0f %.0f %.0f", r_editlights_cursorlocation[0], r_editlights_cursorlocation[1], r_editlights_cursorlocation[2]); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4457         sprintf(temp, "Total lights : %i active (%i total)", lightcount, (int)Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray)); DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, false);y += 8;
4458         y += 8;
4459         if (r_shadow_selectedlight == NULL)
4460                 return;
4461         sprintf(temp, "Light #%i properties:", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4462         sprintf(temp, "Origin       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4463         sprintf(temp, "Angles       : %.0f %.0f %.0f\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, NULL, true);y += 8;
4464         sprintf(temp, "Color        : %.2f %.2f %.2f\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, NULL, true);y += 8;
4465         sprintf(temp, "Radius       : %.0f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4466         sprintf(temp, "Corona       : %.0f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4467         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4468         sprintf(temp, "Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4469         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4470         sprintf(temp, "CoronaSize   : %.2f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4471         sprintf(temp, "Ambient      : %.2f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4472         sprintf(temp, "Diffuse      : %.2f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4473         sprintf(temp, "Specular     : %.2f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4474         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, NULL, true);y += 8;
4475         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, NULL, true);y += 8;
4476 }
4477
4478 void R_Shadow_EditLights_ToggleShadow_f(void)
4479 {
4480         if (!r_editlights.integer)
4481         {
4482                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4483                 return;
4484         }
4485         if (!r_shadow_selectedlight)
4486         {
4487                 Con_Print("No selected light.\n");
4488                 return;
4489         }
4490         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);
4491 }
4492
4493 void R_Shadow_EditLights_ToggleCorona_f(void)
4494 {
4495         if (!r_editlights.integer)
4496         {
4497                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4498                 return;
4499         }
4500         if (!r_shadow_selectedlight)
4501         {
4502                 Con_Print("No selected light.\n");
4503                 return;
4504         }
4505         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);
4506 }
4507
4508 void R_Shadow_EditLights_Remove_f(void)
4509 {
4510         if (!r_editlights.integer)
4511         {
4512                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
4513                 return;
4514         }
4515         if (!r_shadow_selectedlight)
4516         {
4517                 Con_Print("No selected light.\n");
4518                 return;
4519         }
4520         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4521         r_shadow_selectedlight = NULL;
4522 }
4523
4524 void R_Shadow_EditLights_Help_f(void)
4525 {
4526         Con_Print(
4527 "Documentation on r_editlights system:\n"
4528 "Settings:\n"
4529 "r_editlights : enable/disable editing mode\n"
4530 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4531 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4532 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4533 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4534 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4535 "Commands:\n"
4536 "r_editlights_help : this help\n"
4537 "r_editlights_clear : remove all lights\n"
4538 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4539 "r_editlights_save : save to .rtlights file\n"
4540 "r_editlights_spawn : create a light with default settings\n"
4541 "r_editlights_edit command : edit selected light - more documentation below\n"
4542 "r_editlights_remove : remove selected light\n"
4543 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4544 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4545 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4546 "Edit commands:\n"
4547 "origin x y z : set light location\n"
4548 "originx x: set x component of light location\n"
4549 "originy y: set y component of light location\n"
4550 "originz z: set z component of light location\n"
4551 "move x y z : adjust light location\n"
4552 "movex x: adjust x component of light location\n"
4553 "movey y: adjust y component of light location\n"
4554 "movez z: adjust z component of light location\n"
4555 "angles x y z : set light angles\n"
4556 "anglesx x: set x component of light angles\n"
4557 "anglesy y: set y component of light angles\n"
4558 "anglesz z: set z component of light angles\n"
4559 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4560 "radius radius : set radius (size) of light\n"
4561 "colorscale grey : multiply color of light (1 does nothing)\n"
4562 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
4563 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
4564 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
4565 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4566 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4567 "shadows 1/0 : turn on/off shadows\n"
4568 "corona n : set corona intensity\n"
4569 "coronasize n : set corona size (0-1)\n"
4570 "ambient n : set ambient intensity (0-1)\n"
4571 "diffuse n : set diffuse intensity (0-1)\n"
4572 "specular n : set specular intensity (0-1)\n"
4573 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4574 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4575 "<nothing> : print light properties to console\n"
4576         );
4577 }
4578
4579 void R_Shadow_EditLights_CopyInfo_f(void)
4580 {
4581         if (!r_editlights.integer)
4582         {
4583                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
4584                 return;
4585         }
4586         if (!r_shadow_selectedlight)
4587         {
4588                 Con_Print("No selected light.\n");
4589                 return;
4590         }
4591         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4592         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4593         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4594         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4595         if (r_shadow_selectedlight->cubemapname)
4596                 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
4597         else
4598                 r_shadow_bufferlight.cubemapname[0] = 0;
4599         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4600         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4601         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4602         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4603         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4604         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4605         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4606 }
4607
4608 void R_Shadow_EditLights_PasteInfo_f(void)
4609 {
4610         if (!r_editlights.integer)
4611         {
4612                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
4613                 return;
4614         }
4615         if (!r_shadow_selectedlight)
4616         {
4617                 Con_Print("No selected light.\n");
4618                 return;
4619         }
4620         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);
4621 }
4622
4623 void R_Shadow_EditLights_Init(void)
4624 {
4625         Cvar_RegisterVariable(&r_editlights);
4626         Cvar_RegisterVariable(&r_editlights_cursordistance);
4627         Cvar_RegisterVariable(&r_editlights_cursorpushback);
4628         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4629         Cvar_RegisterVariable(&r_editlights_cursorgrid);
4630         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4631         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
4632         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
4633         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f, "reloads rtlights file (or imports from .lights file or .ent file or the map itself)");
4634         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
4635         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
4636         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
4637         Cmd_AddCommand("r_editlights_editall", R_Shadow_EditLights_EditAll_f, "changes a property on ALL lights at once (tip: use radiusscale and colorscale to alter these properties)");
4638         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
4639         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
4640         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
4641         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
4642         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
4643         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
4644         Cmd_AddCommand("r_editlights_pasteinfo", R_Shadow_EditLights_PasteInfo_f, "apply the stored properties onto the selected light (making it exactly identical except for origin)");
4645 }
4646
4647
4648
4649 /*
4650 =============================================================================
4651
4652 LIGHT SAMPLING
4653
4654 =============================================================================
4655 */
4656
4657 void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic)
4658 {
4659         VectorClear(diffusecolor);
4660         VectorClear(diffusenormal);
4661
4662         if (!r_fullbright.integer && r_refdef.worldmodel && r_refdef.worldmodel->brush.LightPoint)
4663         {
4664                 ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_ambient.value * (2.0f / 128.0f);
4665                 r_refdef.worldmodel->brush.LightPoint(r_refdef.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
4666         }
4667         else
4668                 VectorSet(ambientcolor, 1, 1, 1);
4669
4670         if (dynamic)
4671         {
4672                 int i;
4673                 float f, v[3];
4674                 rtlight_t *light;
4675                 for (i = 0;i < r_refdef.numlights;i++)
4676                 {
4677                         light = &r_refdef.lights[i];
4678                         Matrix4x4_Transform(&light->matrix_worldtolight, p, v);
4679                         f = 1 - VectorLength2(v);
4680                         if (f > 0 && CL_Move(p, vec3_origin, vec3_origin, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
4681                                 VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
4682                 }
4683         }
4684 }