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