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