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