]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
use GL20TU_ enum values instead of constant texture unit numbers, to
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is 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 (cl.worldmodel && strncmp(cl.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_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap)); // light filter
1139                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1140                 GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1141                 CHECKGLERROR
1142         }
1143 }
1144
1145 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
1146 {
1147         CHECKGLERROR
1148         R_Shadow_RenderMode_Reset();
1149         GL_BlendFunc(GL_ONE, GL_ONE);
1150         GL_DepthRange(0, 1);
1151         GL_DepthTest(r_showshadowvolumes.integer < 2);
1152         GL_Color(0.0, 0.0125 * r_refdef.view.colorscale, 0.1 * r_refdef.view.colorscale, 1);
1153         GL_PolygonOffset(r_refdef.shadowpolygonfactor, r_refdef.shadowpolygonoffset);CHECKGLERROR
1154         GL_CullFace(GL_NONE);
1155         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
1156 }
1157
1158 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
1159 {
1160         CHECKGLERROR
1161         R_Shadow_RenderMode_Reset();
1162         GL_BlendFunc(GL_ONE, GL_ONE);
1163         GL_DepthRange(0, 1);
1164         GL_DepthTest(r_showlighting.integer < 2);
1165         GL_Color(0.1 * r_refdef.view.colorscale, 0.0125 * r_refdef.view.colorscale, 0, 1);
1166         if (!transparent)
1167         {
1168                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1169         }
1170         if (stenciltest)
1171         {
1172                 qglEnable(GL_STENCIL_TEST);CHECKGLERROR
1173                 qglStencilFunc(GL_EQUAL, 128, ~0);CHECKGLERROR
1174         }
1175         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
1176 }
1177
1178 void R_Shadow_RenderMode_End(void)
1179 {
1180         CHECKGLERROR
1181         R_Shadow_RenderMode_Reset();
1182         R_Shadow_RenderMode_ActiveLight(NULL);
1183         GL_DepthMask(true);
1184         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1185         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1186 }
1187
1188 int bboxedges[12][2] =
1189 {
1190         // top
1191         {0, 1}, // +X
1192         {0, 2}, // +Y
1193         {1, 3}, // Y, +X
1194         {2, 3}, // X, +Y
1195         // bottom
1196         {4, 5}, // +X
1197         {4, 6}, // +Y
1198         {5, 7}, // Y, +X
1199         {6, 7}, // X, +Y
1200         // verticals
1201         {0, 4}, // +Z
1202         {1, 5}, // X, +Z
1203         {2, 6}, // Y, +Z
1204         {3, 7}, // XY, +Z
1205 };
1206
1207 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1208 {
1209         int i, ix1, iy1, ix2, iy2;
1210         float x1, y1, x2, y2;
1211         vec4_t v, v2;
1212         float vertex[20][3];
1213         int j, k;
1214         vec4_t plane4f;
1215         int numvertices;
1216         float corner[8][4];
1217         float dist[8];
1218         int sign[8];
1219         float f;
1220
1221         if (!r_shadow_scissor.integer)
1222                 return false;
1223
1224         // if view is inside the light box, just say yes it's visible
1225         if (BoxesOverlap(r_refdef.view.origin, r_refdef.view.origin, mins, maxs))
1226         {
1227                 GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
1228                 return false;
1229         }
1230
1231         x1 = y1 = x2 = y2 = 0;
1232
1233         // transform all corners that are infront of the nearclip plane
1234         VectorNegate(r_refdef.view.frustum[4].normal, plane4f);
1235         plane4f[3] = r_refdef.view.frustum[4].dist;
1236         numvertices = 0;
1237         for (i = 0;i < 8;i++)
1238         {
1239                 Vector4Set(corner[i], (i & 1) ? maxs[0] : mins[0], (i & 2) ? maxs[1] : mins[1], (i & 4) ? maxs[2] : mins[2], 1);
1240                 dist[i] = DotProduct4(corner[i], plane4f);
1241                 sign[i] = dist[i] > 0;
1242                 if (!sign[i])
1243                 {
1244                         VectorCopy(corner[i], vertex[numvertices]);
1245                         numvertices++;
1246                 }
1247         }
1248         // if some points are behind the nearclip, add clipped edge points to make
1249         // sure that the scissor boundary is complete
1250         if (numvertices > 0 && numvertices < 8)
1251         {
1252                 // add clipped edge points
1253                 for (i = 0;i < 12;i++)
1254                 {
1255                         j = bboxedges[i][0];
1256                         k = bboxedges[i][1];
1257                         if (sign[j] != sign[k])
1258                         {
1259                                 f = dist[j] / (dist[j] - dist[k]);
1260                                 VectorLerp(corner[j], f, corner[k], vertex[numvertices]);
1261                                 numvertices++;
1262                         }
1263                 }
1264         }
1265
1266         // if we have no points to check, the light is behind the view plane
1267         if (!numvertices)
1268                 return true;
1269
1270         // if we have some points to transform, check what screen area is covered
1271         x1 = y1 = x2 = y2 = 0;
1272         v[3] = 1.0f;
1273         //Con_Printf("%i vertices to transform...\n", numvertices);
1274         for (i = 0;i < numvertices;i++)
1275         {
1276                 VectorCopy(vertex[i], v);
1277                 GL_TransformToScreen(v, v2);
1278                 //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]);
1279                 if (i)
1280                 {
1281                         if (x1 > v2[0]) x1 = v2[0];
1282                         if (x2 < v2[0]) x2 = v2[0];
1283                         if (y1 > v2[1]) y1 = v2[1];
1284                         if (y2 < v2[1]) y2 = v2[1];
1285                 }
1286                 else
1287                 {
1288                         x1 = x2 = v2[0];
1289                         y1 = y2 = v2[1];
1290                 }
1291         }
1292
1293         // now convert the scissor rectangle to integer screen coordinates
1294         ix1 = (int)(x1 - 1.0f);
1295         iy1 = (int)(y1 - 1.0f);
1296         ix2 = (int)(x2 + 1.0f);
1297         iy2 = (int)(y2 + 1.0f);
1298         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1299
1300         // clamp it to the screen
1301         if (ix1 < r_refdef.view.x) ix1 = r_refdef.view.x;
1302         if (iy1 < r_refdef.view.y) iy1 = r_refdef.view.y;
1303         if (ix2 > r_refdef.view.x + r_refdef.view.width) ix2 = r_refdef.view.x + r_refdef.view.width;
1304         if (iy2 > r_refdef.view.y + r_refdef.view.height) iy2 = r_refdef.view.y + r_refdef.view.height;
1305
1306         // if it is inside out, it's not visible
1307         if (ix2 <= ix1 || iy2 <= iy1)
1308                 return true;
1309
1310         // the light area is visible, set up the scissor rectangle
1311         GL_Scissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1312         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);CHECKGLERROR
1313         //qglEnable(GL_SCISSOR_TEST);CHECKGLERROR
1314         r_refdef.stats.lights_scissored++;
1315         return false;
1316 }
1317
1318 static void R_Shadow_RenderLighting_Light_Vertex_Shading(int firstvertex, int numverts, int numtriangles, const int *element3i, const float *diffusecolor, const float *ambientcolor)
1319 {
1320         float *vertex3f = rsurface.vertex3f + 3 * firstvertex;
1321         float *normal3f = rsurface.normal3f + 3 * firstvertex;
1322         float *color4f = rsurface.array_color4f + 4 * firstvertex;
1323         float dist, dot, distintensity, shadeintensity, v[3], n[3];
1324         if (r_textureunits.integer >= 3)
1325         {
1326                 if (VectorLength2(diffusecolor) > 0)
1327                 {
1328                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1329                         {
1330                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1331                                 Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1332                                 if ((dot = DotProduct(n, v)) < 0)
1333                                 {
1334                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1335                                         VectorMA(ambientcolor, shadeintensity, diffusecolor, color4f);
1336                                 }
1337                                 else
1338                                         VectorCopy(ambientcolor, color4f);
1339                                 if (r_refdef.fogenabled)
1340                                 {
1341                                         float f;
1342                                         f = FogPoint_Model(vertex3f);
1343                                         VectorScale(color4f, f, color4f);
1344                                 }
1345                                 color4f[3] = 1;
1346                         }
1347                 }
1348                 else
1349                 {
1350                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1351                         {
1352                                 VectorCopy(ambientcolor, color4f);
1353                                 if (r_refdef.fogenabled)
1354                                 {
1355                                         float f;
1356                                         Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1357                                         f = FogPoint_Model(vertex3f);
1358                                         VectorScale(color4f, f, color4f);
1359                                 }
1360                                 color4f[3] = 1;
1361                         }
1362                 }
1363         }
1364         else if (r_textureunits.integer >= 2)
1365         {
1366                 if (VectorLength2(diffusecolor) > 0)
1367                 {
1368                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1369                         {
1370                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1371                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1372                                 {
1373                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1374                                         if ((dot = DotProduct(n, v)) < 0)
1375                                         {
1376                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1377                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1378                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1379                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1380                                         }
1381                                         else
1382                                         {
1383                                                 color4f[0] = ambientcolor[0] * distintensity;
1384                                                 color4f[1] = ambientcolor[1] * distintensity;
1385                                                 color4f[2] = ambientcolor[2] * distintensity;
1386                                         }
1387                                         if (r_refdef.fogenabled)
1388                                         {
1389                                                 float f;
1390                                                 f = FogPoint_Model(vertex3f);
1391                                                 VectorScale(color4f, f, color4f);
1392                                         }
1393                                 }
1394                                 else
1395                                         VectorClear(color4f);
1396                                 color4f[3] = 1;
1397                         }
1398                 }
1399                 else
1400                 {
1401                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1402                         {
1403                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1404                                 if ((dist = fabs(v[2])) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1405                                 {
1406                                         color4f[0] = ambientcolor[0] * distintensity;
1407                                         color4f[1] = ambientcolor[1] * distintensity;
1408                                         color4f[2] = ambientcolor[2] * distintensity;
1409                                         if (r_refdef.fogenabled)
1410                                         {
1411                                                 float f;
1412                                                 f = FogPoint_Model(vertex3f);
1413                                                 VectorScale(color4f, f, color4f);
1414                                         }
1415                                 }
1416                                 else
1417                                         VectorClear(color4f);
1418                                 color4f[3] = 1;
1419                         }
1420                 }
1421         }
1422         else
1423         {
1424                 if (VectorLength2(diffusecolor) > 0)
1425                 {
1426                         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1427                         {
1428                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1429                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1430                                 {
1431                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1432                                         Matrix4x4_Transform3x3(&rsurface.entitytolight, normal3f, n);
1433                                         if ((dot = DotProduct(n, v)) < 0)
1434                                         {
1435                                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1436                                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1437                                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1438                                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1439                                         }
1440                                         else
1441                                         {
1442                                                 color4f[0] = ambientcolor[0] * distintensity;
1443                                                 color4f[1] = ambientcolor[1] * distintensity;
1444                                                 color4f[2] = ambientcolor[2] * distintensity;
1445                                         }
1446                                         if (r_refdef.fogenabled)
1447                                         {
1448                                                 float f;
1449                                                 f = FogPoint_Model(vertex3f);
1450                                                 VectorScale(color4f, f, color4f);
1451                                         }
1452                                 }
1453                                 else
1454                                         VectorClear(color4f);
1455                                 color4f[3] = 1;
1456                         }
1457                 }
1458                 else
1459                 {
1460                         for (;numverts > 0;numverts--, vertex3f += 3, color4f += 4)
1461                         {
1462                                 Matrix4x4_Transform(&rsurface.entitytolight, vertex3f, v);
1463                                 if ((dist = VectorLength(v)) < 1 && (distintensity = r_shadow_attentable[(int)(dist * ATTENTABLESIZE)]))
1464                                 {
1465                                         distintensity = (1 - dist) * r_shadow_lightattenuationlinearscale.value / (r_shadow_lightattenuationdividebias.value + dist*dist);
1466                                         color4f[0] = ambientcolor[0] * distintensity;
1467                                         color4f[1] = ambientcolor[1] * distintensity;
1468                                         color4f[2] = ambientcolor[2] * distintensity;
1469                                         if (r_refdef.fogenabled)
1470                                         {
1471                                                 float f;
1472                                                 f = FogPoint_Model(vertex3f);
1473                                                 VectorScale(color4f, f, color4f);
1474                                         }
1475                                 }
1476                                 else
1477                                         VectorClear(color4f);
1478                                 color4f[3] = 1;
1479                         }
1480                 }
1481         }
1482 }
1483
1484 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1485
1486 static void R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1487 {
1488         int i;
1489         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1490         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1491         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1492         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1493         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1494         float lightdir[3];
1495         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1496         {
1497                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1498                 // the cubemap normalizes this for us
1499                 out3f[0] = DotProduct(svector3f, lightdir);
1500                 out3f[1] = DotProduct(tvector3f, lightdir);
1501                 out3f[2] = DotProduct(normal3f, lightdir);
1502         }
1503 }
1504
1505 static void R_Shadow_GenTexCoords_Specular_NormalCubeMap(int firstvertex, int numvertices, int numtriangles, const int *element3i)
1506 {
1507         int i;
1508         float       *out3f     = rsurface.array_texcoord3f + 3 * firstvertex;
1509         const float *vertex3f  = rsurface.vertex3f         + 3 * firstvertex;
1510         const float *svector3f = rsurface.svector3f        + 3 * firstvertex;
1511         const float *tvector3f = rsurface.tvector3f        + 3 * firstvertex;
1512         const float *normal3f  = rsurface.normal3f         + 3 * firstvertex;
1513         float lightdir[3], eyedir[3], halfdir[3];
1514         for (i = 0;i < numvertices;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1515         {
1516                 VectorSubtract(rsurface.entitylightorigin, vertex3f, lightdir);
1517                 VectorNormalize(lightdir);
1518                 VectorSubtract(rsurface.modelorg, vertex3f, eyedir);
1519                 VectorNormalize(eyedir);
1520                 VectorAdd(lightdir, eyedir, halfdir);
1521                 // the cubemap normalizes this for us
1522                 out3f[0] = DotProduct(svector3f, halfdir);
1523                 out3f[1] = DotProduct(tvector3f, halfdir);
1524                 out3f[2] = DotProduct(normal3f, halfdir);
1525         }
1526 }
1527
1528 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)
1529 {
1530         // used to display how many times a surface is lit for level design purposes
1531         GL_Color(0.1 * r_refdef.view.colorscale, 0.025 * r_refdef.view.colorscale, 0, 1);
1532         R_Mesh_ColorPointer(NULL, 0, 0);
1533         R_Mesh_ResetTextureState();
1534         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1535 }
1536
1537 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)
1538 {
1539         // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1540         R_SetupSurfaceShader(lightcolorbase, false, ambientscale, diffusescale, specularscale, RSURFPASS_RTLIGHT);
1541         R_Mesh_TexMatrix(0, &rsurface.texture->currenttexmatrix);
1542         R_Mesh_TexBind(GL20TU_NORMAL, R_GetTexture(rsurface.texture->currentskinframe->nmap));
1543         R_Mesh_TexBind(GL20TU_COLOR, R_GetTexture(rsurface.texture->basetexture));
1544         R_Mesh_TexBind(GL20TU_GLOSS, R_GetTexture(rsurface.texture->glosstexture));
1545         //R_Mesh_TexBindCubeMap(GL20TU_CUBE, R_GetTexture(rsurface.rtlight->currentcubemap));
1546         R_Mesh_TexBind(GL20TU_FOGMASK, R_GetTexture(r_texture_fogattenuation));
1547         R_Mesh_TexBind(GL20TU_PANTS, R_GetTexture(rsurface.texture->currentskinframe->pants));
1548         R_Mesh_TexBind(GL20TU_SHIRT, R_GetTexture(rsurface.texture->currentskinframe->shirt));
1549         R_Mesh_TexBind(GL20TU_ATTENUATION, R_GetTexture(r_shadow_attenuationgradienttexture));
1550         R_Mesh_TexCoordPointer(0, 2, rsurface.texcoordtexture2f, rsurface.texcoordtexture2f_bufferobject, rsurface.texcoordtexture2f_bufferoffset);
1551         R_Mesh_TexCoordPointer(1, 3, rsurface.svector3f, rsurface.svector3f_bufferobject, rsurface.svector3f_bufferoffset);
1552         R_Mesh_TexCoordPointer(2, 3, rsurface.tvector3f, rsurface.tvector3f_bufferobject, rsurface.tvector3f_bufferoffset);
1553         R_Mesh_TexCoordPointer(3, 3, rsurface.normal3f, rsurface.normal3f_bufferobject, rsurface.normal3f_bufferoffset);
1554         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1555         {
1556                 qglDepthFunc(GL_EQUAL);CHECKGLERROR
1557         }
1558         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1559         if (rsurface.texture->currentmaterialflags & MATERIALFLAG_ALPHATEST)
1560         {
1561                 qglDepthFunc(GL_LEQUAL);CHECKGLERROR
1562         }
1563 }
1564
1565 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)
1566 {
1567         // shared final code for all the dot3 layers
1568         int renders;
1569         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 0);
1570         for (renders = 0;renders < 64 && (r > 0 || g > 0 || b > 0);renders++, r--, g--, b--)
1571         {
1572                 GL_Color(bound(0, r, 1), bound(0, g, 1), bound(0, b, 1), 1);
1573                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1574         }
1575 }
1576
1577 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)
1578 {
1579         rmeshstate_t m;
1580         // colorscale accounts for how much we multiply the brightness
1581         // during combine.
1582         //
1583         // mult is how many times the final pass of the lighting will be
1584         // performed to get more brightness than otherwise possible.
1585         //
1586         // Limit mult to 64 for sanity sake.
1587         GL_Color(1,1,1,1);
1588         if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1589         {
1590                 // 3 3D combine path (Geforce3, Radeon 8500)
1591                 memset(&m, 0, sizeof(m));
1592                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1593                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1594                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1595                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1596                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1597                 m.tex[1] = R_GetTexture(basetexture);
1598                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1599                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1600                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1601                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1602                 m.texcubemap[2] = R_GetTexture(rsurface.rtlight->currentcubemap);
1603                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1604                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1605                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1606                 m.texmatrix[2] = rsurface.entitytolight;
1607                 GL_BlendFunc(GL_ONE, GL_ONE);
1608         }
1609         else if (r_shadow_texture3d.integer && rsurface.rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1610         {
1611                 // 2 3D combine path (Geforce3, original Radeon)
1612                 memset(&m, 0, sizeof(m));
1613                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1614                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1615                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1616                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1617                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1618                 m.tex[1] = R_GetTexture(basetexture);
1619                 m.pointer_texcoord[1] = rsurface.texcoordtexture2f;
1620                 m.pointer_texcoord_bufferobject[1] = rsurface.texcoordtexture2f_bufferobject;
1621                 m.pointer_texcoord_bufferoffset[1] = rsurface.texcoordtexture2f_bufferoffset;
1622                 m.texmatrix[1] = rsurface.texture->currenttexmatrix;
1623                 GL_BlendFunc(GL_ONE, GL_ONE);
1624         }
1625         else if (r_textureunits.integer >= 4 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1626         {
1627                 // 4 2D combine path (Geforce3, Radeon 8500)
1628                 memset(&m, 0, sizeof(m));
1629                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1630                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1631                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1632                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1633                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1634                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1635                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1636                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1637                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1638                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1639                 m.tex[2] = R_GetTexture(basetexture);
1640                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1641                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1642                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1643                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1644                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1645                 {
1646                         m.texcubemap[3] = R_GetTexture(rsurface.rtlight->currentcubemap);
1647                         m.pointer_texcoord3f[3] = rsurface.vertex3f;
1648                         m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1649                         m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1650                         m.texmatrix[3] = rsurface.entitytolight;
1651                 }
1652                 GL_BlendFunc(GL_ONE, GL_ONE);
1653         }
1654         else if (r_textureunits.integer >= 3 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1655         {
1656                 // 3 2D combine path (Geforce3, original Radeon)
1657                 memset(&m, 0, sizeof(m));
1658                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1659                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1660                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1661                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1662                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1663                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1664                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1665                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1666                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1667                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1668                 m.tex[2] = R_GetTexture(basetexture);
1669                 m.pointer_texcoord[2] = rsurface.texcoordtexture2f;
1670                 m.pointer_texcoord_bufferobject[2] = rsurface.texcoordtexture2f_bufferobject;
1671                 m.pointer_texcoord_bufferoffset[2] = rsurface.texcoordtexture2f_bufferoffset;
1672                 m.texmatrix[2] = rsurface.texture->currenttexmatrix;
1673                 GL_BlendFunc(GL_ONE, GL_ONE);
1674         }
1675         else
1676         {
1677                 // 2/2/2 2D combine path (any dot3 card)
1678                 memset(&m, 0, sizeof(m));
1679                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1680                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1681                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1682                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1683                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1684                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1685                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1686                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1687                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1688                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1689                 R_Mesh_TextureState(&m);
1690                 GL_ColorMask(0,0,0,1);
1691                 GL_BlendFunc(GL_ONE, GL_ZERO);
1692                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1693
1694                 // second pass
1695                 memset(&m, 0, sizeof(m));
1696                 m.tex[0] = R_GetTexture(basetexture);
1697                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1698                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1699                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1700                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1701                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1702                 {
1703                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1704                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1705                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1706                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1707                         m.texmatrix[1] = rsurface.entitytolight;
1708                 }
1709                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1710         }
1711         // this final code is shared
1712         R_Mesh_TextureState(&m);
1713         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1714 }
1715
1716 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)
1717 {
1718         rmeshstate_t m;
1719         // colorscale accounts for how much we multiply the brightness
1720         // during combine.
1721         //
1722         // mult is how many times the final pass of the lighting will be
1723         // performed to get more brightness than otherwise possible.
1724         //
1725         // Limit mult to 64 for sanity sake.
1726         GL_Color(1,1,1,1);
1727         // generate normalization cubemap texcoords
1728         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1729         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1730         {
1731                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1732                 memset(&m, 0, sizeof(m));
1733                 m.tex[0] = R_GetTexture(normalmaptexture);
1734                 m.texcombinergb[0] = GL_REPLACE;
1735                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1736                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1737                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1738                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1739                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1740                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1741                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1742                 m.pointer_texcoord_bufferobject[1] = 0;
1743                 m.pointer_texcoord_bufferoffset[1] = 0;
1744                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1745                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1746                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1747                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1748                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1749                 R_Mesh_TextureState(&m);
1750                 GL_ColorMask(0,0,0,1);
1751                 GL_BlendFunc(GL_ONE, GL_ZERO);
1752                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1753
1754                 // second pass
1755                 memset(&m, 0, sizeof(m));
1756                 m.tex[0] = R_GetTexture(basetexture);
1757                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1758                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1759                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1760                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1761                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1762                 {
1763                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1764                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1765                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1766                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1767                         m.texmatrix[1] = rsurface.entitytolight;
1768                 }
1769                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1770         }
1771         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1772         {
1773                 // 1/2/2 3D combine path (original Radeon)
1774                 memset(&m, 0, sizeof(m));
1775                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1776                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1777                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1778                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1779                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1780                 R_Mesh_TextureState(&m);
1781                 GL_ColorMask(0,0,0,1);
1782                 GL_BlendFunc(GL_ONE, GL_ZERO);
1783                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1784
1785                 // second pass
1786                 memset(&m, 0, sizeof(m));
1787                 m.tex[0] = R_GetTexture(normalmaptexture);
1788                 m.texcombinergb[0] = GL_REPLACE;
1789                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1790                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1791                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1792                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1793                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1794                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1795                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1796                 m.pointer_texcoord_bufferobject[1] = 0;
1797                 m.pointer_texcoord_bufferoffset[1] = 0;
1798                 R_Mesh_TextureState(&m);
1799                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1800                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1801
1802                 // second pass
1803                 memset(&m, 0, sizeof(m));
1804                 m.tex[0] = R_GetTexture(basetexture);
1805                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1806                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1807                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1808                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1809                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1810                 {
1811                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1812                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1813                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1814                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1815                         m.texmatrix[1] = rsurface.entitytolight;
1816                 }
1817                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1818         }
1819         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube)
1820         {
1821                 // 2/2 3D combine path (original Radeon)
1822                 memset(&m, 0, sizeof(m));
1823                 m.tex[0] = R_GetTexture(normalmaptexture);
1824                 m.texcombinergb[0] = GL_REPLACE;
1825                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1826                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1827                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1828                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1829                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1830                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1831                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1832                 m.pointer_texcoord_bufferobject[1] = 0;
1833                 m.pointer_texcoord_bufferoffset[1] = 0;
1834                 R_Mesh_TextureState(&m);
1835                 GL_ColorMask(0,0,0,1);
1836                 GL_BlendFunc(GL_ONE, GL_ZERO);
1837                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1838
1839                 // second pass
1840                 memset(&m, 0, sizeof(m));
1841                 m.tex[0] = R_GetTexture(basetexture);
1842                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1843                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1844                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1845                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1846                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1847                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1848                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1849                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1850                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
1851                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1852         }
1853         else if (r_textureunits.integer >= 4)
1854         {
1855                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1856                 memset(&m, 0, sizeof(m));
1857                 m.tex[0] = R_GetTexture(normalmaptexture);
1858                 m.texcombinergb[0] = GL_REPLACE;
1859                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1860                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1861                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1862                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1863                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1864                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1865                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1866                 m.pointer_texcoord_bufferobject[1] = 0;
1867                 m.pointer_texcoord_bufferoffset[1] = 0;
1868                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1869                 m.pointer_texcoord3f[2] = rsurface.vertex3f;
1870                 m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
1871                 m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
1872                 m.texmatrix[2] = rsurface.entitytoattenuationxyz;
1873                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1874                 m.pointer_texcoord3f[3] = rsurface.vertex3f;
1875                 m.pointer_texcoord_bufferobject[3] = rsurface.vertex3f_bufferobject;
1876                 m.pointer_texcoord_bufferoffset[3] = rsurface.vertex3f_bufferoffset;
1877                 m.texmatrix[3] = rsurface.entitytoattenuationz;
1878                 R_Mesh_TextureState(&m);
1879                 GL_ColorMask(0,0,0,1);
1880                 GL_BlendFunc(GL_ONE, GL_ZERO);
1881                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1882
1883                 // second pass
1884                 memset(&m, 0, sizeof(m));
1885                 m.tex[0] = R_GetTexture(basetexture);
1886                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1887                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1888                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1889                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1890                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1891                 {
1892                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1893                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1894                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1895                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1896                         m.texmatrix[1] = rsurface.entitytolight;
1897                 }
1898                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1899         }
1900         else
1901         {
1902                 // 2/2/2 2D combine path (any dot3 card)
1903                 memset(&m, 0, sizeof(m));
1904                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1905                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1906                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1907                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1908                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
1909                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1910                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
1911                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
1912                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
1913                 m.texmatrix[1] = rsurface.entitytoattenuationz;
1914                 R_Mesh_TextureState(&m);
1915                 GL_ColorMask(0,0,0,1);
1916                 GL_BlendFunc(GL_ONE, GL_ZERO);
1917                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1918
1919                 // second pass
1920                 memset(&m, 0, sizeof(m));
1921                 m.tex[0] = R_GetTexture(normalmaptexture);
1922                 m.texcombinergb[0] = GL_REPLACE;
1923                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1924                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1925                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1926                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1927                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1928                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1929                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1930                 m.pointer_texcoord_bufferobject[1] = 0;
1931                 m.pointer_texcoord_bufferoffset[1] = 0;
1932                 R_Mesh_TextureState(&m);
1933                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1934                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1935
1936                 // second pass
1937                 memset(&m, 0, sizeof(m));
1938                 m.tex[0] = R_GetTexture(basetexture);
1939                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1940                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1941                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1942                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1943                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
1944                 {
1945                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
1946                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
1947                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
1948                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
1949                         m.texmatrix[1] = rsurface.entitytolight;
1950                 }
1951                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1952         }
1953         // this final code is shared
1954         R_Mesh_TextureState(&m);
1955         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
1956 }
1957
1958 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)
1959 {
1960         float glossexponent;
1961         rmeshstate_t m;
1962         // FIXME: detect blendsquare!
1963         //if (!gl_support_blendsquare)
1964         //      return;
1965         GL_Color(1,1,1,1);
1966         // generate normalization cubemap texcoords
1967         R_Shadow_GenTexCoords_Specular_NormalCubeMap(firstvertex, numvertices, numtriangles, element3i);
1968         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap != r_texture_whitecube)
1969         {
1970                 // 2/0/0/1/2 3D combine blendsquare path
1971                 memset(&m, 0, sizeof(m));
1972                 m.tex[0] = R_GetTexture(normalmaptexture);
1973                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
1974                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
1975                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
1976                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
1977                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1978                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1979                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
1980                 m.pointer_texcoord_bufferobject[1] = 0;
1981                 m.pointer_texcoord_bufferoffset[1] = 0;
1982                 R_Mesh_TextureState(&m);
1983                 GL_ColorMask(0,0,0,1);
1984                 // this squares the result
1985                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1986                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1987
1988                 // second and third pass
1989                 R_Mesh_ResetTextureState();
1990                 // square alpha in framebuffer a few times to make it shiny
1991                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1992                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
1993                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
1994
1995                 // fourth pass
1996                 memset(&m, 0, sizeof(m));
1997                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1998                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
1999                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2000                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2001                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2002                 R_Mesh_TextureState(&m);
2003                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2004                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2005
2006                 // fifth pass
2007                 memset(&m, 0, sizeof(m));
2008                 m.tex[0] = R_GetTexture(glosstexture);
2009                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2010                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2011                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2012                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2013                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2014                 {
2015                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2016                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2017                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2018                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2019                         m.texmatrix[1] = rsurface.entitytolight;
2020                 }
2021                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2022         }
2023         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && rsurface.rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
2024         {
2025                 // 2/0/0/2 3D combine blendsquare path
2026                 memset(&m, 0, sizeof(m));
2027                 m.tex[0] = R_GetTexture(normalmaptexture);
2028                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2029                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2030                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2031                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2032                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2033                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2034                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2035                 m.pointer_texcoord_bufferobject[1] = 0;
2036                 m.pointer_texcoord_bufferoffset[1] = 0;
2037                 R_Mesh_TextureState(&m);
2038                 GL_ColorMask(0,0,0,1);
2039                 // this squares the result
2040                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2041                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2042
2043                 // second and third pass
2044                 R_Mesh_ResetTextureState();
2045                 // square alpha in framebuffer a few times to make it shiny
2046                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2047                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2048                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2049
2050                 // fourth pass
2051                 memset(&m, 0, sizeof(m));
2052                 m.tex[0] = R_GetTexture(glosstexture);
2053                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2054                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2055                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2056                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2057                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
2058                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2059                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2060                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2061                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2062                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2063         }
2064         else
2065         {
2066                 // 2/0/0/2/2 2D combine blendsquare path
2067                 memset(&m, 0, sizeof(m));
2068                 m.tex[0] = R_GetTexture(normalmaptexture);
2069                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2070                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2071                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2072                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2073                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
2074                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
2075                 m.pointer_texcoord3f[1] = rsurface.array_texcoord3f;
2076                 m.pointer_texcoord_bufferobject[1] = 0;
2077                 m.pointer_texcoord_bufferoffset[1] = 0;
2078                 R_Mesh_TextureState(&m);
2079                 GL_ColorMask(0,0,0,1);
2080                 // this squares the result
2081                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
2082                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2083
2084                 // second and third pass
2085                 R_Mesh_ResetTextureState();
2086                 // square alpha in framebuffer a few times to make it shiny
2087                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
2088                 for (glossexponent = 2;glossexponent * 2 <= r_shadow_glossexponent.value;glossexponent *= 2)
2089                         R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2090
2091                 // fourth pass
2092                 memset(&m, 0, sizeof(m));
2093                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
2094                 m.pointer_texcoord3f[0] = rsurface.vertex3f;
2095                 m.pointer_texcoord_bufferobject[0] = rsurface.vertex3f_bufferobject;
2096                 m.pointer_texcoord_bufferoffset[0] = rsurface.vertex3f_bufferoffset;
2097                 m.texmatrix[0] = rsurface.entitytoattenuationxyz;
2098                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2099                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2100                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2101                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2102                 m.texmatrix[1] = rsurface.entitytoattenuationz;
2103                 R_Mesh_TextureState(&m);
2104                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
2105                 R_Mesh_Draw(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2106
2107                 // fifth pass
2108                 memset(&m, 0, sizeof(m));
2109                 m.tex[0] = R_GetTexture(glosstexture);
2110                 m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2111                 m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2112                 m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2113                 m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2114                 if (rsurface.rtlight->currentcubemap != r_texture_whitecube)
2115                 {
2116                         m.texcubemap[1] = R_GetTexture(rsurface.rtlight->currentcubemap);
2117                         m.pointer_texcoord3f[1] = rsurface.vertex3f;
2118                         m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2119                         m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2120                         m.texmatrix[1] = rsurface.entitytolight;
2121                 }
2122                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
2123         }
2124         // this final code is shared
2125         R_Mesh_TextureState(&m);
2126         R_Shadow_RenderLighting_Light_Dot3_Finalize(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase[0] * colorscale, lightcolorbase[1] * colorscale, lightcolorbase[2] * colorscale);
2127 }
2128
2129 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)
2130 {
2131         // ARB path (any Geforce, any Radeon)
2132         qboolean doambient = ambientscale > 0;
2133         qboolean dodiffuse = diffusescale > 0;
2134         qboolean dospecular = specularscale > 0;
2135         if (!doambient && !dodiffuse && !dospecular)
2136                 return;
2137         R_Mesh_ColorPointer(NULL, 0, 0);
2138         if (doambient)
2139                 R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, ambientscale * r_refdef.view.colorscale);
2140         if (dodiffuse)
2141                 R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, basetexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2142         if (dopants)
2143         {
2144                 if (doambient)
2145                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, ambientscale * r_refdef.view.colorscale);
2146                 if (dodiffuse)
2147                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorpants, pantstexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2148         }
2149         if (doshirt)
2150         {
2151                 if (doambient)
2152                         R_Shadow_RenderLighting_Light_Dot3_AmbientPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, ambientscale * r_refdef.view.colorscale);
2153                 if (dodiffuse)
2154                         R_Shadow_RenderLighting_Light_Dot3_DiffusePass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorshirt, shirttexture, normalmaptexture, diffusescale * r_refdef.view.colorscale);
2155         }
2156         if (dospecular)
2157                 R_Shadow_RenderLighting_Light_Dot3_SpecularPass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, lightcolorbase, glosstexture, normalmaptexture, specularscale * r_refdef.view.colorscale);
2158 }
2159
2160 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)
2161 {
2162         int renders;
2163         int i;
2164         int stop;
2165         int newfirstvertex;
2166         int newlastvertex;
2167         int newnumtriangles;
2168         int *newe;
2169         const int *e;
2170         float *c;
2171         int newelements[4096*3];
2172         R_Shadow_RenderLighting_Light_Vertex_Shading(firstvertex, numvertices, numtriangles, element3i, diffusecolor2, ambientcolor2);
2173         for (renders = 0;renders < 64;renders++)
2174         {
2175                 stop = true;
2176                 newfirstvertex = 0;
2177                 newlastvertex = 0;
2178                 newnumtriangles = 0;
2179                 newe = newelements;
2180                 // due to low fillrate on the cards this vertex lighting path is
2181                 // designed for, we manually cull all triangles that do not
2182                 // contain a lit vertex
2183                 // this builds batches of triangles from multiple surfaces and
2184                 // renders them at once
2185                 for (i = 0, e = element3i;i < numtriangles;i++, e += 3)
2186                 {
2187                         if (VectorLength2(rsurface.array_color4f + e[0] * 4) + VectorLength2(rsurface.array_color4f + e[1] * 4) + VectorLength2(rsurface.array_color4f + e[2] * 4) >= 0.01)
2188                         {
2189                                 if (newnumtriangles)
2190                                 {
2191                                         newfirstvertex = min(newfirstvertex, e[0]);
2192                                         newlastvertex  = max(newlastvertex, e[0]);
2193                                 }
2194                                 else
2195                                 {
2196                                         newfirstvertex = e[0];
2197                                         newlastvertex = e[0];
2198                                 }
2199                                 newfirstvertex = min(newfirstvertex, e[1]);
2200                                 newlastvertex  = max(newlastvertex, e[1]);
2201                                 newfirstvertex = min(newfirstvertex, e[2]);
2202                                 newlastvertex  = max(newlastvertex, e[2]);
2203                                 newe[0] = e[0];
2204                                 newe[1] = e[1];
2205                                 newe[2] = e[2];
2206                                 newnumtriangles++;
2207                                 newe += 3;
2208                                 if (newnumtriangles >= (int)(sizeof(newelements)/sizeof(float[3])))
2209                                 {
2210                                         R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2211                                         newnumtriangles = 0;
2212                                         newe = newelements;
2213                                         stop = false;
2214                                 }
2215                         }
2216                 }
2217                 if (newnumtriangles >= 1)
2218                 {
2219                         // if all triangles are included, use the original array to take advantage of the bufferobject if possible
2220                         if (newnumtriangles == numtriangles)
2221                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset);
2222                         else
2223                                 R_Mesh_Draw(newfirstvertex, newlastvertex - newfirstvertex + 1, newnumtriangles, newelements, 0, 0);
2224                         stop = false;
2225                 }
2226                 // if we couldn't find any lit triangles, exit early
2227                 if (stop)
2228                         break;
2229                 // now reduce the intensity for the next overbright pass
2230                 // we have to clamp to 0 here incase the drivers have improper
2231                 // handling of negative colors
2232                 // (some old drivers even have improper handling of >1 color)
2233                 stop = true;
2234                 for (i = 0, c = rsurface.array_color4f + 4 * firstvertex;i < numvertices;i++, c += 4)
2235                 {
2236                         if (c[0] > 1 || c[1] > 1 || c[2] > 1)
2237                         {
2238                                 c[0] = max(0, c[0] - 1);
2239                                 c[1] = max(0, c[1] - 1);
2240                                 c[2] = max(0, c[2] - 1);
2241                                 stop = false;
2242                         }
2243                         else
2244                                 VectorClear(c);
2245                 }
2246                 // another check...
2247                 if (stop)
2248                         break;
2249         }
2250 }
2251
2252 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)
2253 {
2254         // OpenGL 1.1 path (anything)
2255         float ambientcolorbase[3], diffusecolorbase[3];
2256         float ambientcolorpants[3], diffusecolorpants[3];
2257         float ambientcolorshirt[3], diffusecolorshirt[3];
2258         rmeshstate_t m;
2259         VectorScale(lightcolorbase, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorbase);
2260         VectorScale(lightcolorbase, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorbase);
2261         VectorScale(lightcolorpants, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorpants);
2262         VectorScale(lightcolorpants, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorpants);
2263         VectorScale(lightcolorshirt, ambientscale * 2 * r_refdef.view.colorscale, ambientcolorshirt);
2264         VectorScale(lightcolorshirt, diffusescale * 2 * r_refdef.view.colorscale, diffusecolorshirt);
2265         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
2266         R_Mesh_ColorPointer(rsurface.array_color4f, 0, 0);
2267         memset(&m, 0, sizeof(m));
2268         m.tex[0] = R_GetTexture(basetexture);
2269         m.texmatrix[0] = rsurface.texture->currenttexmatrix;
2270         m.pointer_texcoord[0] = rsurface.texcoordtexture2f;
2271         m.pointer_texcoord_bufferobject[0] = rsurface.texcoordtexture2f_bufferobject;
2272         m.pointer_texcoord_bufferoffset[0] = rsurface.texcoordtexture2f_bufferoffset;
2273         if (r_textureunits.integer >= 2)
2274         {
2275                 // voodoo2 or TNT
2276                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
2277                 m.texmatrix[1] = rsurface.entitytoattenuationxyz;
2278                 m.pointer_texcoord3f[1] = rsurface.vertex3f;
2279                 m.pointer_texcoord_bufferobject[1] = rsurface.vertex3f_bufferobject;
2280                 m.pointer_texcoord_bufferoffset[1] = rsurface.vertex3f_bufferoffset;
2281                 if (r_textureunits.integer >= 3)
2282                 {
2283                         // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
2284                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
2285                         m.texmatrix[2] = rsurface.entitytoattenuationz;
2286                         m.pointer_texcoord3f[2] = rsurface.vertex3f;
2287                         m.pointer_texcoord_bufferobject[2] = rsurface.vertex3f_bufferobject;
2288                         m.pointer_texcoord_bufferoffset[2] = rsurface.vertex3f_bufferoffset;
2289                 }
2290         }
2291         R_Mesh_TextureState(&m);
2292         //R_Mesh_TexBind(0, R_GetTexture(basetexture));
2293         R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorbase, ambientcolorbase);
2294         if (dopants)
2295         {
2296                 R_Mesh_TexBind(0, R_GetTexture(pantstexture));
2297                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorpants, ambientcolorpants);
2298         }
2299         if (doshirt)
2300         {
2301                 R_Mesh_TexBind(0, R_GetTexture(shirttexture));
2302                 R_Shadow_RenderLighting_Light_Vertex_Pass(firstvertex, numvertices, numtriangles, element3i, element3i_bufferobject, element3i_bufferoffset, diffusecolorshirt, ambientcolorshirt);
2303         }
2304 }
2305
2306 extern cvar_t gl_lightmaps;
2307 void R_Shadow_RenderLighting(int firstvertex, int numvertices, int numtriangles, const int *element3i, int element3i_bufferobject, size_t element3i_bufferoffset)
2308 {
2309         float ambientscale, diffusescale, specularscale;
2310         vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
2311         rtexture_t *nmap;
2312         // calculate colors to render this texture with
2313         lightcolorbase[0] = rsurface.rtlight->currentcolor[0] * rsurface.texture->dlightcolor[0];
2314         lightcolorbase[1] = rsurface.rtlight->currentcolor[1] * rsurface.texture->dlightcolor[1];
2315         lightcolorbase[2] = rsurface.rtlight->currentcolor[2] * rsurface.texture->dlightcolor[2];
2316         ambientscale = rsurface.rtlight->ambientscale;
2317         diffusescale = rsurface.rtlight->diffusescale;
2318         specularscale = rsurface.rtlight->specularscale * rsurface.texture->specularscale;
2319         if (!r_shadow_usenormalmap.integer)
2320         {
2321                 ambientscale += 1.0f * diffusescale;
2322                 diffusescale = 0;
2323                 specularscale = 0;
2324         }
2325         if ((ambientscale + diffusescale) * VectorLength2(lightcolorbase) + specularscale * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2326                 return;
2327         RSurf_SetupDepthAndCulling();
2328         nmap = rsurface.texture->currentskinframe->nmap;
2329         if (gl_lightmaps.integer)
2330                 nmap = r_texture_blanknormalmap;
2331         if (rsurface.texture->colormapping && !gl_lightmaps.integer)
2332         {
2333                 qboolean dopants = rsurface.texture->currentskinframe->pants != NULL && VectorLength2(rsurface.colormap_pantscolor) >= (1.0f / 1048576.0f);
2334                 qboolean doshirt = rsurface.texture->currentskinframe->shirt != NULL && VectorLength2(rsurface.colormap_shirtcolor) >= (1.0f / 1048576.0f);
2335                 if (dopants)
2336                 {
2337                         lightcolorpants[0] = lightcolorbase[0] * rsurface.colormap_pantscolor[0];
2338                         lightcolorpants[1] = lightcolorbase[1] * rsurface.colormap_pantscolor[1];
2339                         lightcolorpants[2] = lightcolorbase[2] * rsurface.colormap_pantscolor[2];
2340                 }
2341                 else
2342                         VectorClear(lightcolorpants);
2343                 if (doshirt)
2344                 {
2345                         lightcolorshirt[0] = lightcolorbase[0] * rsurface.colormap_shirtcolor[0];
2346                         lightcolorshirt[1] = lightcolorbase[1] * rsurface.colormap_shirtcolor[1];
2347                         lightcolorshirt[2] = lightcolorbase[2] * rsurface.colormap_shirtcolor[2];
2348                 }
2349                 else
2350                         VectorClear(lightcolorshirt);
2351                 switch (r_shadow_rendermode)
2352                 {
2353                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2354                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2355                         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);
2356                         break;
2357                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2358                         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);
2359                         break;
2360                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2361                         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);
2362                         break;
2363                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2364                         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);
2365                         break;
2366                 default:
2367                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2368                         break;
2369                 }
2370         }
2371         else
2372         {
2373                 switch (r_shadow_rendermode)
2374                 {
2375                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2376                         GL_DepthTest(!(rsurface.texture->currentmaterialflags & MATERIALFLAG_NODEPTHTEST) && !r_showdisabledepthtest.integer);
2377                         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);
2378                         break;
2379                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2380                         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);
2381                         break;
2382                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2383                         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);
2384                         break;
2385                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2386                         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);
2387                         break;
2388                 default:
2389                         Con_Printf("R_Shadow_RenderLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2390                         break;
2391                 }
2392         }
2393 }
2394
2395 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)
2396 {
2397         matrix4x4_t tempmatrix = *matrix;
2398         Matrix4x4_Scale(&tempmatrix, r_shadow_lightradiusscale.value, 1);
2399
2400         // if this light has been compiled before, free the associated data
2401         R_RTLight_Uncompile(rtlight);
2402
2403         // clear it completely to avoid any lingering data
2404         memset(rtlight, 0, sizeof(*rtlight));
2405
2406         // copy the properties
2407         rtlight->matrix_lighttoworld = tempmatrix;
2408         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &tempmatrix);
2409         Matrix4x4_OriginFromMatrix(&tempmatrix, rtlight->shadoworigin);
2410         rtlight->radius = Matrix4x4_ScaleFromMatrix(&tempmatrix);
2411         VectorCopy(color, rtlight->color);
2412         rtlight->cubemapname[0] = 0;
2413         if (cubemapname && cubemapname[0])
2414                 strlcpy(rtlight->cubemapname, cubemapname, sizeof(rtlight->cubemapname));
2415         rtlight->shadow = shadow;
2416         rtlight->corona = corona;
2417         rtlight->style = style;
2418         rtlight->isstatic = isstatic;
2419         rtlight->coronasizescale = coronasizescale;
2420         rtlight->ambientscale = ambientscale;
2421         rtlight->diffusescale = diffusescale;
2422         rtlight->specularscale = specularscale;
2423         rtlight->flags = flags;
2424
2425         // compute derived data
2426         //rtlight->cullradius = rtlight->radius;
2427         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2428         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2429         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2430         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2431         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2432         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2433         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2434 }
2435
2436 // compiles rtlight geometry
2437 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2438 void R_RTLight_Compile(rtlight_t *rtlight)
2439 {
2440         int i;
2441         int numsurfaces, numleafs, numleafpvsbytes, numshadowtrispvsbytes, numlighttrispvsbytes;
2442         int lighttris, shadowtris, shadowmeshes, shadowmeshtris;
2443         entity_render_t *ent = r_refdef.scene.worldentity;
2444         model_t *model = r_refdef.scene.worldmodel;
2445         unsigned char *data;
2446
2447         // compile the light
2448         rtlight->compiled = true;
2449         rtlight->static_numleafs = 0;
2450         rtlight->static_numleafpvsbytes = 0;
2451         rtlight->static_leaflist = NULL;
2452         rtlight->static_leafpvs = NULL;
2453         rtlight->static_numsurfaces = 0;
2454         rtlight->static_surfacelist = NULL;
2455         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2456         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2457         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2458         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2459         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2460         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2461
2462         if (model && model->GetLightInfo)
2463         {
2464                 // this variable must be set for the CompileShadowVolume code
2465                 r_shadow_compilingrtlight = rtlight;
2466                 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);
2467                 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);
2468                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2469                 numshadowtrispvsbytes = ((model->brush.shadowmesh ? model->brush.shadowmesh->numtriangles : model->surfmesh.num_triangles) + 7) >> 3;
2470                 numlighttrispvsbytes = (model->surfmesh.num_triangles + 7) >> 3;
2471                 data = (unsigned char *)Mem_Alloc(r_main_mempool, sizeof(int) * numsurfaces + sizeof(int) * numleafs + numleafpvsbytes + numshadowtrispvsbytes + numlighttrispvsbytes);
2472                 rtlight->static_numsurfaces = numsurfaces;
2473                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2474                 rtlight->static_numleafs = numleafs;
2475                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2476                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2477                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2478                 rtlight->static_numshadowtrispvsbytes = numshadowtrispvsbytes;
2479                 rtlight->static_shadowtrispvs = (unsigned char *)data;data += numshadowtrispvsbytes;
2480                 rtlight->static_numlighttrispvsbytes = numlighttrispvsbytes;
2481                 rtlight->static_lighttrispvs = (unsigned char *)data;data += numlighttrispvsbytes;
2482                 if (rtlight->static_numsurfaces)
2483                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2484                 if (rtlight->static_numleafs)
2485                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2486                 if (rtlight->static_numleafpvsbytes)
2487                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2488                 if (rtlight->static_numshadowtrispvsbytes)
2489                         memcpy(rtlight->static_shadowtrispvs, r_shadow_buffer_shadowtrispvs, rtlight->static_numshadowtrispvsbytes);
2490                 if (rtlight->static_numlighttrispvsbytes)
2491                         memcpy(rtlight->static_lighttrispvs, r_shadow_buffer_lighttrispvs, rtlight->static_numlighttrispvsbytes);
2492                 if (model->CompileShadowVolume && rtlight->shadow)
2493                         model->CompileShadowVolume(ent, rtlight->shadoworigin, NULL, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2494                 // now we're done compiling the rtlight
2495                 r_shadow_compilingrtlight = NULL;
2496         }
2497
2498
2499         // use smallest available cullradius - box radius or light radius
2500         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2501         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2502
2503         shadowmeshes = 0;
2504         shadowmeshtris = 0;
2505         if (rtlight->static_meshchain_shadow)
2506         {
2507                 shadowmesh_t *mesh;
2508                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2509                 {
2510                         shadowmeshes++;
2511                         shadowmeshtris += mesh->numtriangles;
2512                 }
2513         }
2514
2515         lighttris = 0;
2516         if (rtlight->static_numlighttrispvsbytes)
2517                 for (i = 0;i < rtlight->static_numlighttrispvsbytes*8;i++)
2518                         if (CHECKPVSBIT(rtlight->static_lighttrispvs, i))
2519                                 lighttris++;
2520
2521         shadowtris = 0;
2522         if (rtlight->static_numlighttrispvsbytes)
2523                 for (i = 0;i < rtlight->static_numshadowtrispvsbytes*8;i++)
2524                         if (CHECKPVSBIT(rtlight->static_shadowtrispvs, i))
2525                                 shadowtris++;
2526
2527         if (developer.integer >= 10)
2528                 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);
2529 }
2530
2531 void R_RTLight_Uncompile(rtlight_t *rtlight)
2532 {
2533         if (rtlight->compiled)
2534         {
2535                 if (rtlight->static_meshchain_shadow)
2536                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2537                 rtlight->static_meshchain_shadow = NULL;
2538                 // these allocations are grouped
2539                 if (rtlight->static_surfacelist)
2540                         Mem_Free(rtlight->static_surfacelist);
2541                 rtlight->static_numleafs = 0;
2542                 rtlight->static_numleafpvsbytes = 0;
2543                 rtlight->static_leaflist = NULL;
2544                 rtlight->static_leafpvs = NULL;
2545                 rtlight->static_numsurfaces = 0;
2546                 rtlight->static_surfacelist = NULL;
2547                 rtlight->static_numshadowtrispvsbytes = 0;
2548                 rtlight->static_shadowtrispvs = NULL;
2549                 rtlight->static_numlighttrispvsbytes = 0;
2550                 rtlight->static_lighttrispvs = NULL;
2551                 rtlight->compiled = false;
2552         }
2553 }
2554
2555 void R_Shadow_UncompileWorldLights(void)
2556 {
2557         size_t lightindex;
2558         dlight_t *light;
2559         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
2560         {
2561                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
2562                 if (!light)
2563                         continue;
2564                 R_RTLight_Uncompile(&light->rtlight);
2565         }
2566 }
2567
2568 void R_Shadow_ComputeShadowCasterCullingPlanes(rtlight_t *rtlight)
2569 {
2570         int i, j;
2571         mplane_t plane;
2572         // reset the count of frustum planes
2573         // see rsurface.rtlight_frustumplanes definition for how much this array
2574         // can hold
2575         rsurface.rtlight_numfrustumplanes = 0;
2576
2577         // haven't implemented a culling path for ortho rendering
2578         if (!r_refdef.view.useperspective)
2579         {
2580                 // check if the light is on screen and copy the 4 planes if it is
2581                 for (i = 0;i < 4;i++)
2582                         if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2583                                 break;
2584                 if (i == 4)
2585                         for (i = 0;i < 4;i++)
2586                                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2587                 return;
2588         }
2589
2590 #if 1
2591         // generate a deformed frustum that includes the light origin, this is
2592         // used to cull shadow casting surfaces that can not possibly cast a
2593         // shadow onto the visible light-receiving surfaces, which can be a
2594         // performance gain
2595         //
2596         // if the light origin is onscreen the result will be 4 planes exactly
2597         // if the light origin is offscreen on only one axis the result will
2598         // be exactly 5 planes (split-side case)
2599         // if the light origin is offscreen on two axes the result will be
2600         // exactly 4 planes (stretched corner case)
2601         for (i = 0;i < 4;i++)
2602         {
2603                 // quickly reject standard frustum planes that put the light
2604                 // origin outside the frustum
2605                 if (PlaneDiff(rtlight->shadoworigin, &r_refdef.view.frustum[i]) < -0.03125)
2606                         continue;
2607                 // copy the plane
2608                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = r_refdef.view.frustum[i];
2609         }
2610         // if all the standard frustum planes were accepted, the light is onscreen
2611         // otherwise we need to generate some more planes below...
2612         if (rsurface.rtlight_numfrustumplanes < 4)
2613         {
2614                 // at least one of the stock frustum planes failed, so we need to
2615                 // create one or two custom planes to enclose the light origin
2616                 for (i = 0;i < 4;i++)
2617                 {
2618                         // create a plane using the view origin and light origin, and a
2619                         // single point from the frustum corner set
2620                         TriangleNormal(r_refdef.view.origin, r_refdef.view.frustumcorner[i], rtlight->shadoworigin, plane.normal);
2621                         VectorNormalize(plane.normal);
2622                         plane.dist = DotProduct(r_refdef.view.origin, plane.normal);
2623                         // see if this plane is backwards and flip it if so
2624                         for (j = 0;j < 4;j++)
2625                                 if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2626                                         break;
2627                         if (j < 4)
2628                         {
2629                                 VectorNegate(plane.normal, plane.normal);
2630                                 plane.dist *= -1;
2631                                 // flipped plane, test again to see if it is now valid
2632                                 for (j = 0;j < 4;j++)
2633                                         if (j != i && DotProduct(r_refdef.view.frustumcorner[j], plane.normal) - plane.dist < -0.03125)
2634                                                 break;
2635                                 // if the plane is still not valid, then it is dividing the
2636                                 // frustum and has to be rejected
2637                                 if (j < 4)
2638                                         continue;
2639                         }
2640                         // we have created a valid plane, compute extra info
2641                         PlaneClassify(&plane);
2642                         // copy the plane
2643                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2644 #if 1
2645                         // if we've found 5 frustum planes then we have constructed a
2646                         // proper split-side case and do not need to keep searching for
2647                         // planes to enclose the light origin
2648                         if (rsurface.rtlight_numfrustumplanes == 5)
2649                                 break;
2650 #endif
2651                 }
2652         }
2653 #endif
2654
2655 #if 0
2656         for (i = 0;i < rsurface.rtlight_numfrustumplanes;i++)
2657         {
2658                 plane = rsurface.rtlight_frustumplanes[i];
2659                 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));
2660         }
2661 #endif
2662
2663 #if 0
2664         // now add the light-space box planes if the light box is rotated, as any
2665         // caster outside the oriented light box is irrelevant (even if it passed
2666         // the worldspace light box, which is axial)
2667         if (rtlight->matrix_lighttoworld.m[0][0] != 1 || rtlight->matrix_lighttoworld.m[1][1] != 1 || rtlight->matrix_lighttoworld.m[2][2] != 1)
2668         {
2669                 for (i = 0;i < 6;i++)
2670                 {
2671                         vec3_t v;
2672                         VectorClear(v);
2673                         v[i >> 1] = (i & 1) ? -1 : 1;
2674                         Matrix4x4_Transform(&rtlight->matrix_lighttoworld, v, plane.normal);
2675                         VectorSubtract(plane.normal, rtlight->shadoworigin, plane.normal);
2676                         plane.dist = VectorNormalizeLength(plane.normal);
2677                         plane.dist += DotProduct(plane.normal, rtlight->shadoworigin);
2678                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2679                 }
2680         }
2681 #endif
2682
2683 #if 0
2684         // add the world-space reduced box planes
2685         for (i = 0;i < 6;i++)
2686         {
2687                 VectorClear(plane.normal);
2688                 plane.normal[i >> 1] = (i & 1) ? -1 : 1;
2689                 plane.dist = (i & 1) ? -rsurface.rtlight_cullmaxs[i >> 1] : rsurface.rtlight_cullmins[i >> 1];
2690                 rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = plane;
2691         }
2692 #endif
2693
2694 #if 0
2695         {
2696         int j, oldnum;
2697         vec3_t points[8];
2698         vec_t bestdist;
2699         // reduce all plane distances to tightly fit the rtlight cull box, which
2700         // is in worldspace
2701         VectorSet(points[0], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2702         VectorSet(points[1], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmins[2]);
2703         VectorSet(points[2], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2704         VectorSet(points[3], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmins[2]);
2705         VectorSet(points[4], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2706         VectorSet(points[5], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmins[1], rsurface.rtlight_cullmaxs[2]);
2707         VectorSet(points[6], rsurface.rtlight_cullmins[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2708         VectorSet(points[7], rsurface.rtlight_cullmaxs[0], rsurface.rtlight_cullmaxs[1], rsurface.rtlight_cullmaxs[2]);
2709         oldnum = rsurface.rtlight_numfrustumplanes;
2710         rsurface.rtlight_numfrustumplanes = 0;
2711         for (j = 0;j < oldnum;j++)
2712         {
2713                 // find the nearest point on the box to this plane
2714                 bestdist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[0]);
2715                 for (i = 1;i < 8;i++)
2716                 {
2717                         dist = DotProduct(rsurface.rtlight_frustumplanes[j].normal, points[i]);
2718                         if (bestdist > dist)
2719                                 bestdist = dist;
2720                 }
2721                 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);
2722                 // if the nearest point is near or behind the plane, we want this
2723                 // plane, otherwise the plane is useless as it won't cull anything
2724                 if (rsurface.rtlight_frustumplanes[j].dist < bestdist + 0.03125)
2725                 {
2726                         PlaneClassify(&rsurface.rtlight_frustumplanes[j]);
2727                         rsurface.rtlight_frustumplanes[rsurface.rtlight_numfrustumplanes++] = rsurface.rtlight_frustumplanes[j];
2728                 }
2729         }
2730         }
2731 #endif
2732 }
2733
2734 void R_Shadow_DrawWorldShadow(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2735 {
2736         RSurf_ActiveWorldEntity();
2737         if (rsurface.rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2738         {
2739                 shadowmesh_t *mesh;
2740                 CHECKGLERROR
2741                 for (mesh = rsurface.rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2742                 {
2743                         r_refdef.stats.lights_shadowtriangles += mesh->numtriangles;
2744                         R_Mesh_VertexPointer(mesh->vertex3f, mesh->vbo, mesh->vbooffset_vertex3f);
2745                         GL_LockArrays(0, mesh->numverts);
2746                         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2747                         {
2748                                 // decrement stencil if backface is behind depthbuffer
2749                                 GL_CullFace(r_refdef.view.cullface_front);
2750                                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);CHECKGLERROR
2751                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2752                                 // increment stencil if frontface is behind depthbuffer
2753                                 GL_CullFace(r_refdef.view.cullface_back);
2754                                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);CHECKGLERROR
2755                         }
2756                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->ebo, 0);
2757                         GL_LockArrays(0, 0);
2758                 }
2759                 CHECKGLERROR
2760         }
2761         else if (numsurfaces && r_refdef.scene.worldmodel->brush.shadowmesh && r_shadow_culltriangles.integer)
2762         {
2763                 int t, tend;
2764                 int surfacelistindex;
2765                 msurface_t *surface;
2766                 R_Shadow_PrepareShadowMark(r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles);
2767                 for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
2768                 {
2769                         surface = r_refdef.scene.worldmodel->data_surfaces + surfacelist[surfacelistindex];
2770                         for (t = surface->num_firstshadowmeshtriangle, tend = t + surface->num_triangles;t < tend;t++)
2771                                 if (CHECKPVSBIT(trispvs, t))
2772                                         shadowmarklist[numshadowmark++] = t;
2773                 }
2774                 R_Shadow_VolumeFromList(r_refdef.scene.worldmodel->brush.shadowmesh->numverts, r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles, r_refdef.scene.worldmodel->brush.shadowmesh->vertex3f, r_refdef.scene.worldmodel->brush.shadowmesh->element3i, r_refdef.scene.worldmodel->brush.shadowmesh->neighbor3i, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius + r_refdef.scene.worldmodel->radius*2 + r_shadow_projectdistance.value, numshadowmark, shadowmarklist);
2775         }
2776         else if (numsurfaces)
2777                 r_refdef.scene.worldmodel->DrawShadowVolume(r_refdef.scene.worldentity, rsurface.rtlight->shadoworigin, NULL, rsurface.rtlight->radius, numsurfaces, surfacelist, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs);
2778 }
2779
2780 void R_Shadow_DrawEntityShadow(entity_render_t *ent)
2781 {
2782         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2783         vec_t relativeshadowradius;
2784         RSurf_ActiveModelEntity(ent, false, false);
2785         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, relativeshadoworigin);
2786         relativeshadowradius = rsurface.rtlight->radius / ent->scale;
2787         relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2788         relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2789         relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2790         relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2791         relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2792         relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2793         ent->model->DrawShadowVolume(ent, relativeshadoworigin, NULL, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2794 }
2795
2796 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2797 {
2798         // set up properties for rendering light onto this entity
2799         RSurf_ActiveModelEntity(ent, true, true);
2800         Matrix4x4_Concat(&rsurface.entitytolight, &rsurface.rtlight->matrix_worldtolight, &ent->matrix);
2801         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2802         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2803         Matrix4x4_Transform(&ent->inversematrix, rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2804         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2805                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2806 }
2807
2808 void R_Shadow_DrawWorldLight(int numsurfaces, int *surfacelist, const unsigned char *trispvs)
2809 {
2810         if (!r_refdef.scene.worldmodel->DrawLight)
2811                 return;
2812
2813         // set up properties for rendering light onto this entity
2814         RSurf_ActiveWorldEntity();
2815         rsurface.entitytolight = rsurface.rtlight->matrix_worldtolight;
2816         Matrix4x4_Concat(&rsurface.entitytoattenuationxyz, &matrix_attenuationxyz, &rsurface.entitytolight);
2817         Matrix4x4_Concat(&rsurface.entitytoattenuationz, &matrix_attenuationz, &rsurface.entitytolight);
2818         VectorCopy(rsurface.rtlight->shadoworigin, rsurface.entitylightorigin);
2819         if (r_shadow_lightingrendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
2820                 R_Mesh_TexMatrix(3, &rsurface.entitytolight);
2821
2822         r_refdef.scene.worldmodel->DrawLight(r_refdef.scene.worldentity, numsurfaces, surfacelist, trispvs);
2823 }
2824
2825 void R_Shadow_DrawEntityLight(entity_render_t *ent)
2826 {
2827         model_t *model = ent->model;
2828         if (!model->DrawLight)
2829                 return;
2830
2831         R_Shadow_SetupEntityLight(ent);
2832
2833         model->DrawLight(ent, model->nummodelsurfaces, model->surfacelist, NULL);
2834 }
2835
2836 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2837 {
2838         int i;
2839         float f;
2840         int numleafs, numsurfaces;
2841         int *leaflist, *surfacelist;
2842         unsigned char *leafpvs, *shadowtrispvs, *lighttrispvs;
2843         int numlightentities;
2844         int numlightentities_noselfshadow;
2845         int numshadowentities;
2846         int numshadowentities_noselfshadow;
2847         entity_render_t *lightentities[MAX_EDICTS];
2848         entity_render_t *lightentities_noselfshadow[MAX_EDICTS];
2849         entity_render_t *shadowentities[MAX_EDICTS];
2850         entity_render_t *shadowentities_noselfshadow[MAX_EDICTS];
2851
2852         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2853         // skip lights that are basically invisible (color 0 0 0)
2854         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2855                 return;
2856
2857         // loading is done before visibility checks because loading should happen
2858         // all at once at the start of a level, not when it stalls gameplay.
2859         // (especially important to benchmarks)
2860         // compile light
2861         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2862                 R_RTLight_Compile(rtlight);
2863         // load cubemap
2864         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2865
2866         // look up the light style value at this time
2867         f = (rtlight->style >= 0 ? r_refdef.scene.rtlightstylevalue[rtlight->style] : 1) * r_shadow_lightintensityscale.value;
2868         VectorScale(rtlight->color, f, rtlight->currentcolor);
2869         /*
2870         if (rtlight->selected)
2871         {
2872                 f = 2 + sin(realtime * M_PI * 4.0);
2873                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2874         }
2875         */
2876
2877         // if lightstyle is currently off, don't draw the light
2878         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2879                 return;
2880
2881         // if the light box is offscreen, skip it
2882         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2883                 return;
2884
2885         VectorCopy(rtlight->cullmins, rsurface.rtlight_cullmins);
2886         VectorCopy(rtlight->cullmaxs, rsurface.rtlight_cullmaxs);
2887
2888         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2889         {
2890                 // compiled light, world available and can receive realtime lighting
2891                 // retrieve leaf information
2892                 numleafs = rtlight->static_numleafs;
2893                 leaflist = rtlight->static_leaflist;
2894                 leafpvs = rtlight->static_leafpvs;
2895                 numsurfaces = rtlight->static_numsurfaces;
2896                 surfacelist = rtlight->static_surfacelist;
2897                 shadowtrispvs = rtlight->static_shadowtrispvs;
2898                 lighttrispvs = rtlight->static_lighttrispvs;
2899         }
2900         else if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->GetLightInfo)
2901         {
2902                 // dynamic light, world available and can receive realtime lighting
2903                 // calculate lit surfaces and leafs
2904                 R_Shadow_EnlargeLeafSurfaceTrisBuffer(r_refdef.scene.worldmodel->brush.num_leafs, r_refdef.scene.worldmodel->num_surfaces, r_refdef.scene.worldmodel->brush.shadowmesh ? r_refdef.scene.worldmodel->brush.shadowmesh->numtriangles : r_refdef.scene.worldmodel->surfmesh.num_triangles, r_refdef.scene.worldmodel->surfmesh.num_triangles);
2905                 r_refdef.scene.worldmodel->GetLightInfo(r_refdef.scene.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);
2906                 leaflist = r_shadow_buffer_leaflist;
2907                 leafpvs = r_shadow_buffer_leafpvs;
2908                 surfacelist = r_shadow_buffer_surfacelist;
2909                 shadowtrispvs = r_shadow_buffer_shadowtrispvs;
2910                 lighttrispvs = r_shadow_buffer_lighttrispvs;
2911                 // if the reduced leaf bounds are offscreen, skip it
2912                 if (R_CullBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2913                         return;
2914         }
2915         else
2916         {
2917                 // no world
2918                 numleafs = 0;
2919                 leaflist = NULL;
2920                 leafpvs = NULL;
2921                 numsurfaces = 0;
2922                 surfacelist = NULL;
2923                 shadowtrispvs = NULL;
2924                 lighttrispvs = NULL;
2925         }
2926         // check if light is illuminating any visible leafs
2927         if (numleafs)
2928         {
2929                 for (i = 0;i < numleafs;i++)
2930                         if (r_refdef.viewcache.world_leafvisible[leaflist[i]])
2931                                 break;
2932                 if (i == numleafs)
2933                         return;
2934         }
2935         // set up a scissor rectangle for this light
2936         if (R_Shadow_ScissorForBBox(rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2937                 return;
2938
2939         R_Shadow_ComputeShadowCasterCullingPlanes(rtlight);
2940
2941         // make a list of lit entities and shadow casting entities
2942         numlightentities = 0;
2943         numlightentities_noselfshadow = 0;
2944         numshadowentities = 0;
2945         numshadowentities_noselfshadow = 0;
2946         // add dynamic entities that are lit by the light
2947         if (r_drawentities.integer)
2948         {
2949                 for (i = 0;i < r_refdef.scene.numentities;i++)
2950                 {
2951                         model_t *model;
2952                         entity_render_t *ent = r_refdef.scene.entities[i];
2953                         vec3_t org;
2954                         if (!BoxesOverlap(ent->mins, ent->maxs, rsurface.rtlight_cullmins, rsurface.rtlight_cullmaxs))
2955                                 continue;
2956                         // skip the object entirely if it is not within the valid
2957                         // shadow-casting region (which includes the lit region)
2958                         if (R_CullBoxCustomPlanes(ent->mins, ent->maxs, rsurface.rtlight_numfrustumplanes, rsurface.rtlight_frustumplanes))
2959                                 continue;
2960                         if (!(model = ent->model))
2961                                 continue;
2962                         if (r_refdef.viewcache.entityvisible[i] && model->DrawLight && (ent->flags & RENDER_LIGHT))
2963                         {
2964                                 // this entity wants to receive light, is visible, and is
2965                                 // inside the light box
2966                                 // TODO: check if the surfaces in the model can receive light
2967                                 // so now check if it's in a leaf seen by the light
2968                                 if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
2969                                         continue;
2970                                 if (ent->flags & RENDER_NOSELFSHADOW)
2971                                         lightentities_noselfshadow[numlightentities_noselfshadow++] = ent;
2972                                 else
2973                                         lightentities[numlightentities++] = ent;
2974                                 // since it is lit, it probably also casts a shadow...
2975                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2976                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
2977                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
2978                                 {
2979                                         // note: exterior models without the RENDER_NOSELFSHADOW
2980                                         // flag still create a RENDER_NOSELFSHADOW shadow but
2981                                         // are lit normally, this means that they are
2982                                         // self-shadowing but do not shadow other
2983                                         // RENDER_NOSELFSHADOW entities such as the gun
2984                                         // (very weird, but keeps the player shadow off the gun)
2985                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
2986                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
2987                                         else
2988                                                 shadowentities[numshadowentities++] = ent;
2989                                 }
2990                         }
2991                         else if (ent->flags & RENDER_SHADOW)
2992                         {
2993                                 // this entity is not receiving light, but may still need to
2994                                 // cast a shadow...
2995                                 // TODO: check if the surfaces in the model can cast shadow
2996                                 // now check if it is in a leaf seen by the light
2997                                 if (r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS && !r_refdef.scene.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.scene.worldmodel, leafpvs, ent->mins, ent->maxs))
2998                                         continue;
2999                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
3000                                 Matrix4x4_OriginFromMatrix(&ent->matrix, org);
3001                                 if ((ent->flags & RENDER_SHADOW) && model->DrawShadowVolume && VectorDistance2(org, rtlight->shadoworigin) > 0.1)
3002                                 {
3003                                         if (ent->flags & (RENDER_NOSELFSHADOW | RENDER_EXTERIORMODEL))
3004                                                 shadowentities_noselfshadow[numshadowentities_noselfshadow++] = ent;
3005                                         else
3006                                                 shadowentities[numshadowentities++] = ent;
3007                                 }
3008                         }
3009                 }
3010         }
3011
3012         // return if there's nothing at all to light
3013         if (!numlightentities && !numsurfaces)
3014                 return;
3015
3016         // don't let sound skip if going slow
3017         if (r_refdef.scene.extraupdate)
3018                 S_ExtraUpdate ();
3019
3020         // make this the active rtlight for rendering purposes
3021         R_Shadow_RenderMode_ActiveLight(rtlight);
3022         // count this light in the r_speeds
3023         r_refdef.stats.lights++;
3024
3025         if (r_showshadowvolumes.integer && r_refdef.view.showdebug && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3026         {
3027                 // optionally draw visible shape of the shadow volumes
3028                 // for performance analysis by level designers
3029                 R_Shadow_RenderMode_VisibleShadowVolumes();
3030                 if (numsurfaces)
3031                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3032                 for (i = 0;i < numshadowentities;i++)
3033                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3034                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3035                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3036         }
3037
3038         if (gl_stencil && numsurfaces + numshadowentities + numshadowentities_noselfshadow && rtlight->shadow && (rtlight->isstatic ? r_refdef.rtworldshadows : r_refdef.rtdlightshadows))
3039         {
3040                 // draw stencil shadow volumes to mask off pixels that are in shadow
3041                 // so that they won't receive lighting
3042                 R_Shadow_RenderMode_StencilShadowVolumes(true);
3043                 if (numsurfaces)
3044                         R_Shadow_DrawWorldShadow(numsurfaces, surfacelist, shadowtrispvs);
3045                 for (i = 0;i < numshadowentities;i++)
3046                         R_Shadow_DrawEntityShadow(shadowentities[i]);
3047                 if (numlightentities_noselfshadow)
3048                 {
3049                         // draw lighting in the unmasked areas
3050                         R_Shadow_RenderMode_Lighting(true, false);
3051                         for (i = 0;i < numlightentities_noselfshadow;i++)
3052                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3053
3054                         // optionally draw the illuminated areas
3055                         // for performance analysis by level designers
3056                         if (r_showlighting.integer && r_refdef.view.showdebug)
3057                         {
3058                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, false);
3059                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3060                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3061                         }
3062
3063                         R_Shadow_RenderMode_StencilShadowVolumes(false);
3064                 }
3065                 for (i = 0;i < numshadowentities_noselfshadow;i++)
3066                         R_Shadow_DrawEntityShadow(shadowentities_noselfshadow[i]);
3067
3068                 if (numsurfaces + numlightentities)
3069                 {
3070                         // draw lighting in the unmasked areas
3071                         R_Shadow_RenderMode_Lighting(true, false);
3072                         if (numsurfaces)
3073                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3074                         for (i = 0;i < numlightentities;i++)
3075                                 R_Shadow_DrawEntityLight(lightentities[i]);
3076
3077                         // optionally draw the illuminated areas
3078                         // for performance analysis by level designers
3079                         if (r_showlighting.integer && r_refdef.view.showdebug)
3080                         {
3081                                 R_Shadow_RenderMode_VisibleLighting(!r_showdisabledepthtest.integer, 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                 }
3088         }
3089         else
3090         {
3091                 if (numsurfaces + numlightentities)
3092                 {
3093                         // draw lighting in the unmasked areas
3094                         R_Shadow_RenderMode_Lighting(false, false);
3095                         if (numsurfaces)
3096                                 R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3097                         for (i = 0;i < numlightentities;i++)
3098                                 R_Shadow_DrawEntityLight(lightentities[i]);
3099                         for (i = 0;i < numlightentities_noselfshadow;i++)
3100                                 R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3101
3102                         // optionally draw the illuminated areas
3103                         // for performance analysis by level designers
3104                         if (r_showlighting.integer && r_refdef.view.showdebug)
3105                         {
3106                                 R_Shadow_RenderMode_VisibleLighting(false, false);
3107                                 if (numsurfaces)
3108                                         R_Shadow_DrawWorldLight(numsurfaces, surfacelist, lighttrispvs);
3109                                 for (i = 0;i < numlightentities;i++)
3110                                         R_Shadow_DrawEntityLight(lightentities[i]);
3111                                 for (i = 0;i < numlightentities_noselfshadow;i++)
3112                                         R_Shadow_DrawEntityLight(lightentities_noselfshadow[i]);
3113                         }
3114                 }
3115         }
3116 }
3117
3118 void R_Shadow_DrawLightSprites(void);
3119 void R_ShadowVolumeLighting(qboolean visible)
3120 {
3121         int flag;
3122         int lnum;
3123         size_t lightindex;
3124         dlight_t *light;
3125
3126         if (r_editlights.integer)
3127                 R_Shadow_DrawLightSprites();
3128
3129         R_Shadow_RenderMode_Begin();
3130
3131         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3132         if (r_shadow_debuglight.integer >= 0)
3133         {
3134                 lightindex = r_shadow_debuglight.integer;
3135                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3136                 if (light && (light->flags & flag))
3137                         R_DrawRTLight(&light->rtlight, visible);
3138         }
3139         else
3140         {
3141                 for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3142                 {
3143                         light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3144                         if (light && (light->flags & flag))
3145                                 R_DrawRTLight(&light->rtlight, visible);
3146                 }
3147         }
3148         if (r_refdef.rtdlight)
3149                 for (lnum = 0;lnum < r_refdef.scene.numlights;lnum++)
3150                         R_DrawRTLight(&r_refdef.scene.lights[lnum], visible);
3151
3152         R_Shadow_RenderMode_End();
3153 }
3154
3155 extern void R_SetupView(void);
3156 extern cvar_t r_shadows_throwdistance;
3157 void R_DrawModelShadows(void)
3158 {
3159         int i;
3160         float relativethrowdistance;
3161         entity_render_t *ent;
3162         vec3_t relativelightorigin;
3163         vec3_t relativelightdirection;
3164         vec3_t relativeshadowmins, relativeshadowmaxs;
3165         float vertex3f[12];
3166
3167         if (!r_drawentities.integer || !gl_stencil)
3168                 return;
3169
3170         CHECKGLERROR
3171         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3172
3173         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
3174
3175         if (gl_ext_separatestencil.integer)
3176                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_SEPARATESTENCIL;
3177         else if (gl_ext_stenciltwoside.integer)
3178                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
3179         else
3180                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
3181
3182         R_Shadow_RenderMode_StencilShadowVolumes(true);
3183
3184         for (i = 0;i < r_refdef.scene.numentities;i++)
3185         {
3186                 ent = r_refdef.scene.entities[i];
3187                 // cast shadows from anything that is not a submodel of the map
3188                 if (ent->model && ent->model->DrawShadowVolume != NULL && !ent->model->brush.submodel && (ent->flags & RENDER_SHADOW))
3189                 {
3190                         relativethrowdistance = r_shadows_throwdistance.value * Matrix4x4_ScaleFromMatrix(&ent->inversematrix);
3191                         VectorSet(relativeshadowmins, -relativethrowdistance, -relativethrowdistance, -relativethrowdistance);
3192                         VectorSet(relativeshadowmaxs, relativethrowdistance, relativethrowdistance, relativethrowdistance);
3193                         VectorNegate(ent->modellight_lightdir, relativelightdirection);
3194                         VectorScale(relativelightdirection, -relativethrowdistance, relativelightorigin);
3195                         RSurf_ActiveModelEntity(ent, false, false);
3196                         ent->model->DrawShadowVolume(ent, relativelightorigin, relativelightdirection, relativethrowdistance, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
3197                 }
3198         }
3199
3200         // not really the right mode, but this will disable any silly stencil features
3201         R_Shadow_RenderMode_VisibleLighting(true, true);
3202
3203         // vertex coordinates for a quad that covers the screen exactly
3204         vertex3f[0] = 0;vertex3f[1] = 0;vertex3f[2] = 0;
3205         vertex3f[3] = 1;vertex3f[4] = 0;vertex3f[5] = 0;
3206         vertex3f[6] = 1;vertex3f[7] = 1;vertex3f[8] = 0;
3207         vertex3f[9] = 0;vertex3f[10] = 1;vertex3f[11] = 0;
3208
3209         // set up ortho view for rendering this pass
3210         GL_SetupView_Mode_Ortho(0, 0, 1, 1, -10, 100);
3211         GL_Scissor(r_refdef.view.x, r_refdef.view.y, r_refdef.view.width, r_refdef.view.height);
3212         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3213         GL_ScissorTest(true);
3214         R_Mesh_Matrix(&identitymatrix);
3215         R_Mesh_ResetTextureState();
3216         R_Mesh_VertexPointer(vertex3f, 0, 0);
3217         R_Mesh_ColorPointer(NULL, 0, 0);
3218
3219         // set up a 50% darkening blend on shadowed areas
3220         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
3221         GL_DepthRange(0, 1);
3222         GL_DepthTest(false);
3223         GL_DepthMask(false);
3224         GL_PolygonOffset(0, 0);CHECKGLERROR
3225         GL_Color(0, 0, 0, 0.5);
3226         GL_ColorMask(r_refdef.view.colormask[0], r_refdef.view.colormask[1], r_refdef.view.colormask[2], 1);
3227         qglDepthFunc(GL_ALWAYS);CHECKGLERROR
3228         qglEnable(GL_STENCIL_TEST);CHECKGLERROR
3229         qglStencilMask(~0);CHECKGLERROR
3230         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);CHECKGLERROR
3231         qglStencilFunc(GL_NOTEQUAL, 128, ~0);CHECKGLERROR
3232
3233         // apply the blend to the shadowed areas
3234         R_Mesh_Draw(0, 4, 2, polygonelements, 0, 0);
3235
3236         // restoring the perspective view is done by R_RenderScene
3237         //R_SetupView();
3238
3239         // restore other state to normal
3240         R_Shadow_RenderMode_End();
3241 }
3242
3243 void R_DrawCoronas(void)
3244 {
3245         int i, flag;
3246         float cscale, scale;
3247         size_t lightindex;
3248         dlight_t *light;
3249         rtlight_t *rtlight;
3250         if (r_coronas.value < (1.0f / 256.0f) && !gl_flashblend.integer)
3251                 return;
3252         R_Mesh_Matrix(&identitymatrix);
3253         flag = r_refdef.rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
3254         // FIXME: these traces should scan all render entities instead of cl.world
3255         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3256         {
3257                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3258                 if (!light)
3259                         continue;
3260                 rtlight = &light->rtlight;
3261                 if (!(rtlight->flags & flag))
3262                         continue;
3263                 if (rtlight->corona * r_coronas.value <= 0)
3264                         continue;
3265                 if (r_shadow_debuglight.integer >= 0 && r_shadow_debuglight.integer != (int)lightindex)
3266                         continue;
3267                 cscale = rtlight->corona * r_coronas.value* 0.25f;
3268                 scale = rtlight->radius * rtlight->coronasizescale;
3269                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 16.0f * 16.0f)
3270                         continue;
3271                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3272                         continue;
3273                 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);
3274         }
3275         for (i = 0;i < r_refdef.scene.numlights;i++)
3276         {
3277                 rtlight = &r_refdef.scene.lights[i];
3278                 if (!(rtlight->flags & flag))
3279                         continue;
3280                 if (rtlight->corona <= 0)
3281                         continue;
3282                 if (VectorDistance2(rtlight->shadoworigin, r_refdef.view.origin) < 32.0f * 32.0f)
3283                         continue;
3284                 if (gl_flashblend.integer)
3285                 {
3286                         cscale = rtlight->corona * 1.0f;
3287                         scale = rtlight->radius * rtlight->coronasizescale * 2.0f;
3288                 }
3289                 else
3290                 {
3291                         cscale = rtlight->corona * r_coronas.value* 0.25f;
3292                         scale = rtlight->radius * rtlight->coronasizescale;
3293                 }
3294                 if (VectorLength(rtlight->color) * cscale < (1.0f / 256.0f))
3295                         continue;
3296                 if (CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, rtlight->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction < 1)
3297                         continue;
3298                 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);
3299         }
3300 }
3301
3302
3303
3304 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
3305 typedef struct suffixinfo_s
3306 {
3307         char *suffix;
3308         qboolean flipx, flipy, flipdiagonal;
3309 }
3310 suffixinfo_t;
3311 static suffixinfo_t suffix[3][6] =
3312 {
3313         {
3314                 {"px",   false, false, false},
3315                 {"nx",   false, false, false},
3316                 {"py",   false, false, false},
3317                 {"ny",   false, false, false},
3318                 {"pz",   false, false, false},
3319                 {"nz",   false, false, false}
3320         },
3321         {
3322                 {"posx", false, false, false},
3323                 {"negx", false, false, false},
3324                 {"posy", false, false, false},
3325                 {"negy", false, false, false},
3326                 {"posz", false, false, false},
3327                 {"negz", false, false, false}
3328         },
3329         {
3330                 {"rt",    true, false,  true},
3331                 {"lf",   false,  true,  true},
3332                 {"ft",    true,  true, false},
3333                 {"bk",   false, false, false},
3334                 {"up",    true, false,  true},
3335                 {"dn",    true, false,  true}
3336         }
3337 };
3338
3339 static int componentorder[4] = {0, 1, 2, 3};
3340
3341 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
3342 {
3343         int i, j, cubemapsize;
3344         unsigned char *cubemappixels, *image_buffer;
3345         rtexture_t *cubemaptexture;
3346         char name[256];
3347         // must start 0 so the first loadimagepixels has no requested width/height
3348         cubemapsize = 0;
3349         cubemappixels = NULL;
3350         cubemaptexture = NULL;
3351         // keep trying different suffix groups (posx, px, rt) until one loads
3352         for (j = 0;j < 3 && !cubemappixels;j++)
3353         {
3354                 // load the 6 images in the suffix group
3355                 for (i = 0;i < 6;i++)
3356                 {
3357                         // generate an image name based on the base and and suffix
3358                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
3359                         // load it
3360                         if ((image_buffer = loadimagepixelsbgra(name, false, false)))
3361                         {
3362                                 // an image loaded, make sure width and height are equal
3363                                 if (image_width == image_height && (!cubemappixels || image_width == cubemapsize))
3364                                 {
3365                                         // if this is the first image to load successfully, allocate the cubemap memory
3366                                         if (!cubemappixels && image_width >= 1)
3367                                         {
3368                                                 cubemapsize = image_width;
3369                                                 // note this clears to black, so unavailable sides are black
3370                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
3371                                         }
3372                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
3373                                         if (cubemappixels)
3374                                                 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);
3375                                 }
3376                                 else
3377                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
3378                                 // free the image
3379                                 Mem_Free(image_buffer);
3380                         }
3381                 }
3382         }
3383         // if a cubemap loaded, upload it
3384         if (cubemappixels)
3385         {
3386                 if (!r_shadow_filters_texturepool)
3387                         r_shadow_filters_texturepool = R_AllocTexturePool();
3388                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_BGRA, TEXF_PRECACHE | (gl_texturecompression_lightcubemaps.integer ? TEXF_COMPRESS : 0), NULL);
3389                 Mem_Free(cubemappixels);
3390         }
3391         else
3392         {
3393                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
3394                 for (j = 0;j < 3;j++)
3395                         for (i = 0;i < 6;i++)
3396                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
3397                 Con_Print(" and was unable to find any of them.\n");
3398         }
3399         return cubemaptexture;
3400 }
3401
3402 rtexture_t *R_Shadow_Cubemap(const char *basename)
3403 {
3404         int i;
3405         for (i = 0;i < numcubemaps;i++)
3406                 if (!strcasecmp(cubemaps[i].basename, basename))
3407                         return cubemaps[i].texture;
3408         if (i >= MAX_CUBEMAPS)
3409                 return r_texture_whitecube;
3410         numcubemaps++;
3411         strlcpy(cubemaps[i].basename, basename, sizeof(cubemaps[i].basename));
3412         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
3413         if (!cubemaps[i].texture)
3414                 cubemaps[i].texture = r_texture_whitecube;
3415         return cubemaps[i].texture;
3416 }
3417
3418 void R_Shadow_FreeCubemaps(void)
3419 {
3420         numcubemaps = 0;
3421         R_FreeTexturePool(&r_shadow_filters_texturepool);
3422 }
3423
3424 dlight_t *R_Shadow_NewWorldLight(void)
3425 {
3426         return (dlight_t *)Mem_ExpandableArray_AllocRecord(&r_shadow_worldlightsarray);
3427 }
3428
3429 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)
3430 {
3431         matrix4x4_t matrix;
3432         // validate parameters
3433         if (style < 0 || style >= MAX_LIGHTSTYLES)
3434         {
3435                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
3436                 style = 0;
3437         }
3438         if (!cubemapname)
3439                 cubemapname = "";
3440
3441         // copy to light properties
3442         VectorCopy(origin, light->origin);
3443         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
3444         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
3445         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
3446         light->color[0] = max(color[0], 0);
3447         light->color[1] = max(color[1], 0);
3448         light->color[2] = max(color[2], 0);
3449         light->radius = max(radius, 0);
3450         light->style = style;
3451         light->shadow = shadowenable;
3452         light->corona = corona;
3453         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
3454         light->coronasizescale = coronasizescale;
3455         light->ambientscale = ambientscale;
3456         light->diffusescale = diffusescale;
3457         light->specularscale = specularscale;
3458         light->flags = flags;
3459
3460         // update renderable light data
3461         Matrix4x4_CreateFromQuakeEntity(&matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], light->radius);
3462         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);
3463 }
3464
3465 void R_Shadow_FreeWorldLight(dlight_t *light)
3466 {
3467         if (r_shadow_selectedlight == light)
3468                 r_shadow_selectedlight = NULL;
3469         R_RTLight_Uncompile(&light->rtlight);
3470         Mem_ExpandableArray_FreeRecord(&r_shadow_worldlightsarray, light);
3471 }
3472
3473 void R_Shadow_ClearWorldLights(void)
3474 {
3475         size_t lightindex;
3476         dlight_t *light;
3477         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3478         {
3479                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3480                 if (light)
3481                         R_Shadow_FreeWorldLight(light);
3482         }
3483         r_shadow_selectedlight = NULL;
3484         R_Shadow_FreeCubemaps();
3485 }
3486
3487 void R_Shadow_SelectLight(dlight_t *light)
3488 {
3489         if (r_shadow_selectedlight)
3490                 r_shadow_selectedlight->selected = false;
3491         r_shadow_selectedlight = light;
3492         if (r_shadow_selectedlight)
3493                 r_shadow_selectedlight->selected = true;
3494 }
3495
3496 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3497 {
3498         // this is never batched (there can be only one)
3499         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);
3500 }
3501
3502 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, const rtlight_t *rtlight, int numsurfaces, int *surfacelist)
3503 {
3504         float intensity;
3505         float s;
3506         vec3_t spritecolor;
3507         cachepic_t *pic;
3508
3509         // this is never batched (due to the ent parameter changing every time)
3510         // so numsurfaces == 1 and surfacelist[0] == lightnumber
3511         const dlight_t *light = (dlight_t *)ent;
3512         s = EDLIGHTSPRSIZE;
3513         intensity = 0.5f;
3514         VectorScale(light->color, intensity, spritecolor);
3515         if (VectorLength(spritecolor) < 0.1732f)
3516                 VectorSet(spritecolor, 0.1f, 0.1f, 0.1f);
3517         if (VectorLength(spritecolor) > 1.0f)
3518                 VectorNormalize(spritecolor);
3519
3520         // draw light sprite
3521         if (light->cubemapname[0] && !light->shadow)
3522                 pic = r_editlights_sprcubemapnoshadowlight;
3523         else if (light->cubemapname[0])
3524                 pic = r_editlights_sprcubemaplight;
3525         else if (!light->shadow)
3526                 pic = r_editlights_sprnoshadowlight;
3527         else
3528                 pic = r_editlights_sprlight;
3529         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);
3530         // draw selection sprite if light is selected
3531         if (light->selected)
3532                 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);
3533         // VorteX todo: add normalmode/realtime mode light overlay sprites?
3534 }
3535
3536 void R_Shadow_DrawLightSprites(void)
3537 {
3538         size_t lightindex;
3539         dlight_t *light;
3540         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3541         {
3542                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3543                 if (light)
3544                         R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 5, &light->rtlight);
3545         }
3546         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
3547 }
3548
3549 void R_Shadow_SelectLightInView(void)
3550 {
3551         float bestrating, rating, temp[3];
3552         dlight_t *best;
3553         size_t lightindex;
3554         dlight_t *light;
3555         best = NULL;
3556         bestrating = 0;
3557         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3558         {
3559                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3560                 if (!light)
3561                         continue;
3562                 VectorSubtract(light->origin, r_refdef.view.origin, temp);
3563                 rating = (DotProduct(temp, r_refdef.view.forward) / sqrt(DotProduct(temp, temp)));
3564                 if (rating >= 0.95)
3565                 {
3566                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
3567                         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)
3568                         {
3569                                 bestrating = rating;
3570                                 best = light;
3571                         }
3572                 }
3573         }
3574         R_Shadow_SelectLight(best);
3575 }
3576
3577 void R_Shadow_LoadWorldLights(void)
3578 {
3579         int n, a, style, shadow, flags;
3580         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
3581         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
3582         if (cl.worldmodel == NULL)
3583         {
3584                 Con_Print("No map loaded.\n");
3585                 return;
3586         }
3587         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3588         strlcat (name, ".rtlights", sizeof (name));
3589         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3590         if (lightsstring)
3591         {
3592                 s = lightsstring;
3593                 n = 0;
3594                 while (*s)
3595                 {
3596                         t = s;
3597                         /*
3598                         shadow = true;
3599                         for (;COM_Parse(t, true) && strcmp(
3600                         if (COM_Parse(t, true))
3601                         {
3602                                 if (com_token[0] == '!')
3603                                 {
3604                                         shadow = false;
3605                                         origin[0] = atof(com_token+1);
3606                                 }
3607                                 else
3608                                         origin[0] = atof(com_token);
3609                                 if (Com_Parse(t
3610                         }
3611                         */
3612                         t = s;
3613                         while (*s && *s != '\n' && *s != '\r')
3614                                 s++;
3615                         if (!*s)
3616                                 break;
3617                         tempchar = *s;
3618                         shadow = true;
3619                         // check for modifier flags
3620                         if (*t == '!')
3621                         {
3622                                 shadow = false;
3623                                 t++;
3624                         }
3625                         *s = 0;
3626                         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);
3627                         *s = tempchar;
3628                         if (a < 18)
3629                                 flags = LIGHTFLAG_REALTIMEMODE;
3630                         if (a < 17)
3631                                 specularscale = 1;
3632                         if (a < 16)
3633                                 diffusescale = 1;
3634                         if (a < 15)
3635                                 ambientscale = 0;
3636                         if (a < 14)
3637                                 coronasizescale = 0.25f;
3638                         if (a < 13)
3639                                 VectorClear(angles);
3640                         if (a < 10)
3641                                 corona = 0;
3642                         if (a < 9 || !strcmp(cubemapname, "\"\""))
3643                                 cubemapname[0] = 0;
3644                         // remove quotes on cubemapname
3645                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
3646                         {
3647                                 size_t namelen;
3648                                 namelen = strlen(cubemapname) - 2;
3649                                 memmove(cubemapname, cubemapname + 1, namelen);
3650                                 cubemapname[namelen] = '\0';
3651                         }
3652                         if (a < 8)
3653                         {
3654                                 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);
3655                                 break;
3656                         }
3657                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3658                         if (*s == '\r')
3659                                 s++;
3660                         if (*s == '\n')
3661                                 s++;
3662                         n++;
3663                 }
3664                 if (*s)
3665                         Con_Printf("invalid rtlights file \"%s\"\n", name);
3666                 Mem_Free(lightsstring);
3667         }
3668 }
3669
3670 void R_Shadow_SaveWorldLights(void)
3671 {
3672         size_t lightindex;
3673         dlight_t *light;
3674         size_t bufchars, bufmaxchars;
3675         char *buf, *oldbuf;
3676         char name[MAX_QPATH];
3677         char line[MAX_INPUTLINE];
3678         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
3679                 return;
3680         if (cl.worldmodel == NULL)
3681         {
3682                 Con_Print("No map loaded.\n");
3683                 return;
3684         }
3685         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3686         strlcat (name, ".rtlights", sizeof (name));
3687         bufchars = bufmaxchars = 0;
3688         buf = NULL;
3689         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
3690         {
3691                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
3692                 if (!light)
3693                         continue;
3694                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3695                         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);
3696                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3697                         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]);
3698                 else
3699                         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);
3700                 if (bufchars + strlen(line) > bufmaxchars)
3701                 {
3702                         bufmaxchars = bufchars + strlen(line) + 2048;
3703                         oldbuf = buf;
3704                         buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3705                         if (oldbuf)
3706                         {
3707                                 if (bufchars)
3708                                         memcpy(buf, oldbuf, bufchars);
3709                                 Mem_Free(oldbuf);
3710                         }
3711                 }
3712                 if (strlen(line))
3713                 {
3714                         memcpy(buf + bufchars, line, strlen(line));
3715                         bufchars += strlen(line);
3716                 }
3717         }
3718         if (bufchars)
3719                 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3720         if (buf)
3721                 Mem_Free(buf);
3722 }
3723
3724 void R_Shadow_LoadLightsFile(void)
3725 {
3726         int n, a, style;
3727         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3728         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3729         if (cl.worldmodel == NULL)
3730         {
3731                 Con_Print("No map loaded.\n");
3732                 return;
3733         }
3734         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
3735         strlcat (name, ".lights", sizeof (name));
3736         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3737         if (lightsstring)
3738         {
3739                 s = lightsstring;
3740                 n = 0;
3741                 while (*s)
3742                 {
3743                         t = s;
3744                         while (*s && *s != '\n' && *s != '\r')
3745                                 s++;
3746                         if (!*s)
3747                                 break;
3748                         tempchar = *s;
3749                         *s = 0;
3750                         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);
3751                         *s = tempchar;
3752                         if (a < 14)
3753                         {
3754                                 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);
3755                                 break;
3756                         }
3757                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3758                         radius = bound(15, radius, 4096);
3759                         VectorScale(color, (2.0f / (8388608.0f)), color);
3760                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3761                         if (*s == '\r')
3762                                 s++;
3763                         if (*s == '\n')
3764                                 s++;
3765                         n++;
3766                 }
3767                 if (*s)
3768                         Con_Printf("invalid lights file \"%s\"\n", name);
3769                 Mem_Free(lightsstring);
3770         }
3771 }
3772
3773 // tyrlite/hmap2 light types in the delay field
3774 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3775
3776 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3777 {
3778         int entnum, style, islight, skin, pflags, effects, type, n;
3779         char *entfiledata;
3780         const char *data;
3781         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3782         char key[256], value[MAX_INPUTLINE];
3783
3784         if (cl.worldmodel == NULL)
3785         {
3786                 Con_Print("No map loaded.\n");
3787                 return;
3788         }
3789         // try to load a .ent file first
3790         FS_StripExtension (cl.worldmodel->name, key, sizeof (key));
3791         strlcat (key, ".ent", sizeof (key));
3792         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3793         // and if that is not found, fall back to the bsp file entity string
3794         if (!data)
3795                 data = cl.worldmodel->brush.entities;
3796         if (!data)
3797                 return;
3798         for (entnum = 0;COM_ParseToken_Simple(&data, false, false) && com_token[0] == '{';entnum++)
3799         {
3800                 type = LIGHTTYPE_MINUSX;
3801                 origin[0] = origin[1] = origin[2] = 0;
3802                 originhack[0] = originhack[1] = originhack[2] = 0;
3803                 angles[0] = angles[1] = angles[2] = 0;
3804                 color[0] = color[1] = color[2] = 1;
3805                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3806                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3807                 fadescale = 1;
3808                 lightscale = 1;
3809                 style = 0;
3810                 skin = 0;
3811                 pflags = 0;
3812                 effects = 0;
3813                 islight = false;
3814                 while (1)
3815                 {
3816                         if (!COM_ParseToken_Simple(&data, false, false))
3817                                 break; // error
3818                         if (com_token[0] == '}')
3819                                 break; // end of entity
3820                         if (com_token[0] == '_')
3821                                 strlcpy(key, com_token + 1, sizeof(key));
3822                         else
3823                                 strlcpy(key, com_token, sizeof(key));
3824                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3825                                 key[strlen(key)-1] = 0;
3826                         if (!COM_ParseToken_Simple(&data, false, false))
3827                                 break; // error
3828                         strlcpy(value, com_token, sizeof(value));
3829
3830                         // now that we have the key pair worked out...
3831                         if (!strcmp("light", key))
3832                         {
3833                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3834                                 if (n == 1)
3835                                 {
3836                                         // quake
3837                                         light[0] = vec[0] * (1.0f / 256.0f);
3838                                         light[1] = vec[0] * (1.0f / 256.0f);
3839                                         light[2] = vec[0] * (1.0f / 256.0f);
3840                                         light[3] = vec[0];
3841                                 }
3842                                 else if (n == 4)
3843                                 {
3844                                         // halflife
3845                                         light[0] = vec[0] * (1.0f / 255.0f);
3846                                         light[1] = vec[1] * (1.0f / 255.0f);
3847                                         light[2] = vec[2] * (1.0f / 255.0f);
3848                                         light[3] = vec[3];
3849                                 }
3850                         }
3851                         else if (!strcmp("delay", key))
3852                                 type = atoi(value);
3853                         else if (!strcmp("origin", key))
3854                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3855                         else if (!strcmp("angle", key))
3856                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3857                         else if (!strcmp("angles", key))
3858                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3859                         else if (!strcmp("color", key))
3860                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3861                         else if (!strcmp("wait", key))
3862                                 fadescale = atof(value);
3863                         else if (!strcmp("classname", key))
3864                         {
3865                                 if (!strncmp(value, "light", 5))
3866                                 {
3867                                         islight = true;
3868                                         if (!strcmp(value, "light_fluoro"))
3869                                         {
3870                                                 originhack[0] = 0;
3871                                                 originhack[1] = 0;
3872                                                 originhack[2] = 0;
3873                                                 overridecolor[0] = 1;
3874                                                 overridecolor[1] = 1;
3875                                                 overridecolor[2] = 1;
3876                                         }
3877                                         if (!strcmp(value, "light_fluorospark"))
3878                                         {
3879                                                 originhack[0] = 0;
3880                                                 originhack[1] = 0;
3881                                                 originhack[2] = 0;
3882                                                 overridecolor[0] = 1;
3883                                                 overridecolor[1] = 1;
3884                                                 overridecolor[2] = 1;
3885                                         }
3886                                         if (!strcmp(value, "light_globe"))
3887                                         {
3888                                                 originhack[0] = 0;
3889                                                 originhack[1] = 0;
3890                                                 originhack[2] = 0;
3891                                                 overridecolor[0] = 1;
3892                                                 overridecolor[1] = 0.8;
3893                                                 overridecolor[2] = 0.4;
3894                                         }
3895                                         if (!strcmp(value, "light_flame_large_yellow"))
3896                                         {
3897                                                 originhack[0] = 0;
3898                                                 originhack[1] = 0;
3899                                                 originhack[2] = 0;
3900                                                 overridecolor[0] = 1;
3901                                                 overridecolor[1] = 0.5;
3902                                                 overridecolor[2] = 0.1;
3903                                         }
3904                                         if (!strcmp(value, "light_flame_small_yellow"))
3905                                         {
3906                                                 originhack[0] = 0;
3907                                                 originhack[1] = 0;
3908                                                 originhack[2] = 0;
3909                                                 overridecolor[0] = 1;
3910                                                 overridecolor[1] = 0.5;
3911                                                 overridecolor[2] = 0.1;
3912                                         }
3913                                         if (!strcmp(value, "light_torch_small_white"))
3914                                         {
3915                                                 originhack[0] = 0;
3916                                                 originhack[1] = 0;
3917                                                 originhack[2] = 0;
3918                                                 overridecolor[0] = 1;
3919                                                 overridecolor[1] = 0.5;
3920                                                 overridecolor[2] = 0.1;
3921                                         }
3922                                         if (!strcmp(value, "light_torch_small_walltorch"))
3923                                         {
3924                                                 originhack[0] = 0;
3925                                                 originhack[1] = 0;
3926                                                 originhack[2] = 0;
3927                                                 overridecolor[0] = 1;
3928                                                 overridecolor[1] = 0.5;
3929                                                 overridecolor[2] = 0.1;
3930                                         }
3931                                 }
3932                         }
3933                         else if (!strcmp("style", key))
3934                                 style = atoi(value);
3935                         else if (!strcmp("skin", key))
3936                                 skin = (int)atof(value);
3937                         else if (!strcmp("pflags", key))
3938                                 pflags = (int)atof(value);
3939                         else if (!strcmp("effects", key))
3940                                 effects = (int)atof(value);
3941                         else if (cl.worldmodel->type == mod_brushq3)
3942                         {
3943                                 if (!strcmp("scale", key))
3944                                         lightscale = atof(value);
3945                                 if (!strcmp("fade", key))
3946                                         fadescale = atof(value);
3947                         }
3948                 }
3949                 if (!islight)
3950                         continue;
3951                 if (lightscale <= 0)
3952                         lightscale = 1;
3953                 if (fadescale <= 0)
3954                         fadescale = 1;
3955                 if (color[0] == color[1] && color[0] == color[2])
3956                 {
3957                         color[0] *= overridecolor[0];
3958                         color[1] *= overridecolor[1];
3959                         color[2] *= overridecolor[2];
3960                 }
3961                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3962                 color[0] = color[0] * light[0];
3963                 color[1] = color[1] * light[1];
3964                 color[2] = color[2] * light[2];
3965                 switch (type)
3966                 {
3967                 case LIGHTTYPE_MINUSX:
3968                         break;
3969                 case LIGHTTYPE_RECIPX:
3970                         radius *= 2;
3971                         VectorScale(color, (1.0f / 16.0f), color);
3972                         break;
3973                 case LIGHTTYPE_RECIPXX:
3974                         radius *= 2;
3975                         VectorScale(color, (1.0f / 16.0f), color);
3976                         break;
3977                 default:
3978                 case LIGHTTYPE_NONE:
3979                         break;
3980                 case LIGHTTYPE_SUN:
3981                         break;
3982                 case LIGHTTYPE_MINUSXX:
3983                         break;
3984                 }
3985                 VectorAdd(origin, originhack, origin);
3986                 if (radius >= 1)
3987                         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);
3988         }
3989         if (entfiledata)
3990                 Mem_Free(entfiledata);
3991 }
3992
3993
3994 void R_Shadow_SetCursorLocationForView(void)
3995 {
3996         vec_t dist, push;
3997         vec3_t dest, endpos;
3998         trace_t trace;
3999         VectorMA(r_refdef.view.origin, r_editlights_cursordistance.value, r_refdef.view.forward, dest);
4000         trace = CL_Move(r_refdef.view.origin, vec3_origin, vec3_origin, dest, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false);
4001         if (trace.fraction < 1)
4002         {
4003                 dist = trace.fraction * r_editlights_cursordistance.value;
4004                 push = r_editlights_cursorpushback.value;
4005                 if (push > dist)
4006                         push = dist;
4007                 push = -push;
4008                 VectorMA(trace.endpos, push, r_refdef.view.forward, endpos);
4009                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
4010         }
4011         else
4012         {
4013                 VectorClear( endpos );
4014         }
4015         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4016         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4017         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
4018 }
4019
4020 void R_Shadow_UpdateWorldLightSelection(void)
4021 {
4022         if (r_editlights.integer)
4023         {
4024                 R_Shadow_SetCursorLocationForView();
4025                 R_Shadow_SelectLightInView();
4026         }
4027         else
4028                 R_Shadow_SelectLight(NULL);
4029 }
4030
4031 void R_Shadow_EditLights_Clear_f(void)
4032 {
4033         R_Shadow_ClearWorldLights();
4034 }
4035
4036 void R_Shadow_EditLights_Reload_f(void)
4037 {
4038         if (!cl.worldmodel)
4039                 return;
4040         strlcpy(r_shadow_mapname, cl.worldmodel->name, sizeof(r_shadow_mapname));
4041         R_Shadow_ClearWorldLights();
4042         R_Shadow_LoadWorldLights();
4043         if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4044         {
4045                 R_Shadow_LoadLightsFile();
4046                 if (!Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray))
4047                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4048         }
4049 }
4050
4051 void R_Shadow_EditLights_Save_f(void)
4052 {
4053         if (!cl.worldmodel)
4054                 return;
4055         R_Shadow_SaveWorldLights();
4056 }
4057
4058 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
4059 {
4060         R_Shadow_ClearWorldLights();
4061         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
4062 }
4063
4064 void R_Shadow_EditLights_ImportLightsFile_f(void)
4065 {
4066         R_Shadow_ClearWorldLights();
4067         R_Shadow_LoadLightsFile();
4068 }
4069
4070 void R_Shadow_EditLights_Spawn_f(void)
4071 {
4072         vec3_t color;
4073         if (!r_editlights.integer)
4074         {
4075                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4076                 return;
4077         }
4078         if (Cmd_Argc() != 1)
4079         {
4080                 Con_Print("r_editlights_spawn does not take parameters\n");
4081                 return;
4082         }
4083         color[0] = color[1] = color[2] = 1;
4084         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
4085 }
4086
4087 void R_Shadow_EditLights_Edit_f(void)
4088 {
4089         vec3_t origin, angles, color;
4090         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
4091         int style, shadows, flags, normalmode, realtimemode;
4092         char cubemapname[MAX_INPUTLINE];
4093         if (!r_editlights.integer)
4094         {
4095                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4096                 return;
4097         }
4098         if (!r_shadow_selectedlight)
4099         {
4100                 Con_Print("No selected light.\n");
4101                 return;
4102         }
4103         VectorCopy(r_shadow_selectedlight->origin, origin);
4104         VectorCopy(r_shadow_selectedlight->angles, angles);
4105         VectorCopy(r_shadow_selectedlight->color, color);
4106         radius = r_shadow_selectedlight->radius;
4107         style = r_shadow_selectedlight->style;
4108         if (r_shadow_selectedlight->cubemapname)
4109                 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
4110         else
4111                 cubemapname[0] = 0;
4112         shadows = r_shadow_selectedlight->shadow;
4113         corona = r_shadow_selectedlight->corona;
4114         coronasizescale = r_shadow_selectedlight->coronasizescale;
4115         ambientscale = r_shadow_selectedlight->ambientscale;
4116         diffusescale = r_shadow_selectedlight->diffusescale;
4117         specularscale = r_shadow_selectedlight->specularscale;
4118         flags = r_shadow_selectedlight->flags;
4119         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
4120         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
4121         if (!strcmp(Cmd_Argv(1), "origin"))
4122         {
4123                 if (Cmd_Argc() != 5)
4124                 {
4125                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4126                         return;
4127                 }
4128                 origin[0] = atof(Cmd_Argv(2));
4129                 origin[1] = atof(Cmd_Argv(3));
4130                 origin[2] = atof(Cmd_Argv(4));
4131         }
4132         else if (!strcmp(Cmd_Argv(1), "originx"))
4133         {
4134                 if (Cmd_Argc() != 3)
4135                 {
4136                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4137                         return;
4138                 }
4139                 origin[0] = atof(Cmd_Argv(2));
4140         }
4141         else if (!strcmp(Cmd_Argv(1), "originy"))
4142         {
4143                 if (Cmd_Argc() != 3)
4144                 {
4145                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4146                         return;
4147                 }
4148                 origin[1] = atof(Cmd_Argv(2));
4149         }
4150         else if (!strcmp(Cmd_Argv(1), "originz"))
4151         {
4152                 if (Cmd_Argc() != 3)
4153                 {
4154                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4155                         return;
4156                 }
4157                 origin[2] = atof(Cmd_Argv(2));
4158         }
4159         else if (!strcmp(Cmd_Argv(1), "move"))
4160         {
4161                 if (Cmd_Argc() != 5)
4162                 {
4163                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4164                         return;
4165                 }
4166                 origin[0] += atof(Cmd_Argv(2));
4167                 origin[1] += atof(Cmd_Argv(3));
4168                 origin[2] += atof(Cmd_Argv(4));
4169         }
4170         else if (!strcmp(Cmd_Argv(1), "movex"))
4171         {
4172                 if (Cmd_Argc() != 3)
4173                 {
4174                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4175                         return;
4176                 }
4177                 origin[0] += atof(Cmd_Argv(2));
4178         }
4179         else if (!strcmp(Cmd_Argv(1), "movey"))
4180         {
4181                 if (Cmd_Argc() != 3)
4182                 {
4183                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4184                         return;
4185                 }
4186                 origin[1] += atof(Cmd_Argv(2));
4187         }
4188         else if (!strcmp(Cmd_Argv(1), "movez"))
4189         {
4190                 if (Cmd_Argc() != 3)
4191                 {
4192                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4193                         return;
4194                 }
4195                 origin[2] += atof(Cmd_Argv(2));
4196         }
4197         else if (!strcmp(Cmd_Argv(1), "angles"))
4198         {
4199                 if (Cmd_Argc() != 5)
4200                 {
4201                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
4202                         return;
4203                 }
4204                 angles[0] = atof(Cmd_Argv(2));
4205                 angles[1] = atof(Cmd_Argv(3));
4206                 angles[2] = atof(Cmd_Argv(4));
4207         }
4208         else if (!strcmp(Cmd_Argv(1), "anglesx"))
4209         {
4210                 if (Cmd_Argc() != 3)
4211                 {
4212                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4213                         return;
4214                 }
4215                 angles[0] = atof(Cmd_Argv(2));
4216         }
4217         else if (!strcmp(Cmd_Argv(1), "anglesy"))
4218         {
4219                 if (Cmd_Argc() != 3)
4220                 {
4221                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4222                         return;
4223                 }
4224                 angles[1] = atof(Cmd_Argv(2));
4225         }
4226         else if (!strcmp(Cmd_Argv(1), "anglesz"))
4227         {
4228                 if (Cmd_Argc() != 3)
4229                 {
4230                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4231                         return;
4232                 }
4233                 angles[2] = atof(Cmd_Argv(2));
4234         }
4235         else if (!strcmp(Cmd_Argv(1), "color"))
4236         {
4237                 if (Cmd_Argc() != 5)
4238                 {
4239                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
4240                         return;
4241                 }
4242                 color[0] = atof(Cmd_Argv(2));
4243                 color[1] = atof(Cmd_Argv(3));
4244                 color[2] = atof(Cmd_Argv(4));
4245         }
4246         else if (!strcmp(Cmd_Argv(1), "radius"))
4247         {
4248                 if (Cmd_Argc() != 3)
4249                 {
4250                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4251                         return;
4252                 }
4253                 radius = atof(Cmd_Argv(2));
4254         }
4255         else if (!strcmp(Cmd_Argv(1), "colorscale"))
4256         {
4257                 if (Cmd_Argc() == 3)
4258                 {
4259                         double scale = atof(Cmd_Argv(2));
4260                         color[0] *= scale;
4261                         color[1] *= scale;
4262                         color[2] *= scale;
4263                 }
4264                 else
4265                 {
4266                         if (Cmd_Argc() != 5)
4267                         {
4268                                 Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
4269                                 return;
4270                         }
4271                         color[0] *= atof(Cmd_Argv(2));
4272                         color[1] *= atof(Cmd_Argv(3));
4273                         color[2] *= atof(Cmd_Argv(4));
4274                 }
4275         }
4276         else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
4277         {
4278                 if (Cmd_Argc() != 3)
4279                 {
4280                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4281                         return;
4282                 }
4283                 radius *= atof(Cmd_Argv(2));
4284         }
4285         else if (!strcmp(Cmd_Argv(1), "style"))
4286         {
4287                 if (Cmd_Argc() != 3)
4288                 {
4289                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4290                         return;
4291                 }
4292                 style = atoi(Cmd_Argv(2));
4293         }
4294         else if (!strcmp(Cmd_Argv(1), "cubemap"))
4295         {
4296                 if (Cmd_Argc() > 3)
4297                 {
4298                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4299                         return;
4300                 }
4301                 if (Cmd_Argc() == 3)
4302                         strlcpy(cubemapname, Cmd_Argv(2), sizeof(cubemapname));
4303                 else
4304                         cubemapname[0] = 0;
4305         }
4306         else if (!strcmp(Cmd_Argv(1), "shadows"))
4307         {
4308                 if (Cmd_Argc() != 3)
4309                 {
4310                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4311                         return;
4312                 }
4313                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4314         }
4315         else if (!strcmp(Cmd_Argv(1), "corona"))
4316         {
4317                 if (Cmd_Argc() != 3)
4318                 {
4319                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4320                         return;
4321                 }
4322                 corona = atof(Cmd_Argv(2));
4323         }
4324         else if (!strcmp(Cmd_Argv(1), "coronasize"))
4325         {
4326                 if (Cmd_Argc() != 3)
4327                 {
4328                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4329                         return;
4330                 }
4331                 coronasizescale = atof(Cmd_Argv(2));
4332         }
4333         else if (!strcmp(Cmd_Argv(1), "ambient"))
4334         {
4335                 if (Cmd_Argc() != 3)
4336                 {
4337                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4338                         return;
4339                 }
4340                 ambientscale = atof(Cmd_Argv(2));
4341         }
4342         else if (!strcmp(Cmd_Argv(1), "diffuse"))
4343         {
4344                 if (Cmd_Argc() != 3)
4345                 {
4346                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4347                         return;
4348                 }
4349                 diffusescale = atof(Cmd_Argv(2));
4350         }
4351         else if (!strcmp(Cmd_Argv(1), "specular"))
4352         {
4353                 if (Cmd_Argc() != 3)
4354                 {
4355                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4356                         return;
4357                 }
4358                 specularscale = atof(Cmd_Argv(2));
4359         }
4360         else if (!strcmp(Cmd_Argv(1), "normalmode"))
4361         {
4362                 if (Cmd_Argc() != 3)
4363                 {
4364                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4365                         return;
4366                 }
4367                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4368         }
4369         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
4370         {
4371                 if (Cmd_Argc() != 3)
4372                 {
4373                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
4374                         return;
4375                 }
4376                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
4377         }
4378         else
4379         {
4380                 Con_Print("usage: r_editlights_edit [property] [value]\n");
4381                 Con_Print("Selected light's properties:\n");
4382                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
4383                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
4384                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
4385                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
4386                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
4387                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
4388                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
4389                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
4390                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
4391                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
4392                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
4393                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
4394                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
4395                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
4396                 return;
4397         }
4398         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
4399         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
4400 }
4401
4402 void R_Shadow_EditLights_EditAll_f(void)
4403 {
4404         size_t lightindex;
4405         dlight_t *light;
4406
4407         if (!r_editlights.integer)
4408         {
4409                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
4410                 return;
4411         }
4412
4413         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4414         {
4415                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4416                 if (!light)
4417                         continue;
4418                 R_Shadow_SelectLight(light);
4419                 R_Shadow_EditLights_Edit_f();
4420         }
4421 }
4422
4423 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
4424 {
4425         int lightnumber, lightcount;
4426         size_t lightindex;
4427         dlight_t *light;
4428         float x, y;
4429         char temp[256];
4430         if (!r_editlights.integer)
4431                 return;
4432         x = vid_conwidth.value - 240;
4433         y = 5;
4434         DrawQ_Pic(x-5, y-5, NULL, 250, 155, 0, 0, 0, 0.75, 0);
4435         lightnumber = -1;
4436         lightcount = 0;
4437         for (lightindex = 0;lightindex < Mem_ExpandableArray_IndexRange(&r_shadow_worldlightsarray);lightindex++)
4438         {
4439                 light = Mem_ExpandableArray_RecordAtIndex(&r_shadow_worldlightsarray, lightindex);
4440                 if (!light)
4441                         continue;
4442                 if (light == r_shadow_selectedlight)
4443                         lightnumber = lightindex;
4444                 lightcount++;
4445         }
4446         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;
4447         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;
4448         y += 8;
4449         if (r_shadow_selectedlight == NULL)
4450                 return;
4451         sprintf(temp, "Light #%i properties:", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0, NULL, true);y += 8;
4452         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;
4453         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;
4454         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;
4455         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;
4456         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;
4457         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;
4458         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;
4459         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;
4460         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;
4461         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;
4462         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;
4463         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;
4464         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;
4465         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;
4466 }
4467
4468 void R_Shadow_EditLights_ToggleShadow_f(void)
4469 {
4470         if (!r_editlights.integer)
4471         {
4472                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4473                 return;
4474         }
4475         if (!r_shadow_selectedlight)
4476         {
4477                 Con_Print("No selected light.\n");
4478                 return;
4479         }
4480         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);
4481 }
4482
4483 void R_Shadow_EditLights_ToggleCorona_f(void)
4484 {
4485         if (!r_editlights.integer)
4486         {
4487                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
4488                 return;
4489         }
4490         if (!r_shadow_selectedlight)
4491         {
4492                 Con_Print("No selected light.\n");
4493                 return;
4494         }
4495         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);
4496 }
4497
4498 void R_Shadow_EditLights_Remove_f(void)
4499 {
4500         if (!r_editlights.integer)
4501         {
4502                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
4503                 return;
4504         }
4505         if (!r_shadow_selectedlight)
4506         {
4507                 Con_Print("No selected light.\n");
4508                 return;
4509         }
4510         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
4511         r_shadow_selectedlight = NULL;
4512 }
4513
4514 void R_Shadow_EditLights_Help_f(void)
4515 {
4516         Con_Print(
4517 "Documentation on r_editlights system:\n"
4518 "Settings:\n"
4519 "r_editlights : enable/disable editing mode\n"
4520 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
4521 "r_editlights_cursorpushback : push back cursor this far from surface\n"
4522 "r_editlights_cursorpushoff : push cursor off surface this far\n"
4523 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
4524 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
4525 "Commands:\n"
4526 "r_editlights_help : this help\n"
4527 "r_editlights_clear : remove all lights\n"
4528 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
4529 "r_editlights_save : save to .rtlights file\n"
4530 "r_editlights_spawn : create a light with default settings\n"
4531 "r_editlights_edit command : edit selected light - more documentation below\n"
4532 "r_editlights_remove : remove selected light\n"
4533 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
4534 "r_editlights_importlightentitiesfrommap : reload light entities\n"
4535 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
4536 "Edit commands:\n"
4537 "origin x y z : set light location\n"
4538 "originx x: set x component of light location\n"
4539 "originy y: set y component of light location\n"
4540 "originz z: set z component of light location\n"
4541 "move x y z : adjust light location\n"
4542 "movex x: adjust x component of light location\n"
4543 "movey y: adjust y component of light location\n"
4544 "movez z: adjust z component of light location\n"
4545 "angles x y z : set light angles\n"
4546 "anglesx x: set x component of light angles\n"
4547 "anglesy y: set y component of light angles\n"
4548 "anglesz z: set z component of light angles\n"
4549 "color r g b : set color of light (can be brighter than 1 1 1)\n"
4550 "radius radius : set radius (size) of light\n"
4551 "colorscale grey : multiply color of light (1 does nothing)\n"
4552 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
4553 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
4554 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
4555 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
4556 "cubemap basename : set filter cubemap of light (not yet supported)\n"
4557 "shadows 1/0 : turn on/off shadows\n"
4558 "corona n : set corona intensity\n"
4559 "coronasize n : set corona size (0-1)\n"
4560 "ambient n : set ambient intensity (0-1)\n"
4561 "diffuse n : set diffuse intensity (0-1)\n"
4562 "specular n : set specular intensity (0-1)\n"
4563 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
4564 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
4565 "<nothing> : print light properties to console\n"
4566         );
4567 }
4568
4569 void R_Shadow_EditLights_CopyInfo_f(void)
4570 {
4571         if (!r_editlights.integer)
4572         {
4573                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
4574                 return;
4575         }
4576         if (!r_shadow_selectedlight)
4577         {
4578                 Con_Print("No selected light.\n");
4579                 return;
4580         }
4581         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
4582         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
4583         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
4584         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
4585         if (r_shadow_selectedlight->cubemapname)
4586                 strlcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname, sizeof(r_shadow_bufferlight.cubemapname));
4587         else
4588                 r_shadow_bufferlight.cubemapname[0] = 0;
4589         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
4590         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
4591         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
4592         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
4593         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
4594         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
4595         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
4596 }
4597
4598 void R_Shadow_EditLights_PasteInfo_f(void)
4599 {
4600         if (!r_editlights.integer)
4601         {
4602                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
4603                 return;
4604         }
4605         if (!r_shadow_selectedlight)
4606         {
4607                 Con_Print("No selected light.\n");
4608                 return;
4609         }
4610         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);
4611 }
4612
4613 void R_Shadow_EditLights_Init(void)
4614 {
4615         Cvar_RegisterVariable(&r_editlights);
4616         Cvar_RegisterVariable(&r_editlights_cursordistance);
4617         Cvar_RegisterVariable(&r_editlights_cursorpushback);
4618         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
4619         Cvar_RegisterVariable(&r_editlights_cursorgrid);
4620         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
4621         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
4622         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
4623         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)");
4624         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
4625         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
4626         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
4627         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)");
4628         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
4629         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
4630         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
4631         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
4632         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
4633         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
4634         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)");
4635 }
4636
4637
4638
4639 /*
4640 =============================================================================
4641
4642 LIGHT SAMPLING
4643
4644 =============================================================================
4645 */
4646
4647 void R_CompleteLightPoint(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const vec3_t p, int dynamic)
4648 {
4649         VectorClear(diffusecolor);
4650         VectorClear(diffusenormal);
4651
4652         if (!r_fullbright.integer && r_refdef.scene.worldmodel && r_refdef.scene.worldmodel->brush.LightPoint)
4653         {
4654                 ambientcolor[0] = ambientcolor[1] = ambientcolor[2] = r_ambient.value * (2.0f / 128.0f);
4655                 r_refdef.scene.worldmodel->brush.LightPoint(r_refdef.scene.worldmodel, p, ambientcolor, diffusecolor, diffusenormal);
4656         }
4657         else
4658                 VectorSet(ambientcolor, 1, 1, 1);
4659
4660         if (dynamic)
4661         {
4662                 int i;
4663                 float f, v[3];
4664                 rtlight_t *light;
4665                 for (i = 0;i < r_refdef.scene.numlights;i++)
4666                 {
4667                         light = &r_refdef.scene.lights[i];
4668                         Matrix4x4_Transform(&light->matrix_worldtolight, p, v);
4669                         f = 1 - VectorLength2(v);
4670                         if (f > 0 && CL_Move(p, vec3_origin, vec3_origin, light->shadoworigin, MOVE_NOMONSTERS, NULL, SUPERCONTENTS_SOLID, true, false, NULL, false).fraction == 1)
4671                                 VectorMA(ambientcolor, f, light->currentcolor, ambientcolor);
4672                 }
4673         }
4674 }