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