]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
removed the USETEXMATRIX define (now always on), reduced varray_texcoord3f to only...
[xonotic/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 mempool_t *r_shadow_mempool;
163
164 int maxshadowelements;
165 int *shadowelements;
166
167 int maxshadowmark;
168 int numshadowmark;
169 int *shadowmark;
170 int *shadowmarklist;
171 int shadowmarkcount;
172
173 int maxvertexupdate;
174 int *vertexupdate;
175 int *vertexremap;
176 int vertexupdatenum;
177
178 int r_shadow_buffer_numleafpvsbytes;
179 unsigned char *r_shadow_buffer_leafpvs;
180 int *r_shadow_buffer_leaflist;
181
182 int r_shadow_buffer_numsurfacepvsbytes;
183 unsigned char *r_shadow_buffer_surfacepvs;
184 int *r_shadow_buffer_surfacelist;
185
186 rtexturepool_t *r_shadow_texturepool;
187 rtexture_t *r_shadow_attenuation2dtexture;
188 rtexture_t *r_shadow_attenuation3dtexture;
189
190 // lights are reloaded when this changes
191 char r_shadow_mapname[MAX_QPATH];
192
193 // used only for light filters (cubemaps)
194 rtexturepool_t *r_shadow_filters_texturepool;
195
196 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"};
197 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"};
198 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
199 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)"};
200 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"};
201 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
202 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
203 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
204 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
205 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
206 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
207 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
208 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
209 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!)"};
210 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)"};
211 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"};
212 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"};
213 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
214 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
215 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"};
216 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)"};
217 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
218 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)"};
219 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)"};
220 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
221 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
222 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
223 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
224 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
225 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
226 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
227
228 float r_shadow_attenpower, r_shadow_attenscale;
229
230 rtlight_t *r_shadow_compilingrtlight;
231 dlight_t *r_shadow_worldlightchain;
232 dlight_t *r_shadow_selectedlight;
233 dlight_t r_shadow_bufferlight;
234 vec3_t r_editlights_cursorlocation;
235
236 extern int con_vislines;
237
238 typedef struct cubemapinfo_s
239 {
240         char basename[64];
241         rtexture_t *texture;
242 }
243 cubemapinfo_t;
244
245 #define MAX_CUBEMAPS 256
246 static int numcubemaps;
247 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
248
249 void R_Shadow_UncompileWorldLights(void);
250 void R_Shadow_ClearWorldLights(void);
251 void R_Shadow_SaveWorldLights(void);
252 void R_Shadow_LoadWorldLights(void);
253 void R_Shadow_LoadLightsFile(void);
254 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
255 void R_Shadow_EditLights_Reload_f(void);
256 void R_Shadow_ValidateCvars(void);
257 static void R_Shadow_MakeTextures(void);
258 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
259
260 void r_shadow_start(void)
261 {
262         // allocate vertex processing arrays
263         numcubemaps = 0;
264         r_shadow_attenuation2dtexture = NULL;
265         r_shadow_attenuation3dtexture = NULL;
266         r_shadow_texturepool = NULL;
267         r_shadow_filters_texturepool = NULL;
268         R_Shadow_ValidateCvars();
269         R_Shadow_MakeTextures();
270         maxshadowelements = 0;
271         shadowelements = NULL;
272         maxvertexupdate = 0;
273         vertexupdate = NULL;
274         vertexremap = NULL;
275         vertexupdatenum = 0;
276         maxshadowmark = 0;
277         numshadowmark = 0;
278         shadowmark = NULL;
279         shadowmarklist = NULL;
280         shadowmarkcount = 0;
281         r_shadow_buffer_numleafpvsbytes = 0;
282         r_shadow_buffer_leafpvs = NULL;
283         r_shadow_buffer_leaflist = NULL;
284         r_shadow_buffer_numsurfacepvsbytes = 0;
285         r_shadow_buffer_surfacepvs = NULL;
286         r_shadow_buffer_surfacelist = NULL;
287 }
288
289 void r_shadow_shutdown(void)
290 {
291         R_Shadow_UncompileWorldLights();
292         numcubemaps = 0;
293         r_shadow_attenuation2dtexture = NULL;
294         r_shadow_attenuation3dtexture = NULL;
295         R_FreeTexturePool(&r_shadow_texturepool);
296         R_FreeTexturePool(&r_shadow_filters_texturepool);
297         maxshadowelements = 0;
298         if (shadowelements)
299                 Mem_Free(shadowelements);
300         shadowelements = NULL;
301         maxvertexupdate = 0;
302         if (vertexupdate)
303                 Mem_Free(vertexupdate);
304         vertexupdate = NULL;
305         if (vertexremap)
306                 Mem_Free(vertexremap);
307         vertexremap = NULL;
308         vertexupdatenum = 0;
309         maxshadowmark = 0;
310         numshadowmark = 0;
311         if (shadowmark)
312                 Mem_Free(shadowmark);
313         shadowmark = NULL;
314         if (shadowmarklist)
315                 Mem_Free(shadowmarklist);
316         shadowmarklist = NULL;
317         shadowmarkcount = 0;
318         r_shadow_buffer_numleafpvsbytes = 0;
319         if (r_shadow_buffer_leafpvs)
320                 Mem_Free(r_shadow_buffer_leafpvs);
321         r_shadow_buffer_leafpvs = NULL;
322         if (r_shadow_buffer_leaflist)
323                 Mem_Free(r_shadow_buffer_leaflist);
324         r_shadow_buffer_leaflist = NULL;
325         r_shadow_buffer_numsurfacepvsbytes = 0;
326         if (r_shadow_buffer_surfacepvs)
327                 Mem_Free(r_shadow_buffer_surfacepvs);
328         r_shadow_buffer_surfacepvs = NULL;
329         if (r_shadow_buffer_surfacelist)
330                 Mem_Free(r_shadow_buffer_surfacelist);
331         r_shadow_buffer_surfacelist = NULL;
332 }
333
334 void r_shadow_newmap(void)
335 {
336 }
337
338 void R_Shadow_Help_f(void)
339 {
340         Con_Printf(
341 "Documentation on r_shadow system:\n"
342 "Settings:\n"
343 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
344 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
345 "r_shadow_debuglight : render only this light number (-1 = all)\n"
346 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
347 "r_shadow_gloss2intensity : brightness of forced gloss\n"
348 "r_shadow_glossintensity : brightness of textured gloss\n"
349 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
350 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
351 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
352 "r_shadow_portallight : use portal visibility for static light precomputation\n"
353 "r_shadow_projectdistance : shadow volume projection distance\n"
354 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
355 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
356 "r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
357 "r_shadow_realtime_world : use high quality world lighting mode\n"
358 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
359 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
360 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
361 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
362 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
363 "r_shadow_scissor : use scissor optimization\n"
364 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
365 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
366 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
367 "r_showlighting : useful for performance testing; bright = slow!\n"
368 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
369 "Commands:\n"
370 "r_shadow_help : this help\n"
371         );
372 }
373
374 void R_Shadow_Init(void)
375 {
376         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
377         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
378         Cvar_RegisterVariable(&r_shadow_debuglight);
379         Cvar_RegisterVariable(&r_shadow_gloss);
380         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
381         Cvar_RegisterVariable(&r_shadow_glossintensity);
382         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
383         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
384         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
385         Cvar_RegisterVariable(&r_shadow_portallight);
386         Cvar_RegisterVariable(&r_shadow_projectdistance);
387         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
388         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
389         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
390         Cvar_RegisterVariable(&r_shadow_realtime_world);
391         Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
392         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
393         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
394         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
395         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
396         Cvar_RegisterVariable(&r_shadow_scissor);
397         Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
398         Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
399         Cvar_RegisterVariable(&r_shadow_texture3d);
400         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
401         if (gamemode == GAME_TENEBRAE)
402         {
403                 Cvar_SetValue("r_shadow_gloss", 2);
404                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
405         }
406         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
407         R_Shadow_EditLights_Init();
408         r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
409         r_shadow_worldlightchain = NULL;
410         maxshadowelements = 0;
411         shadowelements = NULL;
412         maxvertexupdate = 0;
413         vertexupdate = NULL;
414         vertexremap = NULL;
415         vertexupdatenum = 0;
416         maxshadowmark = 0;
417         numshadowmark = 0;
418         shadowmark = NULL;
419         shadowmarklist = NULL;
420         shadowmarkcount = 0;
421         r_shadow_buffer_numleafpvsbytes = 0;
422         r_shadow_buffer_leafpvs = NULL;
423         r_shadow_buffer_leaflist = NULL;
424         r_shadow_buffer_numsurfacepvsbytes = 0;
425         r_shadow_buffer_surfacepvs = NULL;
426         r_shadow_buffer_surfacelist = NULL;
427         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
428 }
429
430 matrix4x4_t matrix_attenuationxyz =
431 {
432         {
433                 {0.5, 0.0, 0.0, 0.5},
434                 {0.0, 0.5, 0.0, 0.5},
435                 {0.0, 0.0, 0.5, 0.5},
436                 {0.0, 0.0, 0.0, 1.0}
437         }
438 };
439
440 matrix4x4_t matrix_attenuationz =
441 {
442         {
443                 {0.0, 0.0, 0.5, 0.5},
444                 {0.0, 0.0, 0.0, 0.5},
445                 {0.0, 0.0, 0.0, 0.5},
446                 {0.0, 0.0, 0.0, 1.0}
447         }
448 };
449
450 int *R_Shadow_ResizeShadowElements(int numtris)
451 {
452         // make sure shadowelements is big enough for this volume
453         if (maxshadowelements < numtris * 24)
454         {
455                 maxshadowelements = numtris * 24;
456                 if (shadowelements)
457                         Mem_Free(shadowelements);
458                 shadowelements = (int *)Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
459         }
460         return shadowelements;
461 }
462
463 static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
464 {
465         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
466         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
467         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
468         {
469                 if (r_shadow_buffer_leafpvs)
470                         Mem_Free(r_shadow_buffer_leafpvs);
471                 if (r_shadow_buffer_leaflist)
472                         Mem_Free(r_shadow_buffer_leaflist);
473                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
474                 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
475                 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
476         }
477         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
478         {
479                 if (r_shadow_buffer_surfacepvs)
480                         Mem_Free(r_shadow_buffer_surfacepvs);
481                 if (r_shadow_buffer_surfacelist)
482                         Mem_Free(r_shadow_buffer_surfacelist);
483                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
484                 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
485                 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
486         }
487 }
488
489 void R_Shadow_PrepareShadowMark(int numtris)
490 {
491         // make sure shadowmark is big enough for this volume
492         if (maxshadowmark < numtris)
493         {
494                 maxshadowmark = numtris;
495                 if (shadowmark)
496                         Mem_Free(shadowmark);
497                 if (shadowmarklist)
498                         Mem_Free(shadowmarklist);
499                 shadowmark = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
500                 shadowmarklist = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
501                 shadowmarkcount = 0;
502         }
503         shadowmarkcount++;
504         // if shadowmarkcount wrapped we clear the array and adjust accordingly
505         if (shadowmarkcount == 0)
506         {
507                 shadowmarkcount = 1;
508                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
509         }
510         numshadowmark = 0;
511 }
512
513 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)
514 {
515         int i, j;
516         int outtriangles = 0, outvertices = 0;
517         const int *element;
518         const float *vertex;
519
520         if (maxvertexupdate < innumvertices)
521         {
522                 maxvertexupdate = innumvertices;
523                 if (vertexupdate)
524                         Mem_Free(vertexupdate);
525                 if (vertexremap)
526                         Mem_Free(vertexremap);
527                 vertexupdate = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
528                 vertexremap = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
529                 vertexupdatenum = 0;
530         }
531         vertexupdatenum++;
532         if (vertexupdatenum == 0)
533         {
534                 vertexupdatenum = 1;
535                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
536                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
537         }
538
539         for (i = 0;i < numshadowmarktris;i++)
540                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
541
542         for (i = 0;i < numshadowmarktris;i++)
543         {
544                 element = inelement3i + shadowmarktris[i] * 3;
545                 // make sure the vertices are created
546                 for (j = 0;j < 3;j++)
547                 {
548                         if (vertexupdate[element[j]] != vertexupdatenum)
549                         {
550                                 float ratio, direction[3];
551                                 vertexupdate[element[j]] = vertexupdatenum;
552                                 vertexremap[element[j]] = outvertices;
553                                 vertex = invertex3f + element[j] * 3;
554                                 // project one copy of the vertex to the sphere radius of the light
555                                 // (FIXME: would projecting it to the light box be better?)
556                                 VectorSubtract(vertex, projectorigin, direction);
557                                 ratio = projectdistance / VectorLength(direction);
558                                 VectorCopy(vertex, outvertex3f);
559                                 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
560                                 outvertex3f += 6;
561                                 outvertices += 2;
562                         }
563                 }
564         }
565
566         for (i = 0;i < numshadowmarktris;i++)
567         {
568                 int remappedelement[3];
569                 int markindex;
570                 const int *neighbortriangle;
571
572                 markindex = shadowmarktris[i] * 3;
573                 element = inelement3i + markindex;
574                 neighbortriangle = inneighbor3i + markindex;
575                 // output the front and back triangles
576                 outelement3i[0] = vertexremap[element[0]];
577                 outelement3i[1] = vertexremap[element[1]];
578                 outelement3i[2] = vertexremap[element[2]];
579                 outelement3i[3] = vertexremap[element[2]] + 1;
580                 outelement3i[4] = vertexremap[element[1]] + 1;
581                 outelement3i[5] = vertexremap[element[0]] + 1;
582
583                 outelement3i += 6;
584                 outtriangles += 2;
585                 // output the sides (facing outward from this triangle)
586                 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
587                 {
588                         remappedelement[0] = vertexremap[element[0]];
589                         remappedelement[1] = vertexremap[element[1]];
590                         outelement3i[0] = remappedelement[1];
591                         outelement3i[1] = remappedelement[0];
592                         outelement3i[2] = remappedelement[0] + 1;
593                         outelement3i[3] = remappedelement[1];
594                         outelement3i[4] = remappedelement[0] + 1;
595                         outelement3i[5] = remappedelement[1] + 1;
596
597                         outelement3i += 6;
598                         outtriangles += 2;
599                 }
600                 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
601                 {
602                         remappedelement[1] = vertexremap[element[1]];
603                         remappedelement[2] = vertexremap[element[2]];
604                         outelement3i[0] = remappedelement[2];
605                         outelement3i[1] = remappedelement[1];
606                         outelement3i[2] = remappedelement[1] + 1;
607                         outelement3i[3] = remappedelement[2];
608                         outelement3i[4] = remappedelement[1] + 1;
609                         outelement3i[5] = remappedelement[2] + 1;
610
611                         outelement3i += 6;
612                         outtriangles += 2;
613                 }
614                 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
615                 {
616                         remappedelement[0] = vertexremap[element[0]];
617                         remappedelement[2] = vertexremap[element[2]];
618                         outelement3i[0] = remappedelement[0];
619                         outelement3i[1] = remappedelement[2];
620                         outelement3i[2] = remappedelement[2] + 1;
621                         outelement3i[3] = remappedelement[0];
622                         outelement3i[4] = remappedelement[2] + 1;
623                         outelement3i[5] = remappedelement[0] + 1;
624
625                         outelement3i += 6;
626                         outtriangles += 2;
627                 }
628         }
629         if (outnumvertices)
630                 *outnumvertices = outvertices;
631         return outtriangles;
632 }
633
634 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)
635 {
636         int tris, outverts;
637         if (projectdistance < 0.1)
638         {
639                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
640                 return;
641         }
642         if (!numverts || !nummarktris)
643                 return;
644         // make sure shadowelements is big enough for this volume
645         if (maxshadowelements < nummarktris * 24)
646                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
647         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
648         renderstats.lights_dynamicshadowtriangles += tris;
649         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
650 }
651
652 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)
653 {
654         int t, tend;
655         const int *e;
656         const float *v[3];
657         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
658                 return;
659         tend = firsttriangle + numtris;
660         if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
661          && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
662          && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
663         {
664                 // surface box entirely inside light box, no box cull
665                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
666                         if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
667                                 shadowmarklist[numshadowmark++] = t;
668         }
669         else
670         {
671                 // surface box not entirely inside light box, cull each triangle
672                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
673                 {
674                         v[0] = invertex3f + e[0] * 3;
675                         v[1] = invertex3f + e[1] * 3;
676                         v[2] = invertex3f + e[2] * 3;
677                         if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
678                          && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
679                          && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
680                          && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
681                          && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
682                          && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
683                          && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
684                                 shadowmarklist[numshadowmark++] = t;
685                 }
686         }
687 }
688
689 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
690 {
691         rmeshstate_t m;
692         if (r_shadow_compilingrtlight)
693         {
694                 // if we're compiling an rtlight, capture the mesh
695                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
696                 return;
697         }
698         renderstats.lights_shadowtriangles += numtriangles;
699         memset(&m, 0, sizeof(m));
700         m.pointer_vertex = vertex3f;
701         R_Mesh_State(&m);
702         GL_LockArrays(0, numvertices);
703         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
704         {
705                 // decrement stencil if backface is behind depthbuffer
706                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
707                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
708                 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
709                 // increment stencil if frontface is behind depthbuffer
710                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
711                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
712         }
713         R_Mesh_Draw(0, numvertices, numtriangles, element3i);
714         GL_LockArrays(0, 0);
715 }
716
717 static void R_Shadow_MakeTextures(void)
718 {
719         int x, y, z, d;
720         float v[3], intensity;
721         unsigned char *data;
722         R_FreeTexturePool(&r_shadow_texturepool);
723         r_shadow_texturepool = R_AllocTexturePool();
724         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
725         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
726 #define ATTEN2DSIZE 64
727 #define ATTEN3DSIZE 32
728         data = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
729         for (y = 0;y < ATTEN2DSIZE;y++)
730         {
731                 for (x = 0;x < ATTEN2DSIZE;x++)
732                 {
733                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
734                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
735                         v[2] = 0;
736                         intensity = 1.0f - sqrt(DotProduct(v, v));
737                         if (intensity > 0)
738                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
739                         d = bound(0, intensity, 255);
740                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
741                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
742                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
743                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
744                 }
745         }
746         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
747         if (r_shadow_texture3d.integer)
748         {
749                 for (z = 0;z < ATTEN3DSIZE;z++)
750                 {
751                         for (y = 0;y < ATTEN3DSIZE;y++)
752                         {
753                                 for (x = 0;x < ATTEN3DSIZE;x++)
754                                 {
755                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
756                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
757                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
758                                         intensity = 1.0f - sqrt(DotProduct(v, v));
759                                         if (intensity > 0)
760                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
761                                         d = bound(0, intensity, 255);
762                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
763                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
764                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
765                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
766                                 }
767                         }
768                 }
769                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
770         }
771         Mem_Free(data);
772 }
773
774 void R_Shadow_ValidateCvars(void)
775 {
776         if (r_shadow_texture3d.integer && !gl_texture3d)
777                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
778         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
779                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
780 }
781
782 // light currently being rendered
783 rtlight_t *r_shadow_rtlight;
784
785 // this is the location of the eye in entity space
786 vec3_t r_shadow_entityeyeorigin;
787 // this is the location of the light in entity space
788 vec3_t r_shadow_entitylightorigin;
789 // this transforms entity coordinates to light filter cubemap coordinates
790 // (also often used for other purposes)
791 matrix4x4_t r_shadow_entitytolight;
792 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
793 // of attenuation texturing in full 3D (Z result often ignored)
794 matrix4x4_t r_shadow_entitytoattenuationxyz;
795 // this transforms only the Z to S, and T is always 0.5
796 matrix4x4_t r_shadow_entitytoattenuationz;
797
798 void R_Shadow_RenderMode_Begin(void)
799 {
800         rmeshstate_t m;
801
802         R_Shadow_ValidateCvars();
803
804         if (!r_shadow_attenuation2dtexture
805          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
806          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
807          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
808                 R_Shadow_MakeTextures();
809
810         memset(&m, 0, sizeof(m));
811         R_Mesh_State(&m);
812         GL_BlendFunc(GL_ONE, GL_ZERO);
813         GL_DepthMask(false);
814         GL_DepthTest(true);
815         GL_Color(0, 0, 0, 1);
816         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
817         qglEnable(GL_CULL_FACE);
818         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
819
820         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
821
822         if (gl_ext_stenciltwoside.integer)
823                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
824         else
825                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
826
827         if (r_glsl.integer && gl_support_fragment_shader)
828                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
829         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
830                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
831         else
832                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
833 }
834
835 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
836 {
837         r_shadow_rtlight = rtlight;
838 }
839
840 void R_Shadow_RenderMode_Reset(void)
841 {
842         rmeshstate_t m;
843         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
844         {
845                 qglUseProgramObjectARB(0);
846                 // HACK HACK HACK: work around for bug in NVIDIAI 6xxx drivers that causes GL_OUT_OF_MEMORY and/or software rendering
847                 qglBegin(GL_TRIANGLES);
848                 qglEnd();
849                 CHECKGLERROR
850         }
851         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
852                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
853         memset(&m, 0, sizeof(m));
854         R_Mesh_State(&m);
855 }
856
857 void R_Shadow_RenderMode_StencilShadowVolumes(void)
858 {
859         R_Shadow_RenderMode_Reset();
860         GL_Color(1, 1, 1, 1);
861         GL_ColorMask(0, 0, 0, 0);
862         GL_BlendFunc(GL_ONE, GL_ZERO);
863         GL_DepthMask(false);
864         GL_DepthTest(true);
865         if (!r_showtrispass)
866                 qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
867         //if (r_shadow_shadow_polygonoffset.value != 0)
868         //{
869         //      qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
870         //      qglEnable(GL_POLYGON_OFFSET_FILL);
871         //}
872         //else
873         //      qglDisable(GL_POLYGON_OFFSET_FILL);
874         qglDepthFunc(GL_LESS);
875         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
876         qglEnable(GL_STENCIL_TEST);
877         qglStencilFunc(GL_ALWAYS, 128, ~0);
878         r_shadow_rendermode = r_shadow_shadowingrendermode;
879         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
880         {
881                 qglDisable(GL_CULL_FACE);
882                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
883                 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
884                 qglStencilMask(~0);
885                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
886                 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
887                 qglStencilMask(~0);
888                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
889         }
890         else
891         {
892                 qglEnable(GL_CULL_FACE);
893                 qglStencilMask(~0);
894                 // this is changed by every shadow render so its value here is unimportant
895                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
896         }
897         GL_Clear(GL_STENCIL_BUFFER_BIT);
898         renderstats.lights_clears++;
899 }
900
901 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
902 {
903         R_Shadow_RenderMode_Reset();
904         GL_BlendFunc(GL_ONE, GL_ONE);
905         GL_DepthMask(false);
906         GL_DepthTest(true);
907         if (!r_showtrispass)
908                 qglPolygonOffset(0, 0);
909         //qglDisable(GL_POLYGON_OFFSET_FILL);
910         GL_Color(1, 1, 1, 1);
911         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
912         if (transparent)
913                 qglDepthFunc(GL_LEQUAL);
914         else
915                 qglDepthFunc(GL_EQUAL);
916         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
917         qglEnable(GL_CULL_FACE);
918         if (stenciltest)
919                 qglEnable(GL_STENCIL_TEST);
920         else
921                 qglDisable(GL_STENCIL_TEST);
922         qglStencilMask(~0);
923         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
924         // only draw light where this geometry was already rendered AND the
925         // stencil is 128 (values other than this mean shadow)
926         qglStencilFunc(GL_EQUAL, 128, ~0);
927         r_shadow_rendermode = r_shadow_lightingrendermode;
928         // do global setup needed for the chosen lighting mode
929         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
930         {
931                 R_Mesh_TexCoordPointer(1, 3, varray_svector3f);
932                 R_Mesh_TexCoordPointer(2, 3, varray_tvector3f);
933                 R_Mesh_TexCoordPointer(3, 3, varray_normal3f);
934                 R_Mesh_TexBind(0, R_GetTexture(r_texture_blanknormalmap)); // normal
935                 R_Mesh_TexBind(1, R_GetTexture(r_texture_white)); // diffuse
936                 R_Mesh_TexBind(2, R_GetTexture(r_texture_white)); // gloss
937                 R_Mesh_TexBindCubeMap(3, R_GetTexture(r_shadow_rtlight->currentcubemap)); // light filter
938                 R_Mesh_TexBind(4, R_GetTexture(r_texture_fogattenuation)); // fog
939                 R_Mesh_TexBind(5, R_GetTexture(r_texture_white)); // pants
940                 R_Mesh_TexBind(6, R_GetTexture(r_texture_white)); // shirt
941                 //R_Mesh_TexMatrix(3, r_shadow_entitytolight); // light filter matrix
942                 GL_BlendFunc(GL_ONE, GL_ONE);
943                 GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
944                 CHECKGLERROR
945         }
946 }
947
948 void R_Shadow_RenderMode_VisibleShadowVolumes(void)
949 {
950         R_Shadow_RenderMode_Reset();
951         GL_BlendFunc(GL_ONE, GL_ONE);
952         GL_DepthMask(false);
953         GL_DepthTest(!r_showdisabledepthtest.integer);
954         if (!r_showtrispass)
955                 qglPolygonOffset(0, 0);
956         GL_Color(0.0, 0.0125, 0.1, 1);
957         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
958         qglDepthFunc(GL_GEQUAL);
959         qglCullFace(GL_FRONT); // this culls back
960         qglDisable(GL_CULL_FACE);
961         qglDisable(GL_STENCIL_TEST);
962         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLEVOLUMES;
963 }
964
965 void R_Shadow_RenderMode_VisibleLighting(qboolean stenciltest, qboolean transparent)
966 {
967         R_Shadow_RenderMode_Reset();
968         GL_BlendFunc(GL_ONE, GL_ONE);
969         GL_DepthMask(false);
970         GL_DepthTest(!r_showdisabledepthtest.integer);
971         if (!r_showtrispass)
972                 qglPolygonOffset(0, 0);
973         GL_Color(0.1, 0.0125, 0, 1);
974         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
975         if (transparent)
976                 qglDepthFunc(GL_LEQUAL);
977         else
978                 qglDepthFunc(GL_EQUAL);
979         qglCullFace(GL_FRONT); // this culls back
980         qglEnable(GL_CULL_FACE);
981         if (stenciltest)
982                 qglEnable(GL_STENCIL_TEST);
983         else
984                 qglDisable(GL_STENCIL_TEST);
985         r_shadow_rendermode = R_SHADOW_RENDERMODE_VISIBLELIGHTING;
986 }
987
988 void R_Shadow_RenderMode_End(void)
989 {
990         R_Shadow_RenderMode_Reset();
991         R_Shadow_RenderMode_ActiveLight(NULL);
992         GL_BlendFunc(GL_ONE, GL_ZERO);
993         GL_DepthMask(true);
994         GL_DepthTest(true);
995         if (!r_showtrispass)
996                 qglPolygonOffset(0, 0);
997         //qglDisable(GL_POLYGON_OFFSET_FILL);
998         GL_Color(1, 1, 1, 1);
999         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
1000         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1001         qglDepthFunc(GL_LEQUAL);
1002         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
1003         qglEnable(GL_CULL_FACE);
1004         qglDisable(GL_STENCIL_TEST);
1005         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
1006         if (gl_support_stenciltwoside)
1007                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
1008         qglStencilMask(~0);
1009         qglStencilFunc(GL_ALWAYS, 128, ~0);
1010         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
1011 }
1012
1013 qboolean R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1014 {
1015         int i, ix1, iy1, ix2, iy2;
1016         float x1, y1, x2, y2;
1017         vec4_t v, v2;
1018         rmesh_t mesh;
1019         mplane_t planes[11];
1020         float vertex3f[256*3];
1021
1022         // if view is inside the light box, just say yes it's visible
1023         if (BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
1024         {
1025                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
1026                 return false;
1027         }
1028
1029         // create a temporary brush describing the area the light can affect in worldspace
1030         VectorNegate(frustum[0].normal, planes[ 0].normal);planes[ 0].dist = -frustum[0].dist;
1031         VectorNegate(frustum[1].normal, planes[ 1].normal);planes[ 1].dist = -frustum[1].dist;
1032         VectorNegate(frustum[2].normal, planes[ 2].normal);planes[ 2].dist = -frustum[2].dist;
1033         VectorNegate(frustum[3].normal, planes[ 3].normal);planes[ 3].dist = -frustum[3].dist;
1034         VectorNegate(frustum[4].normal, planes[ 4].normal);planes[ 4].dist = -frustum[4].dist;
1035         VectorSet   (planes[ 5].normal,  1, 0, 0);         planes[ 5].dist =  maxs[0];
1036         VectorSet   (planes[ 6].normal, -1, 0, 0);         planes[ 6].dist = -mins[0];
1037         VectorSet   (planes[ 7].normal, 0,  1, 0);         planes[ 7].dist =  maxs[1];
1038         VectorSet   (planes[ 8].normal, 0, -1, 0);         planes[ 8].dist = -mins[1];
1039         VectorSet   (planes[ 9].normal, 0, 0,  1);         planes[ 9].dist =  maxs[2];
1040         VectorSet   (planes[10].normal, 0, 0, -1);         planes[10].dist = -mins[2];
1041
1042         // turn the brush into a mesh
1043         memset(&mesh, 0, sizeof(rmesh_t));
1044         mesh.maxvertices = 256;
1045         mesh.vertex3f = vertex3f;
1046         mesh.epsilon2 = (1.0f / (32.0f * 32.0f));
1047         R_Mesh_AddBrushMeshFromPlanes(&mesh, 11, planes);
1048
1049         // if that mesh is empty, the light is not visible at all
1050         if (!mesh.numvertices)
1051                 return true;
1052
1053         if (!r_shadow_scissor.integer)
1054                 return false;
1055
1056         // if that mesh is not empty, check what area of the screen it covers
1057         x1 = y1 = x2 = y2 = 0;
1058         v[3] = 1.0f;
1059         for (i = 0;i < mesh.numvertices;i++)
1060         {
1061                 VectorCopy(mesh.vertex3f + i * 3, v);
1062                 GL_TransformToScreen(v, v2);
1063                 //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]);
1064                 if (i)
1065                 {
1066                         if (x1 > v2[0]) x1 = v2[0];
1067                         if (x2 < v2[0]) x2 = v2[0];
1068                         if (y1 > v2[1]) y1 = v2[1];
1069                         if (y2 < v2[1]) y2 = v2[1];
1070                 }
1071                 else
1072                 {
1073                         x1 = x2 = v2[0];
1074                         y1 = y2 = v2[1];
1075                 }
1076         }
1077
1078         // now convert the scissor rectangle to integer screen coordinates
1079         ix1 = x1 - 1.0f;
1080         iy1 = y1 - 1.0f;
1081         ix2 = x2 + 1.0f;
1082         iy2 = y2 + 1.0f;
1083         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1084
1085         // clamp it to the screen
1086         if (ix1 < r_view_x) ix1 = r_view_x;
1087         if (iy1 < r_view_y) iy1 = r_view_y;
1088         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1089         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1090
1091         // if it is inside out, it's not visible
1092         if (ix2 <= ix1 || iy2 <= iy1)
1093                 return true;
1094
1095         // the light area is visible, set up the scissor rectangle
1096         GL_Scissor(ix1, vid.height - iy2, ix2 - ix1, iy2 - iy1);
1097         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1098         //qglEnable(GL_SCISSOR_TEST);
1099         renderstats.lights_scissored++;
1100         return false;
1101 }
1102
1103 extern float *rsurface_vertex3f;
1104 extern float *rsurface_svector3f;
1105 extern float *rsurface_tvector3f;
1106 extern float *rsurface_normal3f;
1107 extern void RSurf_SetVertexPointer(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t modelorg, qboolean generatenormals, qboolean generatetangents);
1108
1109 static void R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(const msurface_t *surface, const float *diffusecolor, const float *ambientcolor)
1110 {
1111         int numverts = surface->num_vertices;
1112         float *vertex3f = rsurface_vertex3f + 3 * surface->num_firstvertex;
1113         float *normal3f = rsurface_normal3f + 3 * surface->num_firstvertex;
1114         float *color4f = varray_color4f + 4 * surface->num_firstvertex;
1115         float dist, dot, distintensity, shadeintensity, v[3], n[3];
1116         if (r_textureunits.integer >= 3)
1117         {
1118                 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1119                 {
1120                         Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1121                         Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1122                         if ((dot = DotProduct(n, v)) < 0)
1123                         {
1124                                 shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1125                                 color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]);
1126                                 color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]);
1127                                 color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]);
1128                                 if (fogenabled)
1129                                 {
1130                                         float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1131                                         VectorScale(color4f, f, color4f);
1132                                 }
1133                         }
1134                         else
1135                                 VectorClear(color4f);
1136                         color4f[3] = 1;
1137                 }
1138         }
1139         else if (r_textureunits.integer >= 2)
1140         {
1141                 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1142                 {
1143                         Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1144                         if ((dist = fabs(v[2])) < 1)
1145                         {
1146                                 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1147                                 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1148                                 if ((dot = DotProduct(n, v)) < 0)
1149                                 {
1150                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1151                                         color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1152                                         color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1153                                         color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1154                                 }
1155                                 else
1156                                 {
1157                                         color4f[0] = ambientcolor[0] * distintensity;
1158                                         color4f[1] = ambientcolor[1] * distintensity;
1159                                         color4f[2] = ambientcolor[2] * distintensity;
1160                                 }
1161                                 if (fogenabled)
1162                                 {
1163                                         float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1164                                         VectorScale(color4f, f, color4f);
1165                                 }
1166                         }
1167                         else
1168                                 VectorClear(color4f);
1169                         color4f[3] = 1;
1170                 }
1171         }
1172         else
1173         {
1174                 for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1175                 {
1176                         Matrix4x4_Transform(&r_shadow_entitytolight, vertex3f, v);
1177                         if ((dist = DotProduct(v, v)) < 1)
1178                         {
1179                                 dist = sqrt(dist);
1180                                 distintensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale;
1181                                 Matrix4x4_Transform3x3(&r_shadow_entitytolight, normal3f, n);
1182                                 if ((dot = DotProduct(n, v)) < 0)
1183                                 {
1184                                         shadeintensity = -dot / sqrt(VectorLength2(v) * VectorLength2(n));
1185                                         color4f[0] = (ambientcolor[0] + shadeintensity * diffusecolor[0]) * distintensity;
1186                                         color4f[1] = (ambientcolor[1] + shadeintensity * diffusecolor[1]) * distintensity;
1187                                         color4f[2] = (ambientcolor[2] + shadeintensity * diffusecolor[2]) * distintensity;
1188                                 }
1189                                 else
1190                                 {
1191                                         color4f[0] = ambientcolor[0] * distintensity;
1192                                         color4f[1] = ambientcolor[1] * distintensity;
1193                                         color4f[2] = ambientcolor[2] * distintensity;
1194                                 }
1195                                 if (fogenabled)
1196                                 {
1197                                         float f = VERTEXFOGTABLE(VectorDistance(v, r_shadow_entityeyeorigin));
1198                                         VectorScale(color4f, f, color4f);
1199                                 }
1200                         }
1201                         else
1202                                 VectorClear(color4f);
1203                         color4f[3] = 1;
1204                 }
1205         }
1206 }
1207
1208 // TODO: use glTexGen instead of feeding vertices to texcoordpointer?
1209
1210 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)
1211 {
1212         int i;
1213         float lightdir[3];
1214         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1215         {
1216                 VectorSubtract(relativelightorigin, vertex3f, lightdir);
1217                 // the cubemap normalizes this for us
1218                 out3f[0] = DotProduct(svector3f, lightdir);
1219                 out3f[1] = DotProduct(tvector3f, lightdir);
1220                 out3f[2] = DotProduct(normal3f, lightdir);
1221         }
1222 }
1223
1224 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)
1225 {
1226         int i;
1227         float lightdir[3], eyedir[3], halfdir[3];
1228         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1229         {
1230                 VectorSubtract(relativelightorigin, vertex3f, lightdir);
1231                 VectorNormalize(lightdir);
1232                 VectorSubtract(relativeeyeorigin, vertex3f, eyedir);
1233                 VectorNormalize(eyedir);
1234                 VectorAdd(lightdir, eyedir, halfdir);
1235                 // the cubemap normalizes this for us
1236                 out3f[0] = DotProduct(svector3f, halfdir);
1237                 out3f[1] = DotProduct(tvector3f, halfdir);
1238                 out3f[2] = DotProduct(normal3f, halfdir);
1239         }
1240 }
1241
1242 static void R_Shadow_RenderSurfacesLighting_VisibleLighting(const entity_render_t *ent, const texture_t *texture, 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)
1243 {
1244         // used to display how many times a surface is lit for level design purposes
1245         int surfacelistindex;
1246         rmeshstate_t m;
1247         GL_Color(0.1, 0.025, 0, 1);
1248         memset(&m, 0, sizeof(m));
1249         R_Mesh_State(&m);
1250         for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1251         {
1252                 const msurface_t *surface = surfacelist[surfacelistindex];
1253                 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, false);
1254                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1255                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, surface->groupmesh->data_element3i + 3 * surface->num_firsttriangle);
1256                 GL_LockArrays(0, 0);
1257         }
1258 }
1259
1260 static void R_Shadow_RenderSurfacesLighting_Light_GLSL(const entity_render_t *ent, const texture_t *texture, 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)
1261 {
1262         // ARB2 GLSL shader path (GFFX5200, Radeon 9500)
1263         int surfacelistindex;
1264         R_SetupSurfaceShader(ent, texture, r_shadow_entityeyeorigin, lightcolorbase, false);
1265         for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1266         {
1267                 const msurface_t *surface = surfacelist[surfacelistindex];
1268                 const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1269                 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, true);
1270                 R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
1271                 R_Mesh_TexCoordPointer(1, 3, rsurface_svector3f);
1272                 R_Mesh_TexCoordPointer(2, 3, rsurface_tvector3f);
1273                 R_Mesh_TexCoordPointer(3, 3, rsurface_normal3f);
1274                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1275                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1276                 GL_LockArrays(0, 0);
1277         }
1278 }
1279
1280 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, float colorscale)
1281 {
1282         int renders;
1283         float color2[3];
1284         rmeshstate_t m;
1285         const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1286         GL_Color(1,1,1,1);
1287         // colorscale accounts for how much we multiply the brightness
1288         // during combine.
1289         //
1290         // mult is how many times the final pass of the lighting will be
1291         // performed to get more brightness than otherwise possible.
1292         //
1293         // Limit mult to 64 for sanity sake.
1294         if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap != r_texture_whitecube && r_textureunits.integer >= 4)
1295         {
1296                 // 3 3D combine path (Geforce3, Radeon 8500)
1297                 memset(&m, 0, sizeof(m));
1298                 m.pointer_vertex = rsurface_vertex3f;
1299                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1300                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1301                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1302                 m.tex[1] = R_GetTexture(basetexture);
1303                 m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
1304                 m.texmatrix[1] = texture->currenttexmatrix;
1305                 m.texcubemap[2] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1306                 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1307                 m.texmatrix[2] = r_shadow_entitytolight;
1308                 GL_BlendFunc(GL_ONE, GL_ONE);
1309         }
1310         else if (r_shadow_texture3d.integer && r_shadow_rtlight->currentcubemap == r_texture_whitecube && r_textureunits.integer >= 2)
1311         {
1312                 // 2 3D combine path (Geforce3, original Radeon)
1313                 memset(&m, 0, sizeof(m));
1314                 m.pointer_vertex = rsurface_vertex3f;
1315                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1316                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1317                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1318                 m.tex[1] = R_GetTexture(basetexture);
1319                 m.pointer_texcoord[1] = surface->groupmesh->data_texcoordtexture2f;
1320                 m.texmatrix[1] = texture->currenttexmatrix;
1321                 GL_BlendFunc(GL_ONE, GL_ONE);
1322         }
1323         else if (r_textureunits.integer >= 4 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1324         {
1325                 // 4 2D combine path (Geforce3, Radeon 8500)
1326                 memset(&m, 0, sizeof(m));
1327                 m.pointer_vertex = rsurface_vertex3f;
1328                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1329                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1330                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1331                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1332                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1333                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1334                 m.tex[2] = R_GetTexture(basetexture);
1335                 m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
1336                 m.texmatrix[2] = texture->currenttexmatrix;
1337                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1338                 {
1339                         m.texcubemap[3] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1340                         m.pointer_texcoord3f[3] = rsurface_vertex3f;
1341                         m.texmatrix[3] = r_shadow_entitytolight;
1342                 }
1343                 GL_BlendFunc(GL_ONE, GL_ONE);
1344         }
1345         else if (r_textureunits.integer >= 3 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1346         {
1347                 // 3 2D combine path (Geforce3, original Radeon)
1348                 memset(&m, 0, sizeof(m));
1349                 m.pointer_vertex = rsurface_vertex3f;
1350                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1351                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1352                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1353                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1354                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1355                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1356                 m.tex[2] = R_GetTexture(basetexture);
1357                 m.pointer_texcoord[2] = surface->groupmesh->data_texcoordtexture2f;
1358                 m.texmatrix[2] = texture->currenttexmatrix;
1359                 GL_BlendFunc(GL_ONE, GL_ONE);
1360         }
1361         else
1362         {
1363                 // 2/2/2 2D combine path (any dot3 card)
1364                 memset(&m, 0, sizeof(m));
1365                 m.pointer_vertex = rsurface_vertex3f;
1366                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1367                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1368                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1369                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1370                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1371                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1372                 R_Mesh_State(&m);
1373                 GL_ColorMask(0,0,0,1);
1374                 GL_BlendFunc(GL_ONE, GL_ZERO);
1375                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1376                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1377                 GL_LockArrays(0, 0);
1378
1379                 memset(&m, 0, sizeof(m));
1380                 m.pointer_vertex = rsurface_vertex3f;
1381                 m.tex[0] = R_GetTexture(basetexture);
1382                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1383                 m.texmatrix[0] = texture->currenttexmatrix;
1384                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1385                 {
1386                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1387                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1388                         m.texmatrix[1] = r_shadow_entitytolight;
1389                 }
1390                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1391         }
1392         // this final code is shared
1393         R_Mesh_State(&m);
1394         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1395         VectorScale(lightcolorbase, colorscale, color2);
1396         GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1397         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1398         {
1399                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1400                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1401         }
1402         GL_LockArrays(0, 0);
1403 }
1404
1405 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *basetexture, rtexture_t *normalmaptexture, float colorscale)
1406 {
1407         int renders;
1408         float color2[3];
1409         rmeshstate_t m;
1410         const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1411         GL_Color(1,1,1,1);
1412         // colorscale accounts for how much we multiply the brightness
1413         // during combine.
1414         //
1415         // mult is how many times the final pass of the lighting will be
1416         // performed to get more brightness than otherwise possible.
1417         //
1418         // Limit mult to 64 for sanity sake.
1419         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1420         {
1421                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1422                 memset(&m, 0, sizeof(m));
1423                 m.pointer_vertex = rsurface_vertex3f;
1424                 m.tex[0] = R_GetTexture(normalmaptexture);
1425                 m.texcombinergb[0] = GL_REPLACE;
1426                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1427                 m.texmatrix[0] = texture->currenttexmatrix;
1428                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1429                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1430                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1431                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_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);
1432                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1433                 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1434                 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1435                 R_Mesh_State(&m);
1436                 GL_ColorMask(0,0,0,1);
1437                 GL_BlendFunc(GL_ONE, GL_ZERO);
1438                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1439                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1440                 GL_LockArrays(0, 0);
1441
1442                 memset(&m, 0, sizeof(m));
1443                 m.pointer_vertex = rsurface_vertex3f;
1444                 m.tex[0] = R_GetTexture(basetexture);
1445                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1446                 m.texmatrix[0] = texture->currenttexmatrix;
1447                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1448                 {
1449                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1450                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1451                         m.texmatrix[1] = r_shadow_entitytolight;
1452                 }
1453                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1454         }
1455         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1456         {
1457                 // 1/2/2 3D combine path (original Radeon)
1458                 memset(&m, 0, sizeof(m));
1459                 m.pointer_vertex = rsurface_vertex3f;
1460                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1461                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1462                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1463                 R_Mesh_State(&m);
1464                 GL_ColorMask(0,0,0,1);
1465                 GL_BlendFunc(GL_ONE, GL_ZERO);
1466                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1467                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1468                 GL_LockArrays(0, 0);
1469
1470                 memset(&m, 0, sizeof(m));
1471                 m.pointer_vertex = rsurface_vertex3f;
1472                 m.tex[0] = R_GetTexture(normalmaptexture);
1473                 m.texcombinergb[0] = GL_REPLACE;
1474                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1475                 m.texmatrix[0] = texture->currenttexmatrix;
1476                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1477                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1478                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1479                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_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);
1480                 R_Mesh_State(&m);
1481                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1482                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1483                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1484                 GL_LockArrays(0, 0);
1485
1486                 memset(&m, 0, sizeof(m));
1487                 m.pointer_vertex = rsurface_vertex3f;
1488                 m.tex[0] = R_GetTexture(basetexture);
1489                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1490                 m.texmatrix[0] = texture->currenttexmatrix;
1491                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1492                 {
1493                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1494                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1495                         m.texmatrix[1] = r_shadow_entitytolight;
1496                 }
1497                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1498         }
1499         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube)
1500         {
1501                 // 2/2 3D combine path (original Radeon)
1502                 memset(&m, 0, sizeof(m));
1503                 m.pointer_vertex = rsurface_vertex3f;
1504                 m.tex[0] = R_GetTexture(normalmaptexture);
1505                 m.texcombinergb[0] = GL_REPLACE;
1506                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1507                 m.texmatrix[0] = texture->currenttexmatrix;
1508                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1509                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1510                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1511                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_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);
1512                 R_Mesh_State(&m);
1513                 GL_ColorMask(0,0,0,1);
1514                 GL_BlendFunc(GL_ONE, GL_ZERO);
1515                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1516                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1517                 GL_LockArrays(0, 0);
1518
1519                 memset(&m, 0, sizeof(m));
1520                 m.pointer_vertex = rsurface_vertex3f;
1521                 m.tex[0] = R_GetTexture(basetexture);
1522                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1523                 m.texmatrix[0] = texture->currenttexmatrix;
1524                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1525                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1526                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1527                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1528         }
1529         else if (r_textureunits.integer >= 4)
1530         {
1531                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1532                 memset(&m, 0, sizeof(m));
1533                 m.pointer_vertex = rsurface_vertex3f;
1534                 m.tex[0] = R_GetTexture(normalmaptexture);
1535                 m.texcombinergb[0] = GL_REPLACE;
1536                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1537                 m.texmatrix[0] = texture->currenttexmatrix;
1538                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1539                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1540                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1541                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_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);
1542                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1543                 m.pointer_texcoord3f[2] = rsurface_vertex3f;
1544                 m.texmatrix[2] = r_shadow_entitytoattenuationxyz;
1545                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1546                 m.pointer_texcoord3f[3] = rsurface_vertex3f;
1547                 m.texmatrix[3] = r_shadow_entitytoattenuationz;
1548                 R_Mesh_State(&m);
1549                 GL_ColorMask(0,0,0,1);
1550                 GL_BlendFunc(GL_ONE, GL_ZERO);
1551                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1552                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1553                 GL_LockArrays(0, 0);
1554
1555                 memset(&m, 0, sizeof(m));
1556                 m.pointer_vertex = rsurface_vertex3f;
1557                 m.tex[0] = R_GetTexture(basetexture);
1558                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1559                 m.texmatrix[0] = texture->currenttexmatrix;
1560                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1561                 {
1562                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1563                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1564                         m.texmatrix[1] = r_shadow_entitytolight;
1565                 }
1566                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1567         }
1568         else
1569         {
1570                 // 2/2/2 2D combine path (any dot3 card)
1571                 memset(&m, 0, sizeof(m));
1572                 m.pointer_vertex = rsurface_vertex3f;
1573                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1574                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1575                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1576                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1577                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1578                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1579                 R_Mesh_State(&m);
1580                 GL_ColorMask(0,0,0,1);
1581                 GL_BlendFunc(GL_ONE, GL_ZERO);
1582                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1583                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1584                 GL_LockArrays(0, 0);
1585
1586                 memset(&m, 0, sizeof(m));
1587                 m.pointer_vertex = rsurface_vertex3f;
1588                 m.tex[0] = R_GetTexture(normalmaptexture);
1589                 m.texcombinergb[0] = GL_REPLACE;
1590                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1591                 m.texmatrix[0] = texture->currenttexmatrix;
1592                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1593                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1594                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1595                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_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);
1596                 R_Mesh_State(&m);
1597                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1598                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1599                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1600                 GL_LockArrays(0, 0);
1601
1602                 memset(&m, 0, sizeof(m));
1603                 m.pointer_vertex = rsurface_vertex3f;
1604                 m.tex[0] = R_GetTexture(basetexture);
1605                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1606                 m.texmatrix[0] = texture->currenttexmatrix;
1607                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1608                 {
1609                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1610                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1611                         m.texmatrix[1] = r_shadow_entitytolight;
1612                 }
1613                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1614         }
1615         // this final code is shared
1616         R_Mesh_State(&m);
1617         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1618         VectorScale(lightcolorbase, colorscale, color2);
1619         GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1620         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1621         {
1622                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1623                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1624         }
1625         GL_LockArrays(0, 0);
1626 }
1627
1628 static void R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(const entity_render_t *ent, const texture_t *texture, const msurface_t *surface, const vec3_t lightcolorbase, rtexture_t *glosstexture, rtexture_t *normalmaptexture, float colorscale)
1629 {
1630         int renders;
1631         float color2[3];
1632         rmeshstate_t m;
1633         const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1634         // FIXME: detect blendsquare!
1635         //if (!gl_support_blendsquare)
1636         //      return;
1637         GL_Color(1,1,1,1);
1638         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap != r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1639         {
1640                 // 2/0/0/1/2 3D combine blendsquare path
1641                 memset(&m, 0, sizeof(m));
1642                 m.pointer_vertex = rsurface_vertex3f;
1643                 m.tex[0] = R_GetTexture(normalmaptexture);
1644                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1645                 m.texmatrix[0] = texture->currenttexmatrix;
1646                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1647                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1648                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1649                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_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);
1650                 R_Mesh_State(&m);
1651                 GL_ColorMask(0,0,0,1);
1652                 // this squares the result
1653                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1654                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1655                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1656                 GL_LockArrays(0, 0);
1657
1658                 memset(&m, 0, sizeof(m));
1659                 m.pointer_vertex = rsurface_vertex3f;
1660                 R_Mesh_State(&m);
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.pointer_vertex = rsurface_vertex3f;
1674                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1675                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1676                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1677                 R_Mesh_State(&m);
1678                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1679                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1680                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1681                 GL_LockArrays(0, 0);
1682
1683                 memset(&m, 0, sizeof(m));
1684                 m.pointer_vertex = rsurface_vertex3f;
1685                 m.tex[0] = R_GetTexture(glosstexture);
1686                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1687                 m.texmatrix[0] = texture->currenttexmatrix;
1688                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1689                 {
1690                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1691                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1692                         m.texmatrix[1] = r_shadow_entitytolight;
1693                 }
1694                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1695         }
1696         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && r_shadow_rtlight->currentcubemap == r_texture_whitecube /* && gl_support_blendsquare*/) // FIXME: detect blendsquare!
1697         {
1698                 // 2/0/0/2 3D combine blendsquare path
1699                 memset(&m, 0, sizeof(m));
1700                 m.pointer_vertex = rsurface_vertex3f;
1701                 m.tex[0] = R_GetTexture(normalmaptexture);
1702                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1703                 m.texmatrix[0] = texture->currenttexmatrix;
1704                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1705                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1706                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1707                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_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);
1708                 R_Mesh_State(&m);
1709                 GL_ColorMask(0,0,0,1);
1710                 // this squares the result
1711                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1712                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1713                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1714                 GL_LockArrays(0, 0);
1715
1716                 memset(&m, 0, sizeof(m));
1717                 m.pointer_vertex = rsurface_vertex3f;
1718                 R_Mesh_State(&m);
1719                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1720                 // square alpha in framebuffer a few times to make it shiny
1721                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1722                 // these comments are a test run through this math for intensity 0.5
1723                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1724                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1725                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1726                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1727                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1728                 GL_LockArrays(0, 0);
1729
1730                 memset(&m, 0, sizeof(m));
1731                 m.pointer_vertex = rsurface_vertex3f;
1732                 m.tex[0] = R_GetTexture(glosstexture);
1733                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1734                 m.texmatrix[0] = texture->currenttexmatrix;
1735                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1736                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1737                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1738                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1739         }
1740         else
1741         {
1742                 // 2/0/0/2/2 2D combine blendsquare path
1743                 memset(&m, 0, sizeof(m));
1744                 m.pointer_vertex = rsurface_vertex3f;
1745                 m.tex[0] = R_GetTexture(normalmaptexture);
1746                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1747                 m.texmatrix[0] = texture->currenttexmatrix;
1748                 m.texcubemap[1] = R_GetTexture(r_texture_normalizationcube);
1749                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1750                 m.pointer_texcoord3f[1] = varray_texcoord3f;
1751                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_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);
1752                 R_Mesh_State(&m);
1753                 GL_ColorMask(0,0,0,1);
1754                 // this squares the result
1755                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1756                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1757                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1758                 GL_LockArrays(0, 0);
1759
1760                 memset(&m, 0, sizeof(m));
1761                 m.pointer_vertex = rsurface_vertex3f;
1762                 R_Mesh_State(&m);
1763                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1764                 // square alpha in framebuffer a few times to make it shiny
1765                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1766                 // these comments are a test run through this math for intensity 0.5
1767                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1768                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1769                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1770                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1771                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1772                 GL_LockArrays(0, 0);
1773
1774                 memset(&m, 0, sizeof(m));
1775                 m.pointer_vertex = rsurface_vertex3f;
1776                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1777                 m.pointer_texcoord3f[0] = rsurface_vertex3f;
1778                 m.texmatrix[0] = r_shadow_entitytoattenuationxyz;
1779                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1780                 m.pointer_texcoord3f[1] = rsurface_vertex3f;
1781                 m.texmatrix[1] = r_shadow_entitytoattenuationz;
1782                 R_Mesh_State(&m);
1783                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1784                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1785                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1786                 GL_LockArrays(0, 0);
1787
1788                 memset(&m, 0, sizeof(m));
1789                 m.pointer_vertex = rsurface_vertex3f;
1790                 m.tex[0] = R_GetTexture(glosstexture);
1791                 m.pointer_texcoord[0] = surface->groupmesh->data_texcoordtexture2f;
1792                 m.texmatrix[0] = texture->currenttexmatrix;
1793                 if (r_shadow_rtlight->currentcubemap != r_texture_whitecube)
1794                 {
1795                         m.texcubemap[1] = R_GetTexture(r_shadow_rtlight->currentcubemap);
1796                         m.pointer_texcoord3f[1] = rsurface_vertex3f;
1797                         m.texmatrix[1] = r_shadow_entitytolight;
1798                 }
1799                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1800         }
1801         R_Mesh_State(&m);
1802         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 0);
1803         VectorScale(lightcolorbase, colorscale, color2);
1804         GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1805         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1806         {
1807                 GL_Color(bound(0, color2[0], 1), bound(0, color2[1], 1), bound(0, color2[2], 1), 1);
1808                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1809         }
1810         GL_LockArrays(0, 0);
1811 }
1812
1813 static void R_Shadow_RenderSurfacesLighting_Light_Dot3(const entity_render_t *ent, const texture_t *texture, 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)
1814 {
1815         // ARB path (any Geforce, any Radeon)
1816         int surfacelistindex;
1817         qboolean doambient = r_shadow_rtlight->ambientscale > 0;
1818         qboolean dodiffuse = r_shadow_rtlight->diffusescale > 0;
1819         qboolean dospecular = specularscale > 0;
1820         if (!doambient && !dodiffuse && !dospecular)
1821                 return;
1822         for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1823         {
1824                 const msurface_t *surface = surfacelist[surfacelistindex];
1825                 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, false, true);
1826                 if (doambient)
1827                         R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorbase, basetexture, r_shadow_rtlight->ambientscale);
1828                 if (dodiffuse)
1829                         R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorbase, basetexture, normalmaptexture, r_shadow_rtlight->diffusescale);
1830                 if (dopants)
1831                 {
1832                         if (doambient)
1833                                 R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorpants, pantstexture, r_shadow_rtlight->ambientscale);
1834                         if (dodiffuse)
1835                                 R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorpants, pantstexture, normalmaptexture, r_shadow_rtlight->diffusescale);
1836                 }
1837                 if (doshirt)
1838                 {
1839                         if (doambient)
1840                                 R_Shadow_RenderSurfacesLighting_Light_Dot3_AmbientPass(ent, texture, surface, lightcolorshirt, shirttexture, r_shadow_rtlight->ambientscale);
1841                         if (dodiffuse)
1842                                 R_Shadow_RenderSurfacesLighting_Light_Dot3_DiffusePass(ent, texture, surface, lightcolorshirt, shirttexture, normalmaptexture, r_shadow_rtlight->diffusescale);
1843                 }
1844                 if (dospecular)
1845                         R_Shadow_RenderSurfacesLighting_Light_Dot3_SpecularPass(ent, texture, surface, lightcolorbase, glosstexture, normalmaptexture, specularscale);
1846         }
1847 }
1848
1849 void R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(const msurface_t *surface, vec3_t diffusecolor2, vec3_t ambientcolor2)
1850 {
1851         int renders;
1852         const int *elements = surface->groupmesh->data_element3i + surface->num_firsttriangle * 3;
1853         R_Shadow_RenderSurfacesLighting_Light_Vertex_Shading(surface, diffusecolor2, ambientcolor2);
1854         for (renders = 0;renders < 64 && (ambientcolor2[0] > renders || ambientcolor2[1] > renders || ambientcolor2[2] > renders || diffusecolor2[0] > renders || diffusecolor2[1] > renders || diffusecolor2[2] > renders);renders++)
1855         {
1856                 int i;
1857                 float *c;
1858 #if 1
1859                 // due to low fillrate on the cards this vertex lighting path is
1860                 // designed for, we manually cull all triangles that do not
1861                 // contain a lit vertex
1862                 int draw;
1863                 const int *e;
1864                 int newnumtriangles;
1865                 int *newe;
1866                 int newelements[3072];
1867                 draw = false;
1868                 newnumtriangles = 0;
1869                 newe = newelements;
1870                 for (i = 0, e = elements;i < surface->num_triangles;i++, e += 3)
1871                 {
1872                         if (newnumtriangles >= 1024)
1873                         {
1874                                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1875                                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
1876                                 GL_LockArrays(0, 0);
1877                                 newnumtriangles = 0;
1878                                 newe = newelements;
1879                         }
1880                         if (VectorLength2(varray_color4f + e[0] * 4) + VectorLength2(varray_color4f + e[1] * 4) + VectorLength2(varray_color4f + e[2] * 4) >= 0.01)
1881                         {
1882                                 newe[0] = e[0];
1883                                 newe[1] = e[1];
1884                                 newe[2] = e[2];
1885                                 newnumtriangles++;
1886                                 newe += 3;
1887                                 draw = true;
1888                         }
1889                 }
1890                 if (newnumtriangles >= 1)
1891                 {
1892                         GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1893                         R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, newnumtriangles, newelements);
1894                         GL_LockArrays(0, 0);
1895                         draw = true;
1896                 }
1897                 if (!draw)
1898                         break;
1899 #else
1900                 for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
1901                         if (VectorLength2(c))
1902                                 goto goodpass;
1903                 break;
1904 goodpass:
1905                 GL_LockArrays(surface->num_firstvertex, surface->num_vertices);
1906                 R_Mesh_Draw(surface->num_firstvertex, surface->num_vertices, surface->num_triangles, elements);
1907                 GL_LockArrays(0, 0);
1908 #endif
1909                 // now reduce the intensity for the next overbright pass
1910                 for (i = 0, c = varray_color4f + 4 * surface->num_firstvertex;i < surface->num_vertices;i++, c += 4)
1911                 {
1912                         c[0] = max(0, c[0] - 1);
1913                         c[1] = max(0, c[1] - 1);
1914                         c[2] = max(0, c[2] - 1);
1915                 }
1916         }
1917 }
1918
1919 static void R_Shadow_RenderSurfacesLighting_Light_Vertex(const entity_render_t *ent, const texture_t *texture, 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)
1920 {
1921         int surfacelistindex;
1922         float ambientcolorbase[3], diffusecolorbase[3];
1923         float ambientcolorpants[3], diffusecolorpants[3];
1924         float ambientcolorshirt[3], diffusecolorshirt[3];
1925         rmeshstate_t m;
1926         VectorScale(lightcolorbase, r_shadow_rtlight->ambientscale * 2, ambientcolorbase);
1927         VectorScale(lightcolorbase, r_shadow_rtlight->diffusescale * 2, diffusecolorbase);
1928         VectorScale(lightcolorpants, r_shadow_rtlight->ambientscale * 2, ambientcolorpants);
1929         VectorScale(lightcolorpants, r_shadow_rtlight->diffusescale * 2, diffusecolorpants);
1930         VectorScale(lightcolorshirt, r_shadow_rtlight->ambientscale * 2, ambientcolorshirt);
1931         VectorScale(lightcolorshirt, r_shadow_rtlight->diffusescale * 2, diffusecolorshirt);
1932         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1933         memset(&m, 0, sizeof(m));
1934         m.tex[0] = R_GetTexture(basetexture);
1935         if (r_textureunits.integer >= 2)
1936         {
1937                 // voodoo2
1938                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1939                 m.texmatrix[1] = r_shadow_entitytoattenuationxyz;
1940                 if (r_textureunits.integer >= 3)
1941                 {
1942                         // Geforce3/Radeon class but not using dot3
1943                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1944                         m.texmatrix[2] = r_shadow_entitytoattenuationz;
1945                 }
1946         }
1947         m.pointer_color = varray_color4f;
1948         R_Mesh_State(&m);
1949         for (surfacelistindex = 0;surfacelistindex < numsurfaces;surfacelistindex++)
1950         {
1951                 const msurface_t *surface = surfacelist[surfacelistindex];
1952                 RSurf_SetVertexPointer(ent, texture, surface, r_shadow_entityeyeorigin, true, false);
1953                 // OpenGL 1.1 path (anything)
1954                 R_Mesh_TexCoordPointer(0, 2, surface->groupmesh->data_texcoordtexture2f);
1955                 R_Mesh_TexMatrix(0, &texture->currenttexmatrix);
1956                 if (r_textureunits.integer >= 2)
1957                 {
1958                         // voodoo2 or TNT
1959                         R_Mesh_TexCoordPointer(1, 3, rsurface_vertex3f);
1960                         if (r_textureunits.integer >= 3)
1961                         {
1962                                 // Voodoo4 or Kyro (or Geforce3/Radeon with gl_combine off)
1963                                 R_Mesh_TexCoordPointer(2, 3, rsurface_vertex3f);
1964                         }
1965                 }
1966                 R_Mesh_TexBind(0, R_GetTexture(basetexture));
1967                 R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorbase, ambientcolorbase);
1968                 if (dopants)
1969                 {
1970                         R_Mesh_TexBind(0, R_GetTexture(pantstexture));
1971                         R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorpants, ambientcolorpants);
1972                 }
1973                 if (doshirt)
1974                 {
1975                         R_Mesh_TexBind(0, R_GetTexture(shirttexture));
1976                         R_Shadow_RenderSurfacesLighting_Light_Vertex_Pass(surface, diffusecolorshirt, ambientcolorshirt);
1977                 }
1978         }
1979 }
1980
1981 void R_Shadow_RenderSurfacesLighting(const entity_render_t *ent, const texture_t *texture, int numsurfaces, msurface_t **surfacelist)
1982 {
1983         // FIXME: support MATERIALFLAG_NODEPTHTEST
1984         vec3_t lightcolorbase, lightcolorpants, lightcolorshirt;
1985         // calculate colors to render this texture with
1986         lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * ent->colormod[0] * texture->currentalpha;
1987         lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * ent->colormod[1] * texture->currentalpha;
1988         lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * ent->colormod[2] * texture->currentalpha;
1989         if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
1990                 return;
1991         if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
1992                 qglDisable(GL_CULL_FACE);
1993         else
1994                 qglEnable(GL_CULL_FACE);
1995         if (texture->colormapping)
1996         {
1997                 qboolean dopants = texture->skin.pants != NULL && VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f);
1998                 qboolean doshirt = texture->skin.shirt != NULL && VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
1999                 if (dopants)
2000                 {
2001                         lightcolorpants[0] = lightcolorbase[0] * ent->colormap_pantscolor[0];
2002                         lightcolorpants[1] = lightcolorbase[1] * ent->colormap_pantscolor[1];
2003                         lightcolorpants[2] = lightcolorbase[2] * ent->colormap_pantscolor[2];
2004                 }
2005                 else
2006                         VectorClear(lightcolorpants);
2007                 if (doshirt)
2008                 {
2009                         lightcolorshirt[0] = lightcolorbase[0] * ent->colormap_shirtcolor[0];
2010                         lightcolorshirt[1] = lightcolorbase[1] * ent->colormap_shirtcolor[1];
2011                         lightcolorshirt[2] = lightcolorbase[2] * ent->colormap_shirtcolor[2];
2012                 }
2013                 else
2014                         VectorClear(lightcolorshirt);
2015                 switch (r_shadow_rendermode)
2016                 {
2017                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2018                         R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2019                         break;
2020                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2021                         R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2022                         break;
2023                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2024                         R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2025                         break;
2026                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2027                         R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2028                         break;
2029                 default:
2030                         Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2031                         break;
2032                 }
2033         }
2034         else
2035         {
2036                 switch (r_shadow_rendermode)
2037                 {
2038                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2039                         R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2040                         break;
2041                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2042                         R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2043                         break;
2044                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2045                         R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2046                         break;
2047                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2048                         R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2049                         break;
2050                 default:
2051                         Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2052                         break;
2053                 }
2054         }
2055 }
2056
2057 void R_RTLight_Update(dlight_t *light, int isstatic)
2058 {
2059         int j, k;
2060         float scale;
2061         rtlight_t *rtlight = &light->rtlight;
2062         R_RTLight_Uncompile(rtlight);
2063         memset(rtlight, 0, sizeof(*rtlight));
2064
2065         VectorCopy(light->origin, rtlight->shadoworigin);
2066         VectorCopy(light->color, rtlight->color);
2067         rtlight->radius = light->radius;
2068         //rtlight->cullradius = rtlight->radius;
2069         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2070         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2071         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2072         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2073         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2074         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2075         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2076         rtlight->cubemapname[0] = 0;
2077         if (light->cubemapname[0])
2078                 strcpy(rtlight->cubemapname, light->cubemapname);
2079         else if (light->cubemapnum > 0)
2080                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2081         rtlight->shadow = light->shadow;
2082         rtlight->corona = light->corona;
2083         rtlight->style = light->style;
2084         rtlight->isstatic = isstatic;
2085         rtlight->coronasizescale = light->coronasizescale;
2086         rtlight->ambientscale = light->ambientscale;
2087         rtlight->diffusescale = light->diffusescale;
2088         rtlight->specularscale = light->specularscale;
2089         rtlight->flags = light->flags;
2090         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2091         // ConcatScale won't work here because this needs to scale rotate and
2092         // translate, not just rotate
2093         scale = 1.0f / rtlight->radius;
2094         for (k = 0;k < 3;k++)
2095                 for (j = 0;j < 4;j++)
2096                         rtlight->matrix_worldtolight.m[k][j] *= scale;
2097 }
2098
2099 // compiles rtlight geometry
2100 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2101 void R_RTLight_Compile(rtlight_t *rtlight)
2102 {
2103         int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
2104         entity_render_t *ent = r_refdef.worldentity;
2105         model_t *model = r_refdef.worldmodel;
2106         unsigned char *data;
2107
2108         // compile the light
2109         rtlight->compiled = true;
2110         rtlight->static_numleafs = 0;
2111         rtlight->static_numleafpvsbytes = 0;
2112         rtlight->static_leaflist = NULL;
2113         rtlight->static_leafpvs = NULL;
2114         rtlight->static_numsurfaces = 0;
2115         rtlight->static_surfacelist = NULL;
2116         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2117         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2118         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2119         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2120         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2121         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2122
2123         if (model && model->GetLightInfo)
2124         {
2125                 // this variable must be set for the CompileShadowVolume code
2126                 r_shadow_compilingrtlight = rtlight;
2127                 R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
2128                 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);
2129                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2130                 data = (unsigned char *)Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
2131                 rtlight->static_numleafs = numleafs;
2132                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2133                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2134                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2135                 rtlight->static_numsurfaces = numsurfaces;
2136                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2137                 if (numleafs)
2138                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2139                 if (numleafpvsbytes)
2140                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2141                 if (numsurfaces)
2142                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2143                 if (model->CompileShadowVolume && rtlight->shadow)
2144                         model->CompileShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2145                 // now we're done compiling the rtlight
2146                 r_shadow_compilingrtlight = NULL;
2147         }
2148
2149
2150         // use smallest available cullradius - box radius or light radius
2151         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2152         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2153
2154         shadowmeshes = 0;
2155         shadowtris = 0;
2156         if (rtlight->static_meshchain_shadow)
2157         {
2158                 shadowmesh_t *mesh;
2159                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2160                 {
2161                         shadowmeshes++;
2162                         shadowtris += mesh->numtriangles;
2163                 }
2164         }
2165
2166         Con_DPrintf("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);
2167 }
2168
2169 void R_RTLight_Uncompile(rtlight_t *rtlight)
2170 {
2171         if (rtlight->compiled)
2172         {
2173                 if (rtlight->static_meshchain_shadow)
2174                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2175                 rtlight->static_meshchain_shadow = NULL;
2176                 // these allocations are grouped
2177                 if (rtlight->static_leaflist)
2178                         Mem_Free(rtlight->static_leaflist);
2179                 rtlight->static_numleafs = 0;
2180                 rtlight->static_numleafpvsbytes = 0;
2181                 rtlight->static_leaflist = NULL;
2182                 rtlight->static_leafpvs = NULL;
2183                 rtlight->static_numsurfaces = 0;
2184                 rtlight->static_surfacelist = NULL;
2185                 rtlight->compiled = false;
2186         }
2187 }
2188
2189 void R_Shadow_UncompileWorldLights(void)
2190 {
2191         dlight_t *light;
2192         for (light = r_shadow_worldlightchain;light;light = light->next)
2193                 R_RTLight_Uncompile(&light->rtlight);
2194 }
2195
2196 void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfacelist)
2197 {
2198         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2199         vec_t relativeshadowradius;
2200         if (ent == r_refdef.worldentity)
2201         {
2202                 if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2203                 {
2204                         shadowmesh_t *mesh;
2205                         R_Mesh_Matrix(&ent->matrix);
2206                         for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2207                         {
2208                                 renderstats.lights_shadowtriangles += mesh->numtriangles;
2209                                 R_Mesh_VertexPointer(mesh->vertex3f);
2210                                 GL_LockArrays(0, mesh->numverts);
2211                                 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2212                                 {
2213                                         // decrement stencil if backface is behind depthbuffer
2214                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2215                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2216                                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2217                                         // increment stencil if frontface is behind depthbuffer
2218                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2219                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2220                                 }
2221                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2222                                 GL_LockArrays(0, 0);
2223                         }
2224                 }
2225                 else if (numsurfaces)
2226                 {
2227                         R_Mesh_Matrix(&ent->matrix);
2228                         ent->model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
2229                 }
2230         }
2231         else
2232         {
2233                 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
2234                 relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
2235                 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2236                 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2237                 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2238                 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2239                 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2240                 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2241                 R_Mesh_Matrix(&ent->matrix);
2242                 ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2243         }
2244 }
2245
2246 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2247 {
2248         // set up properties for rendering light onto this entity
2249         Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
2250         Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2251         Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2252         Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2253         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
2254         R_Mesh_Matrix(&ent->matrix);
2255 }
2256
2257 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2258 {
2259         R_Shadow_SetupEntityLight(ent);
2260         if (ent == r_refdef.worldentity)
2261                 ent->model->DrawLight(ent, numsurfaces, surfacelist);
2262         else
2263                 ent->model->DrawLight(ent, ent->model->nummodelsurfaces, ent->model->surfacelist);
2264 }
2265
2266 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2267 {
2268         int i, usestencil;
2269         float f;
2270         int numleafs, numsurfaces;
2271         int *leaflist, *surfacelist;
2272         unsigned char *leafpvs;
2273         int numlightentities;
2274         int numshadowentities;
2275         entity_render_t *lightentities[MAX_EDICTS];
2276         entity_render_t *shadowentities[MAX_EDICTS];
2277
2278         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2279         // skip lights that are basically invisible (color 0 0 0)
2280         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2281                 return;
2282
2283         // loading is done before visibility checks because loading should happen
2284         // all at once at the start of a level, not when it stalls gameplay.
2285         // (especially important to benchmarks)
2286         // compile light
2287         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2288                 R_RTLight_Compile(rtlight);
2289         // load cubemap
2290         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2291
2292         // look up the light style value at this time
2293         f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2294         VectorScale(rtlight->color, f, rtlight->currentcolor);
2295         /*
2296         if (rtlight->selected)
2297         {
2298                 f = 2 + sin(realtime * M_PI * 4.0);
2299                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2300         }
2301         */
2302
2303         // if lightstyle is currently off, don't draw the light
2304         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2305                 return;
2306
2307         // if the light box is offscreen, skip it
2308         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2309                 return;
2310
2311         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2312         {
2313                 // compiled light, world available and can receive realtime lighting
2314                 // retrieve leaf information
2315                 numleafs = rtlight->static_numleafs;
2316                 leaflist = rtlight->static_leaflist;
2317                 leafpvs = rtlight->static_leafpvs;
2318                 numsurfaces = rtlight->static_numsurfaces;
2319                 surfacelist = rtlight->static_surfacelist;
2320         }
2321         else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2322         {
2323                 // dynamic light, world available and can receive realtime lighting
2324                 // calculate lit surfaces and leafs
2325                 R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
2326                 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);
2327                 leaflist = r_shadow_buffer_leaflist;
2328                 leafpvs = r_shadow_buffer_leafpvs;
2329                 surfacelist = r_shadow_buffer_surfacelist;
2330                 // if the reduced leaf bounds are offscreen, skip it
2331                 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2332                         return;
2333         }
2334         else
2335         {
2336                 // no world
2337                 numleafs = 0;
2338                 leaflist = NULL;
2339                 leafpvs = NULL;
2340                 numsurfaces = 0;
2341                 surfacelist = NULL;
2342         }
2343         // check if light is illuminating any visible leafs
2344         if (numleafs)
2345         {
2346                 for (i = 0;i < numleafs;i++)
2347                         if (r_worldleafvisible[leaflist[i]])
2348                                 break;
2349                 if (i == numleafs)
2350                         return;
2351         }
2352         // set up a scissor rectangle for this light
2353         if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2354                 return;
2355
2356         // make a list of lit entities and shadow casting entities
2357         numlightentities = 0;
2358         numshadowentities = 0;
2359         // don't count the world unless some surfaces are actually lit
2360         if (numsurfaces)
2361         {
2362                 lightentities[numlightentities++] = r_refdef.worldentity;
2363                 shadowentities[numshadowentities++] = r_refdef.worldentity;
2364         }
2365         // add dynamic entities that are lit by the light
2366         if (r_drawentities.integer)
2367         {
2368                 for (i = 0;i < r_refdef.numentities;i++)
2369                 {
2370                         entity_render_t *ent = r_refdef.entities[i];
2371                         if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2372                          && ent->model
2373                          && !(ent->flags & RENDER_TRANSPARENT)
2374                          && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
2375                         {
2376                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2377                                 if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
2378                                         shadowentities[numshadowentities++] = ent;
2379                                 if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
2380                                         lightentities[numlightentities++] = ent;
2381                         }
2382                 }
2383         }
2384
2385         // return if there's nothing at all to light
2386         if (!numlightentities)
2387                 return;
2388
2389         // don't let sound skip if going slow
2390         if (r_refdef.extraupdate)
2391                 S_ExtraUpdate ();
2392
2393         // make this the active rtlight for rendering purposes
2394         R_Shadow_RenderMode_ActiveLight(rtlight);
2395         // count this light in the r_speeds
2396         renderstats.lights++;
2397
2398         usestencil = false;
2399         if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
2400         {
2401                 // draw stencil shadow volumes to mask off pixels that are in shadow
2402                 // so that they won't receive lighting
2403                 if (gl_stencil)
2404                 {
2405                         usestencil = true;
2406                         R_Shadow_RenderMode_StencilShadowVolumes();
2407                         for (i = 0;i < numshadowentities;i++)
2408                                 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2409                 }
2410
2411                 // optionally draw visible shape of the shadow volumes
2412                 // for performance analysis by level designers
2413                 if (r_showshadowvolumes.integer)
2414                 {
2415                         R_Shadow_RenderMode_VisibleShadowVolumes();
2416                         for (i = 0;i < numshadowentities;i++)
2417                                 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2418                 }
2419         }
2420
2421         if (numlightentities)
2422         {
2423                 // draw lighting in the unmasked areas
2424                 R_Shadow_RenderMode_Lighting(usestencil, false);
2425                 for (i = 0;i < numlightentities;i++)
2426                         R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2427
2428                 // optionally draw the illuminated areas
2429                 // for performance analysis by level designers
2430                 if (r_showlighting.integer)
2431                 {
2432                         R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
2433                         for (i = 0;i < numlightentities;i++)
2434                                 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2435                 }
2436         }
2437 }
2438
2439 void R_ShadowVolumeLighting(qboolean visible)
2440 {
2441         int lnum, flag;
2442         dlight_t *light;
2443
2444         if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2445                 R_Shadow_EditLights_Reload_f();
2446
2447         R_Shadow_RenderMode_Begin();
2448
2449         flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2450         if (r_shadow_debuglight.integer >= 0)
2451         {
2452                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2453                         if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2454                                 R_DrawRTLight(&light->rtlight, visible);
2455         }
2456         else
2457                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2458                         if (light->flags & flag)
2459                                 R_DrawRTLight(&light->rtlight, visible);
2460         if (r_rtdlight)
2461                 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
2462                         R_DrawRTLight(&r_refdef.lights[lnum]->rtlight, visible);
2463
2464         R_Shadow_RenderMode_End();
2465 }
2466
2467 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2468 typedef struct suffixinfo_s
2469 {
2470         char *suffix;
2471         qboolean flipx, flipy, flipdiagonal;
2472 }
2473 suffixinfo_t;
2474 static suffixinfo_t suffix[3][6] =
2475 {
2476         {
2477                 {"px",   false, false, false},
2478                 {"nx",   false, false, false},
2479                 {"py",   false, false, false},
2480                 {"ny",   false, false, false},
2481                 {"pz",   false, false, false},
2482                 {"nz",   false, false, false}
2483         },
2484         {
2485                 {"posx", false, false, false},
2486                 {"negx", false, false, false},
2487                 {"posy", false, false, false},
2488                 {"negy", false, false, false},
2489                 {"posz", false, false, false},
2490                 {"negz", false, false, false}
2491         },
2492         {
2493                 {"rt",    true, false,  true},
2494                 {"lf",   false,  true,  true},
2495                 {"ft",    true,  true, false},
2496                 {"bk",   false, false, false},
2497                 {"up",    true, false,  true},
2498                 {"dn",    true, false,  true}
2499         }
2500 };
2501
2502 static int componentorder[4] = {0, 1, 2, 3};
2503
2504 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2505 {
2506         int i, j, cubemapsize;
2507         unsigned char *cubemappixels, *image_rgba;
2508         rtexture_t *cubemaptexture;
2509         char name[256];
2510         // must start 0 so the first loadimagepixels has no requested width/height
2511         cubemapsize = 0;
2512         cubemappixels = NULL;
2513         cubemaptexture = NULL;
2514         // keep trying different suffix groups (posx, px, rt) until one loads
2515         for (j = 0;j < 3 && !cubemappixels;j++)
2516         {
2517                 // load the 6 images in the suffix group
2518                 for (i = 0;i < 6;i++)
2519                 {
2520                         // generate an image name based on the base and and suffix
2521                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2522                         // load it
2523                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2524                         {
2525                                 // an image loaded, make sure width and height are equal
2526                                 if (image_width == image_height)
2527                                 {
2528                                         // if this is the first image to load successfully, allocate the cubemap memory
2529                                         if (!cubemappixels && image_width >= 1)
2530                                         {
2531                                                 cubemapsize = image_width;
2532                                                 // note this clears to black, so unavailable sides are black
2533                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2534                                         }
2535                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2536                                         if (cubemappixels)
2537                                                 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);
2538                                 }
2539                                 else
2540                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2541                                 // free the image
2542                                 Mem_Free(image_rgba);
2543                         }
2544                 }
2545         }
2546         // if a cubemap loaded, upload it
2547         if (cubemappixels)
2548         {
2549                 if (!r_shadow_filters_texturepool)
2550                         r_shadow_filters_texturepool = R_AllocTexturePool();
2551                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2552                 Mem_Free(cubemappixels);
2553         }
2554         else
2555         {
2556                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2557                 for (j = 0;j < 3;j++)
2558                         for (i = 0;i < 6;i++)
2559                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2560                 Con_Print(" and was unable to find any of them.\n");
2561         }
2562         return cubemaptexture;
2563 }
2564
2565 rtexture_t *R_Shadow_Cubemap(const char *basename)
2566 {
2567         int i;
2568         for (i = 0;i < numcubemaps;i++)
2569                 if (!strcasecmp(cubemaps[i].basename, basename))
2570                         return cubemaps[i].texture;
2571         if (i >= MAX_CUBEMAPS)
2572                 return r_texture_whitecube;
2573         numcubemaps++;
2574         strcpy(cubemaps[i].basename, basename);
2575         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2576         if (!cubemaps[i].texture)
2577                 cubemaps[i].texture = r_texture_whitecube;
2578         return cubemaps[i].texture;
2579 }
2580
2581 void R_Shadow_FreeCubemaps(void)
2582 {
2583         numcubemaps = 0;
2584         R_FreeTexturePool(&r_shadow_filters_texturepool);
2585 }
2586
2587 dlight_t *R_Shadow_NewWorldLight(void)
2588 {
2589         dlight_t *light;
2590         light = (dlight_t *)Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2591         light->next = r_shadow_worldlightchain;
2592         r_shadow_worldlightchain = light;
2593         return light;
2594 }
2595
2596 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)
2597 {
2598         VectorCopy(origin, light->origin);
2599         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2600         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2601         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2602         light->color[0] = max(color[0], 0);
2603         light->color[1] = max(color[1], 0);
2604         light->color[2] = max(color[2], 0);
2605         light->radius = max(radius, 0);
2606         light->style = style;
2607         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2608         {
2609                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2610                 light->style = 0;
2611         }
2612         light->shadow = shadowenable;
2613         light->corona = corona;
2614         if (!cubemapname)
2615                 cubemapname = "";
2616         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
2617         light->coronasizescale = coronasizescale;
2618         light->ambientscale = ambientscale;
2619         light->diffusescale = diffusescale;
2620         light->specularscale = specularscale;
2621         light->flags = flags;
2622         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2623
2624         R_RTLight_Update(light, true);
2625 }
2626
2627 void R_Shadow_FreeWorldLight(dlight_t *light)
2628 {
2629         dlight_t **lightpointer;
2630         R_RTLight_Uncompile(&light->rtlight);
2631         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2632         if (*lightpointer != light)
2633                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
2634         *lightpointer = light->next;
2635         Mem_Free(light);
2636 }
2637
2638 void R_Shadow_ClearWorldLights(void)
2639 {
2640         while (r_shadow_worldlightchain)
2641                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2642         r_shadow_selectedlight = NULL;
2643         R_Shadow_FreeCubemaps();
2644 }
2645
2646 void R_Shadow_SelectLight(dlight_t *light)
2647 {
2648         if (r_shadow_selectedlight)
2649                 r_shadow_selectedlight->selected = false;
2650         r_shadow_selectedlight = light;
2651         if (r_shadow_selectedlight)
2652                 r_shadow_selectedlight->selected = true;
2653 }
2654
2655 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2656 {
2657         float scale = r_editlights_cursorgrid.value * 0.5f;
2658         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);
2659 }
2660
2661 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2662 {
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[surfacenumber]->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