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