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