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