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