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