]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
fix crash when using r_editlights mode
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is normally rendered using Carmack's Reverse technique, in which
11 backfaces behind zbuffer (zfail) increment the stencil, and frontfaces behind
12 zbuffer (zfail) decrement the stencil, the result is a stencil value of zero
13 where shadows did not intersect the visible geometry, suitable as a stencil
14 mask for rendering lighting everywhere but shadow.
15
16 In our case to hopefully avoid the Creative Labs patent, we draw the backfaces
17 as decrement and the frontfaces as increment, and we redefine the DepthFunc to
18 GL_LESS (the patent uses GL_GEQUAL) which causes zfail when behind surfaces
19 and zpass when infront (the patent draws where zpass with a GL_GEQUAL test),
20 additionally we clear stencil to 128 to avoid the need for the unclamped
21 incr/decr extension (not related to patent).
22
23 Patent warning:
24 This algorithm may be covered by Creative's patent (US Patent #6384822),
25 however that patent is quite specific about increment on backfaces and
26 decrement on frontfaces where zpass with GL_GEQUAL depth test, which is
27 opposite this implementation and partially opposite Carmack's Reverse paper
28 (which uses GL_LESS, but increments on backfaces and decrements on frontfaces).
29
30
31
32 Terminology: Stencil Light Volume (sometimes called Light Volumes)
33 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
34 areas in shadow it contains the areas in light, this can only be built
35 quickly for certain limited cases (such as portal visibility from a point),
36 but is quite useful for some effects (sunlight coming from sky polygons is
37 one possible example, translucent occluders is another example).
38
39
40
41 Terminology: Optimized Stencil Shadow Volume
42 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
43 no duplicate coverage of areas (no need to shadow an area twice), often this
44 greatly improves performance but is an operation too costly to use on moving
45 lights (however completely optimal Stencil Light Volumes can be constructed
46 in some ideal cases).
47
48
49
50 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
51 Per pixel evaluation of lighting equations, at a bare minimum this involves
52 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
53 vector and surface normal, using a texture of the surface bumps, called a
54 NormalMap) if supported by hardware; in our case there is support for cards
55 which are incapable of DOT3, the quality is quite poor however.  Additionally
56 it is desirable to have specular evaluation per pixel, per vertex
57 normalization of specular halfangle vectors causes noticable distortion but
58 is unavoidable on hardware without GL_ARB_fragment_program or
59 GL_ARB_fragment_shader.
60
61
62
63 Terminology: Normalization CubeMap
64 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
65 encoded as RGB colors) for any possible direction, this technique allows per
66 pixel calculation of incidence vector for per pixel lighting purposes, which
67 would not otherwise be possible per pixel without GL_ARB_fragment_program or
68 GL_ARB_fragment_shader.
69
70
71
72 Terminology: 2D+1D Attenuation Texturing
73 A very crude approximation of light attenuation with distance which results
74 in cylindrical light shapes which fade vertically as a streak (some games
75 such as Doom3 allow this to be rotated to be less noticable in specific
76 cases), the technique is simply modulating lighting by two 2D textures (which
77 can be the same) on different axes of projection (XY and Z, typically), this
78 is the second best technique available without 3D Attenuation Texturing,
79 GL_ARB_fragment_program or GL_ARB_fragment_shader technology.
80
81
82
83 Terminology: 2D+1D Inverse Attenuation Texturing
84 A clever method described in papers on the Abducted engine, this has a squared
85 distance texture (bright on the outside, black in the middle), which is used
86 twice using GL_ADD blending, the result of this is used in an inverse modulate
87 (GL_ONE_MINUS_DST_ALPHA, GL_ZERO) to implement the equation
88 lighting*=(1-((X*X+Y*Y)+(Z*Z))) which is spherical (unlike 2D+1D attenuation
89 texturing).
90
91
92
93 Terminology: 3D Attenuation Texturing
94 A slightly crude approximation of light attenuation with distance, its flaws
95 are limited radius and resolution (performance tradeoffs).
96
97
98
99 Terminology: 3D Attenuation-Normalization Texturing
100 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
101 vectors shorter the lighting becomes darker, a very effective optimization of
102 diffuse lighting if 3D Attenuation Textures are already used.
103
104
105
106 Terminology: Light Cubemap Filtering
107 A technique for modeling non-uniform light distribution according to
108 direction, for example a lantern may use a cubemap to describe the light
109 emission pattern of the cage around the lantern (as well as soot buildup
110 discoloring the light in certain areas), often also used for softened grate
111 shadows and light shining through a stained glass window (done crudely by
112 texturing the lighting with a cubemap), another good example would be a disco
113 light.  This technique is used heavily in many games (Doom3 does not support
114 this however).
115
116
117
118 Terminology: Light Projection Filtering
119 A technique for modeling shadowing of light passing through translucent
120 surfaces, allowing stained glass windows and other effects to be done more
121 elegantly than possible with Light Cubemap Filtering by applying an occluder
122 texture to the lighting combined with a stencil light volume to limit the lit
123 area, this technique is used by Doom3 for spotlights and flashlights, among
124 other things, this can also be used more generally to render light passing
125 through multiple translucent occluders in a scene (using a light volume to
126 describe the area beyond the occluder, and thus mask off rendering of all
127 other areas).
128
129
130
131 Terminology: Doom3 Lighting
132 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
133 CubeMap, 2D+1D Attenuation Texturing, and Light Projection Filtering, as
134 demonstrated by the game Doom3.
135 */
136
137 #include "quakedef.h"
138 #include "r_shadow.h"
139 #include "cl_collision.h"
140 #include "portals.h"
141 #include "image.h"
142
143 extern void R_Shadow_EditLights_Init(void);
144
145 typedef enum r_shadow_rendermode_e
146 {
147         R_SHADOW_RENDERMODE_NONE,
148         R_SHADOW_RENDERMODE_STENCIL,
149         R_SHADOW_RENDERMODE_STENCILTWOSIDE,
150         R_SHADOW_RENDERMODE_LIGHT_VERTEX,
151         R_SHADOW_RENDERMODE_LIGHT_DOT3,
152         R_SHADOW_RENDERMODE_LIGHT_GLSL,
153         R_SHADOW_RENDERMODE_VISIBLEVOLUMES,
154         R_SHADOW_RENDERMODE_VISIBLELIGHTING,
155 }
156 r_shadow_rendermode_t;
157
158 r_shadow_rendermode_t r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
159 r_shadow_rendermode_t r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_NONE;
160 r_shadow_rendermode_t r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_NONE;
161
162 mempool_t *r_shadow_mempool;
163
164 int maxshadowelements;
165 int *shadowelements;
166
167 int maxshadowmark;
168 int numshadowmark;
169 int *shadowmark;
170 int *shadowmarklist;
171 int shadowmarkcount;
172
173 int maxvertexupdate;
174 int *vertexupdate;
175 int *vertexremap;
176 int vertexupdatenum;
177
178 int r_shadow_buffer_numleafpvsbytes;
179 unsigned char *r_shadow_buffer_leafpvs;
180 int *r_shadow_buffer_leaflist;
181
182 int r_shadow_buffer_numsurfacepvsbytes;
183 unsigned char *r_shadow_buffer_surfacepvs;
184 int *r_shadow_buffer_surfacelist;
185
186 rtexturepool_t *r_shadow_texturepool;
187 rtexture_t *r_shadow_attenuation2dtexture;
188 rtexture_t *r_shadow_attenuation3dtexture;
189
190 // lights are reloaded when this changes
191 char r_shadow_mapname[MAX_QPATH];
192
193 // used only for light filters (cubemaps)
194 rtexturepool_t *r_shadow_filters_texturepool;
195
196 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0", "generate fake bumpmaps from diffuse textures at this bumpyness, try 4 to match tenebrae, higher values increase depth, requires r_restart to take effect"};
197 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4", "what magnitude to interpret _bump.tga textures as, higher values increase depth, requires r_restart to take effect"};
198 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1", "renders only one light, for level design purposes or debugging"};
199 cvar_t r_shadow_gloss = {CVAR_SAVE, "r_shadow_gloss", "1", "0 disables gloss (specularity) rendering, 1 uses gloss if textures are found, 2 forces a flat metallic specular effect on everything without textures (similar to tenebrae)"};
200 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25", "how bright the forced flat gloss should look if r_shadow_gloss is 2"};
201 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1", "how bright textured glossmaps should look if r_shadow_gloss is 1 or 2"};
202 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5", "changes attenuation texture generation (does not affect r_glsl lighting)"};
203 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1", "changes attenuation texture generation (does not affect r_glsl lighting)"};
204 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1", "renders all world lights brighter or darker"};
205 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1", "use portal culling to exactly determine lit triangles when compiling world lights"};
206 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000", "how far to cast shadows"};
207 cvar_t r_shadow_realtime_dlight = {CVAR_SAVE, "r_shadow_realtime_dlight", "1", "enables rendering of dynamic lights such as explosions and rocket light"};
208 cvar_t r_shadow_realtime_dlight_shadows = {CVAR_SAVE, "r_shadow_realtime_dlight_shadows", "1", "enables rendering of shadows from dynamic lights"};
209 cvar_t r_shadow_realtime_dlight_portalculling = {0, "r_shadow_realtime_dlight_portalculling", "0", "enables portal culling optimizations on dynamic lights (slow!  you probably don't want this!)"};
210 cvar_t r_shadow_realtime_world = {CVAR_SAVE, "r_shadow_realtime_world", "0", "enables rendering of full world lighting (whether loaded from the map, or a .rtlights file, or a .ent file, or a .lights file produced by hlight)"};
211 cvar_t r_shadow_realtime_world_dlightshadows = {CVAR_SAVE, "r_shadow_realtime_world_dlightshadows", "1", "enables shadows from dynamic lights when using full world lighting"};
212 cvar_t r_shadow_realtime_world_lightmaps = {CVAR_SAVE, "r_shadow_realtime_world_lightmaps", "0", "brightness to render lightmaps when using full world lighting, try 0.5 for a tenebrae-like appearance"};
213 cvar_t r_shadow_realtime_world_shadows = {CVAR_SAVE, "r_shadow_realtime_world_shadows", "1", "enables rendering of shadows from world lights"};
214 cvar_t r_shadow_realtime_world_compile = {0, "r_shadow_realtime_world_compile", "1", "enables compilation of world lights for higher performance rendering"};
215 cvar_t r_shadow_realtime_world_compileshadow = {0, "r_shadow_realtime_world_compileshadow", "1", "enables compilation of shadows from world lights for higher performance rendering"};
216 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1", "use scissor optimization of light rendering (restricts rendering to the portion of the screen affected by the light)"};
217 cvar_t r_shadow_shadow_polygonfactor = {0, "r_shadow_shadow_polygonfactor", "0", "how much to enlarge shadow volume polygons when rendering (should be 0!)"};
218 cvar_t r_shadow_shadow_polygonoffset = {0, "r_shadow_shadow_polygonoffset", "1", "how much to push shadow volumes into the distance when rendering, to reduce chances of zfighting artifacts (should not be less than 0)"};
219 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1", "use 3D voxel textures for spherical attenuation rather than cylindrical (does not affect r_glsl lighting)"};
220 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1", "make use of GL_EXT_stenciltwoside extension (NVIDIA only)"};
221 cvar_t r_editlights = {0, "r_editlights", "0", "enables .rtlights file editing mode"};
222 cvar_t r_editlights_cursordistance = {0, "r_editlights_cursordistance", "1024", "maximum distance of cursor from eye"};
223 cvar_t r_editlights_cursorpushback = {0, "r_editlights_cursorpushback", "0", "how far to pull the cursor back toward the eye"};
224 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_cursorpushoff", "4", "how far to push the cursor off the impacted surface"};
225 cvar_t r_editlights_cursorgrid = {0, "r_editlights_cursorgrid", "4", "snaps cursor to this grid size"};
226 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "1", "changes size of light entities loaded from a map"};
227
228 float r_shadow_attenpower, r_shadow_attenscale;
229
230 rtlight_t *r_shadow_compilingrtlight;
231 dlight_t *r_shadow_worldlightchain;
232 dlight_t *r_shadow_selectedlight;
233 dlight_t r_shadow_bufferlight;
234 vec3_t r_editlights_cursorlocation;
235
236 extern int con_vislines;
237
238 typedef struct cubemapinfo_s
239 {
240         char basename[64];
241         rtexture_t *texture;
242 }
243 cubemapinfo_t;
244
245 #define MAX_CUBEMAPS 256
246 static int numcubemaps;
247 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
248
249 void R_Shadow_UncompileWorldLights(void);
250 void R_Shadow_ClearWorldLights(void);
251 void R_Shadow_SaveWorldLights(void);
252 void R_Shadow_LoadWorldLights(void);
253 void R_Shadow_LoadLightsFile(void);
254 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
255 void R_Shadow_EditLights_Reload_f(void);
256 void R_Shadow_ValidateCvars(void);
257 static void R_Shadow_MakeTextures(void);
258 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
259
260 void r_shadow_start(void)
261 {
262         // allocate vertex processing arrays
263         numcubemaps = 0;
264         r_shadow_attenuation2dtexture = NULL;
265         r_shadow_attenuation3dtexture = NULL;
266         r_shadow_texturepool = NULL;
267         r_shadow_filters_texturepool = NULL;
268         R_Shadow_ValidateCvars();
269         R_Shadow_MakeTextures();
270         maxshadowelements = 0;
271         shadowelements = NULL;
272         maxvertexupdate = 0;
273         vertexupdate = NULL;
274         vertexremap = NULL;
275         vertexupdatenum = 0;
276         maxshadowmark = 0;
277         numshadowmark = 0;
278         shadowmark = NULL;
279         shadowmarklist = NULL;
280         shadowmarkcount = 0;
281         r_shadow_buffer_numleafpvsbytes = 0;
282         r_shadow_buffer_leafpvs = NULL;
283         r_shadow_buffer_leaflist = NULL;
284         r_shadow_buffer_numsurfacepvsbytes = 0;
285         r_shadow_buffer_surfacepvs = NULL;
286         r_shadow_buffer_surfacelist = NULL;
287 }
288
289 void r_shadow_shutdown(void)
290 {
291         R_Shadow_UncompileWorldLights();
292         numcubemaps = 0;
293         r_shadow_attenuation2dtexture = NULL;
294         r_shadow_attenuation3dtexture = NULL;
295         R_FreeTexturePool(&r_shadow_texturepool);
296         R_FreeTexturePool(&r_shadow_filters_texturepool);
297         maxshadowelements = 0;
298         if (shadowelements)
299                 Mem_Free(shadowelements);
300         shadowelements = NULL;
301         maxvertexupdate = 0;
302         if (vertexupdate)
303                 Mem_Free(vertexupdate);
304         vertexupdate = NULL;
305         if (vertexremap)
306                 Mem_Free(vertexremap);
307         vertexremap = NULL;
308         vertexupdatenum = 0;
309         maxshadowmark = 0;
310         numshadowmark = 0;
311         if (shadowmark)
312                 Mem_Free(shadowmark);
313         shadowmark = NULL;
314         if (shadowmarklist)
315                 Mem_Free(shadowmarklist);
316         shadowmarklist = NULL;
317         shadowmarkcount = 0;
318         r_shadow_buffer_numleafpvsbytes = 0;
319         if (r_shadow_buffer_leafpvs)
320                 Mem_Free(r_shadow_buffer_leafpvs);
321         r_shadow_buffer_leafpvs = NULL;
322         if (r_shadow_buffer_leaflist)
323                 Mem_Free(r_shadow_buffer_leaflist);
324         r_shadow_buffer_leaflist = NULL;
325         r_shadow_buffer_numsurfacepvsbytes = 0;
326         if (r_shadow_buffer_surfacepvs)
327                 Mem_Free(r_shadow_buffer_surfacepvs);
328         r_shadow_buffer_surfacepvs = NULL;
329         if (r_shadow_buffer_surfacelist)
330                 Mem_Free(r_shadow_buffer_surfacelist);
331         r_shadow_buffer_surfacelist = NULL;
332 }
333
334 void r_shadow_newmap(void)
335 {
336 }
337
338 void R_Shadow_Help_f(void)
339 {
340         Con_Printf(
341 "Documentation on r_shadow system:\n"
342 "Settings:\n"
343 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
344 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
345 "r_shadow_debuglight : render only this light number (-1 = all)\n"
346 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
347 "r_shadow_gloss2intensity : brightness of forced gloss\n"
348 "r_shadow_glossintensity : brightness of textured gloss\n"
349 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
350 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
351 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
352 "r_shadow_portallight : use portal visibility for static light precomputation\n"
353 "r_shadow_projectdistance : shadow volume projection distance\n"
354 "r_shadow_realtime_dlight : use high quality dynamic lights in normal mode\n"
355 "r_shadow_realtime_dlight_shadows : cast shadows from dlights\n"
356 "r_shadow_realtime_dlight_portalculling : work hard to reduce graphics work\n"
357 "r_shadow_realtime_world : use high quality world lighting mode\n"
358 "r_shadow_realtime_world_dlightshadows : cast shadows from dlights\n"
359 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to lights\n"
360 "r_shadow_realtime_world_shadows : cast shadows from world lights\n"
361 "r_shadow_realtime_world_compile : compile surface/visibility information\n"
362 "r_shadow_realtime_world_compileshadow : compile shadow geometry\n"
363 "r_shadow_scissor : use scissor optimization\n"
364 "r_shadow_shadow_polygonfactor : nudge shadow volumes closer/further\n"
365 "r_shadow_shadow_polygonoffset : nudge shadow volumes closer/further\n"
366 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
367 "r_showlighting : useful for performance testing; bright = slow!\n"
368 "r_showshadowvolumes : useful for performance testing; bright = slow!\n"
369 "Commands:\n"
370 "r_shadow_help : this help\n"
371         );
372 }
373
374 void R_Shadow_Init(void)
375 {
376         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
377         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
378         Cvar_RegisterVariable(&r_shadow_debuglight);
379         Cvar_RegisterVariable(&r_shadow_gloss);
380         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
381         Cvar_RegisterVariable(&r_shadow_glossintensity);
382         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
383         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
384         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
385         Cvar_RegisterVariable(&r_shadow_portallight);
386         Cvar_RegisterVariable(&r_shadow_projectdistance);
387         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
388         Cvar_RegisterVariable(&r_shadow_realtime_dlight_shadows);
389         Cvar_RegisterVariable(&r_shadow_realtime_dlight_portalculling);
390         Cvar_RegisterVariable(&r_shadow_realtime_world);
391         Cvar_RegisterVariable(&r_shadow_realtime_world_dlightshadows);
392         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
393         Cvar_RegisterVariable(&r_shadow_realtime_world_shadows);
394         Cvar_RegisterVariable(&r_shadow_realtime_world_compile);
395         Cvar_RegisterVariable(&r_shadow_realtime_world_compileshadow);
396         Cvar_RegisterVariable(&r_shadow_scissor);
397         Cvar_RegisterVariable(&r_shadow_shadow_polygonfactor);
398         Cvar_RegisterVariable(&r_shadow_shadow_polygonoffset);
399         Cvar_RegisterVariable(&r_shadow_texture3d);
400         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
401         if (gamemode == GAME_TENEBRAE)
402         {
403                 Cvar_SetValue("r_shadow_gloss", 2);
404                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
405         }
406         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f, "prints documentation on console commands and variables used by realtime lighting and shadowing system");
407         R_Shadow_EditLights_Init();
408         r_shadow_mempool = Mem_AllocPool("R_Shadow", 0, NULL);
409         r_shadow_worldlightchain = NULL;
410         maxshadowelements = 0;
411         shadowelements = NULL;
412         maxvertexupdate = 0;
413         vertexupdate = NULL;
414         vertexremap = NULL;
415         vertexupdatenum = 0;
416         maxshadowmark = 0;
417         numshadowmark = 0;
418         shadowmark = NULL;
419         shadowmarklist = NULL;
420         shadowmarkcount = 0;
421         r_shadow_buffer_numleafpvsbytes = 0;
422         r_shadow_buffer_leafpvs = NULL;
423         r_shadow_buffer_leaflist = NULL;
424         r_shadow_buffer_numsurfacepvsbytes = 0;
425         r_shadow_buffer_surfacepvs = NULL;
426         r_shadow_buffer_surfacelist = NULL;
427         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
428 }
429
430 matrix4x4_t matrix_attenuationxyz =
431 {
432         {
433                 {0.5, 0.0, 0.0, 0.5},
434                 {0.0, 0.5, 0.0, 0.5},
435                 {0.0, 0.0, 0.5, 0.5},
436                 {0.0, 0.0, 0.0, 1.0}
437         }
438 };
439
440 matrix4x4_t matrix_attenuationz =
441 {
442         {
443                 {0.0, 0.0, 0.5, 0.5},
444                 {0.0, 0.0, 0.0, 0.5},
445                 {0.0, 0.0, 0.0, 0.5},
446                 {0.0, 0.0, 0.0, 1.0}
447         }
448 };
449
450 int *R_Shadow_ResizeShadowElements(int numtris)
451 {
452         // make sure shadowelements is big enough for this volume
453         if (maxshadowelements < numtris * 24)
454         {
455                 maxshadowelements = numtris * 24;
456                 if (shadowelements)
457                         Mem_Free(shadowelements);
458                 shadowelements = (int *)Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
459         }
460         return shadowelements;
461 }
462
463 static void R_Shadow_EnlargeLeafSurfaceBuffer(int numleafs, int numsurfaces)
464 {
465         int numleafpvsbytes = (((numleafs + 7) >> 3) + 255) & ~255;
466         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
467         if (r_shadow_buffer_numleafpvsbytes < numleafpvsbytes)
468         {
469                 if (r_shadow_buffer_leafpvs)
470                         Mem_Free(r_shadow_buffer_leafpvs);
471                 if (r_shadow_buffer_leaflist)
472                         Mem_Free(r_shadow_buffer_leaflist);
473                 r_shadow_buffer_numleafpvsbytes = numleafpvsbytes;
474                 r_shadow_buffer_leafpvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes);
475                 r_shadow_buffer_leaflist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numleafpvsbytes * 8 * sizeof(*r_shadow_buffer_leaflist));
476         }
477         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
478         {
479                 if (r_shadow_buffer_surfacepvs)
480                         Mem_Free(r_shadow_buffer_surfacepvs);
481                 if (r_shadow_buffer_surfacelist)
482                         Mem_Free(r_shadow_buffer_surfacelist);
483                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
484                 r_shadow_buffer_surfacepvs = (unsigned char *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
485                 r_shadow_buffer_surfacelist = (int *)Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
486         }
487 }
488
489 void R_Shadow_PrepareShadowMark(int numtris)
490 {
491         // make sure shadowmark is big enough for this volume
492         if (maxshadowmark < numtris)
493         {
494                 maxshadowmark = numtris;
495                 if (shadowmark)
496                         Mem_Free(shadowmark);
497                 if (shadowmarklist)
498                         Mem_Free(shadowmarklist);
499                 shadowmark = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
500                 shadowmarklist = (int *)Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
501                 shadowmarkcount = 0;
502         }
503         shadowmarkcount++;
504         // if shadowmarkcount wrapped we clear the array and adjust accordingly
505         if (shadowmarkcount == 0)
506         {
507                 shadowmarkcount = 1;
508                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
509         }
510         numshadowmark = 0;
511 }
512
513 int R_Shadow_ConstructShadowVolume(int innumvertices, int innumtris, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *projectorigin, float projectdistance, int numshadowmarktris, const int *shadowmarktris)
514 {
515         int i, j;
516         int outtriangles = 0, outvertices = 0;
517         const int *element;
518         const float *vertex;
519
520         if (maxvertexupdate < innumvertices)
521         {
522                 maxvertexupdate = innumvertices;
523                 if (vertexupdate)
524                         Mem_Free(vertexupdate);
525                 if (vertexremap)
526                         Mem_Free(vertexremap);
527                 vertexupdate = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
528                 vertexremap = (int *)Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
529                 vertexupdatenum = 0;
530         }
531         vertexupdatenum++;
532         if (vertexupdatenum == 0)
533         {
534                 vertexupdatenum = 1;
535                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
536                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
537         }
538
539         for (i = 0;i < numshadowmarktris;i++)
540                 shadowmark[shadowmarktris[i]] = shadowmarkcount;
541
542         for (i = 0;i < numshadowmarktris;i++)
543         {
544                 element = inelement3i + shadowmarktris[i] * 3;
545                 // make sure the vertices are created
546                 for (j = 0;j < 3;j++)
547                 {
548                         if (vertexupdate[element[j]] != vertexupdatenum)
549                         {
550                                 float ratio, direction[3];
551                                 vertexupdate[element[j]] = vertexupdatenum;
552                                 vertexremap[element[j]] = outvertices;
553                                 vertex = invertex3f + element[j] * 3;
554                                 // project one copy of the vertex to the sphere radius of the light
555                                 // (FIXME: would projecting it to the light box be better?)
556                                 VectorSubtract(vertex, projectorigin, direction);
557                                 ratio = projectdistance / VectorLength(direction);
558                                 VectorCopy(vertex, outvertex3f);
559                                 VectorMA(projectorigin, ratio, direction, (outvertex3f + 3));
560                                 outvertex3f += 6;
561                                 outvertices += 2;
562                         }
563                 }
564         }
565
566         for (i = 0;i < numshadowmarktris;i++)
567         {
568                 int remappedelement[3];
569                 int markindex;
570                 const int *neighbortriangle;
571
572                 markindex = shadowmarktris[i] * 3;
573                 element = inelement3i + markindex;
574                 neighbortriangle = inneighbor3i + markindex;
575                 // output the front and back triangles
576                 outelement3i[0] = vertexremap[element[0]];
577                 outelement3i[1] = vertexremap[element[1]];
578                 outelement3i[2] = vertexremap[element[2]];
579                 outelement3i[3] = vertexremap[element[2]] + 1;
580                 outelement3i[4] = vertexremap[element[1]] + 1;
581                 outelement3i[5] = vertexremap[element[0]] + 1;
582
583                 outelement3i += 6;
584                 outtriangles += 2;
585                 // output the sides (facing outward from this triangle)
586                 if (shadowmark[neighbortriangle[0]] != shadowmarkcount)
587                 {
588                         remappedelement[0] = vertexremap[element[0]];
589                         remappedelement[1] = vertexremap[element[1]];
590                         outelement3i[0] = remappedelement[1];
591                         outelement3i[1] = remappedelement[0];
592                         outelement3i[2] = remappedelement[0] + 1;
593                         outelement3i[3] = remappedelement[1];
594                         outelement3i[4] = remappedelement[0] + 1;
595                         outelement3i[5] = remappedelement[1] + 1;
596
597                         outelement3i += 6;
598                         outtriangles += 2;
599                 }
600                 if (shadowmark[neighbortriangle[1]] != shadowmarkcount)
601                 {
602                         remappedelement[1] = vertexremap[element[1]];
603                         remappedelement[2] = vertexremap[element[2]];
604                         outelement3i[0] = remappedelement[2];
605                         outelement3i[1] = remappedelement[1];
606                         outelement3i[2] = remappedelement[1] + 1;
607                         outelement3i[3] = remappedelement[2];
608                         outelement3i[4] = remappedelement[1] + 1;
609                         outelement3i[5] = remappedelement[2] + 1;
610
611                         outelement3i += 6;
612                         outtriangles += 2;
613                 }
614                 if (shadowmark[neighbortriangle[2]] != shadowmarkcount)
615                 {
616                         remappedelement[0] = vertexremap[element[0]];
617                         remappedelement[2] = vertexremap[element[2]];
618                         outelement3i[0] = remappedelement[0];
619                         outelement3i[1] = remappedelement[2];
620                         outelement3i[2] = remappedelement[2] + 1;
621                         outelement3i[3] = remappedelement[0];
622                         outelement3i[4] = remappedelement[2] + 1;
623                         outelement3i[5] = remappedelement[0] + 1;
624
625                         outelement3i += 6;
626                         outtriangles += 2;
627                 }
628         }
629         if (outnumvertices)
630                 *outnumvertices = outvertices;
631         return outtriangles;
632 }
633
634 void R_Shadow_VolumeFromList(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, int nummarktris, const int *marktris)
635 {
636         int tris, outverts;
637         if (projectdistance < 0.1)
638         {
639                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
640                 return;
641         }
642         if (!numverts || !nummarktris)
643                 return;
644         // make sure shadowelements is big enough for this volume
645         if (maxshadowelements < nummarktris * 24)
646                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
647         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
648         renderstats.lights_dynamicshadowtriangles += tris;
649         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
650 }
651
652 void R_Shadow_MarkVolumeFromBox(int firsttriangle, int numtris, const float *invertex3f, const int *elements, const vec3_t projectorigin, const vec3_t lightmins, const vec3_t lightmaxs, const vec3_t surfacemins, const vec3_t surfacemaxs)
653 {
654         int t, tend;
655         const int *e;
656         const float *v[3];
657         if (!BoxesOverlap(lightmins, lightmaxs, surfacemins, surfacemaxs))
658                 return;
659         tend = firsttriangle + numtris;
660         if (surfacemins[0] >= lightmins[0] && surfacemaxs[0] <= lightmaxs[0]
661          && surfacemins[1] >= lightmins[1] && surfacemaxs[1] <= lightmaxs[1]
662          && surfacemins[2] >= lightmins[2] && surfacemaxs[2] <= lightmaxs[2])
663         {
664                 // surface box entirely inside light box, no box cull
665                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
666                         if (PointInfrontOfTriangle(projectorigin, invertex3f + e[0] * 3, invertex3f + e[1] * 3, invertex3f + e[2] * 3))
667                                 shadowmarklist[numshadowmark++] = t;
668         }
669         else
670         {
671                 // surface box not entirely inside light box, cull each triangle
672                 for (t = firsttriangle, e = elements + t * 3;t < tend;t++, e += 3)
673                 {
674                         v[0] = invertex3f + e[0] * 3;
675                         v[1] = invertex3f + e[1] * 3;
676                         v[2] = invertex3f + e[2] * 3;
677                         if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2])
678                          && lightmaxs[0] > min(v[0][0], min(v[1][0], v[2][0]))
679                          && lightmins[0] < max(v[0][0], max(v[1][0], v[2][0]))
680                          && lightmaxs[1] > min(v[0][1], min(v[1][1], v[2][1]))
681                          && lightmins[1] < max(v[0][1], max(v[1][1], v[2][1]))
682                          && lightmaxs[2] > min(v[0][2], min(v[1][2], v[2][2]))
683                          && lightmins[2] < max(v[0][2], max(v[1][2], v[2][2])))
684                                 shadowmarklist[numshadowmark++] = t;
685                 }
686         }
687 }
688
689 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
690 {
691         rmeshstate_t m;
692         if (r_shadow_compilingrtlight)
693         {
694                 // if we're compiling an rtlight, capture the mesh
695                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
696                 return;
697         }
698         renderstats.lights_shadowtriangles += numtriangles;
699         memset(&m, 0, sizeof(m));
700         m.pointer_vertex = vertex3f;
701         R_Mesh_State(&m);
702         GL_LockArrays(0, numvertices);
703         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
704         {
705                 // decrement stencil if backface is behind depthbuffer
706                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
707                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
708                 R_Mesh_Draw(0, numvertices, numtriangles, element3i);
709                 // increment stencil if frontface is behind depthbuffer
710                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
711                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
712         }
713         R_Mesh_Draw(0, numvertices, numtriangles, element3i);
714         GL_LockArrays(0, 0);
715 }
716
717 static void R_Shadow_MakeTextures(void)
718 {
719         int x, y, z, d;
720         float v[3], intensity;
721         unsigned char *data;
722         R_FreeTexturePool(&r_shadow_texturepool);
723         r_shadow_texturepool = R_AllocTexturePool();
724         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
725         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
726 #define ATTEN2DSIZE 64
727 #define ATTEN3DSIZE 32
728         data = (unsigned char *)Mem_Alloc(tempmempool, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4));
729         for (y = 0;y < ATTEN2DSIZE;y++)
730         {
731                 for (x = 0;x < ATTEN2DSIZE;x++)
732                 {
733                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
734                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
735                         v[2] = 0;
736                         intensity = 1.0f - sqrt(DotProduct(v, v));
737                         if (intensity > 0)
738                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
739                         d = bound(0, intensity, 255);
740                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
741                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
742                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
743                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
744                 }
745         }
746         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
747         if (r_shadow_texture3d.integer)
748         {
749                 for (z = 0;z < ATTEN3DSIZE;z++)
750                 {
751                         for (y = 0;y < ATTEN3DSIZE;y++)
752                         {
753                                 for (x = 0;x < ATTEN3DSIZE;x++)
754                                 {
755                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
756                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
757                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
758                                         intensity = 1.0f - sqrt(DotProduct(v, v));
759                                         if (intensity > 0)
760                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
761                                         d = bound(0, intensity, 255);
762                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
763                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
764                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
765                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
766                                 }
767                         }
768                 }
769                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
770         }
771         Mem_Free(data);
772 }
773
774 void R_Shadow_ValidateCvars(void)
775 {
776         if (r_shadow_texture3d.integer && !gl_texture3d)
777                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
778         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
779                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
780 }
781
782 // light currently being rendered
783 rtlight_t *r_shadow_rtlight;
784
785 // this is the location of the eye in entity space
786 vec3_t r_shadow_entityeyeorigin;
787 // this is the location of the light in entity space
788 vec3_t r_shadow_entitylightorigin;
789 // this transforms entity coordinates to light filter cubemap coordinates
790 // (also often used for other purposes)
791 matrix4x4_t r_shadow_entitytolight;
792 // based on entitytolight this transforms -1 to +1 to 0 to 1 for purposes
793 // of attenuation texturing in full 3D (Z result often ignored)
794 matrix4x4_t r_shadow_entitytoattenuationxyz;
795 // this transforms only the Z to S, and T is always 0.5
796 matrix4x4_t r_shadow_entitytoattenuationz;
797
798 void R_Shadow_RenderMode_Begin(void)
799 {
800         rmeshstate_t m;
801
802         R_Shadow_ValidateCvars();
803
804         if (!r_shadow_attenuation2dtexture
805          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
806          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
807          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
808                 R_Shadow_MakeTextures();
809
810         memset(&m, 0, sizeof(m));
811         R_Mesh_State(&m);
812         GL_BlendFunc(GL_ONE, GL_ZERO);
813         GL_DepthMask(false);
814         GL_DepthTest(true);
815         GL_Color(0, 0, 0, 1);
816         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
817         qglEnable(GL_CULL_FACE);
818         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
819
820         r_shadow_rendermode = R_SHADOW_RENDERMODE_NONE;
821
822         if (gl_ext_stenciltwoside.integer)
823                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCILTWOSIDE;
824         else
825                 r_shadow_shadowingrendermode = R_SHADOW_RENDERMODE_STENCIL;
826
827         if (r_glsl.integer && gl_support_fragment_shader)
828                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_GLSL;
829         else if (gl_dot3arb && gl_texturecubemap && r_textureunits.integer >= 2 && gl_combine.integer && gl_stencil)
830                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_DOT3;
831         else
832                 r_shadow_lightingrendermode = R_SHADOW_RENDERMODE_LIGHT_VERTEX;
833 }
834
835 void R_Shadow_RenderMode_ActiveLight(rtlight_t *rtlight)
836 {
837         r_shadow_rtlight = rtlight;
838 }
839
840 void R_Shadow_RenderMode_Reset(void)
841 {
842         rmeshstate_t m;
843         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
844         {
845                 qglUseProgramObjectARB(0);
846                 // HACK HACK HACK: work around for bug in NVIDIAI 6xxx drivers that causes GL_OUT_OF_MEMORY and/or software rendering
847                 qglBegin(GL_TRIANGLES);
848                 qglEnd();
849                 CHECKGLERROR
850         }
851         else if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
852                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
853         memset(&m, 0, sizeof(m));
854         R_Mesh_State(&m);
855 }
856
857 void R_Shadow_RenderMode_StencilShadowVolumes(void)
858 {
859         R_Shadow_RenderMode_Reset();
860         GL_Color(1, 1, 1, 1);
861         GL_ColorMask(0, 0, 0, 0);
862         GL_BlendFunc(GL_ONE, GL_ZERO);
863         GL_DepthMask(false);
864         GL_DepthTest(true);
865         if (!r_showtrispass)
866                 qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
867         //if (r_shadow_shadow_polygonoffset.value != 0)
868         //{
869         //      qglPolygonOffset(r_shadow_shadow_polygonfactor.value, r_shadow_shadow_polygonoffset.value);
870         //      qglEnable(GL_POLYGON_OFFSET_FILL);
871         //}
872         //else
873         //      qglDisable(GL_POLYGON_OFFSET_FILL);
874         qglDepthFunc(GL_LESS);
875         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
876         qglEnable(GL_STENCIL_TEST);
877         qglStencilFunc(GL_ALWAYS, 128, ~0);
878         r_shadow_rendermode = r_shadow_shadowingrendermode;
879         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCILTWOSIDE)
880         {
881                 qglDisable(GL_CULL_FACE);
882                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
883                 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
884                 qglStencilMask(~0);
885                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
886                 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
887                 qglStencilMask(~0);
888                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
889         }
890         else
891         {
892                 qglEnable(GL_CULL_FACE);
893                 qglStencilMask(~0);
894                 // this is changed by every shadow render so its value here is unimportant
895                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
896         }
897         GL_Clear(GL_STENCIL_BUFFER_BIT);
898         renderstats.lights_clears++;
899 }
900
901 void R_Shadow_RenderMode_Lighting(qboolean stenciltest, qboolean transparent)
902 {
903         R_Shadow_RenderMode_Reset();
904         GL_BlendFunc(GL_ONE, GL_ONE);
905         GL_DepthMask(false);
906         GL_DepthTest(true);
907         if (!r_showtrispass)
908                 qglPolygonOffset(0, 0);
909         //qglDisable(GL_POLYGON_OFFSET_FILL);
910         GL_Color(1, 1, 1, 1);
911         GL_ColorMask(r_refdef.colormask[0], r_refdef.colormask[1], r_refdef.colormask[2], 1);
912         if (transparent)
913                 qglDepthFunc(GL_LEQUAL);
914         else
915                 qglDepthFunc(GL_EQUAL);
916         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
917         qglEnable(GL_CULL_FACE);
918         if (stenciltest)
919                 qglEnable(GL_STENCIL_TEST);
920         else
921                 qglDisable(GL_STENCIL_TEST);
922         qglStencilMask(~0);
923         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
924         // only draw light where this geometry was already rendered AND the
925         // stencil is 128 (values other than this mean shadow)
926         qglStencilFunc(GL_EQUAL, 128, ~0);
927         r_shadow_rendermode = r_shadow_lightingrendermode;
928         // do global setup needed for the chosen lighting mode
929         if (r_shadow_rendermode == R_SHADOW_RENDERMODE_LIGHT_GLSL)
930         {
931                 R_Mesh_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, r_shadow_entityeyeorigin, lightcolorbase, false);
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         // calculate colors to render this texture with
2176         lightcolorbase[0] = r_shadow_rtlight->currentcolor[0] * ent->colormod[0] * texture->currentalpha;
2177         lightcolorbase[1] = r_shadow_rtlight->currentcolor[1] * ent->colormod[1] * texture->currentalpha;
2178         lightcolorbase[2] = r_shadow_rtlight->currentcolor[2] * ent->colormod[2] * texture->currentalpha;
2179         if ((r_shadow_rtlight->ambientscale + r_shadow_rtlight->diffusescale) * VectorLength2(lightcolorbase) + (r_shadow_rtlight->specularscale * texture->specularscale) * VectorLength2(lightcolorbase) < (1.0f / 1048576.0f))
2180                 return;
2181         if ((texture->textureflags & Q3TEXTUREFLAG_TWOSIDED) || (ent->flags & RENDER_NOCULLFACE))
2182                 qglDisable(GL_CULL_FACE);
2183         else
2184                 qglEnable(GL_CULL_FACE);
2185         if (texture->colormapping)
2186         {
2187                 qboolean dopants = texture->skin.pants != NULL && VectorLength2(ent->colormap_pantscolor) >= (1.0f / 1048576.0f);
2188                 qboolean doshirt = texture->skin.shirt != NULL && VectorLength2(ent->colormap_shirtcolor) >= (1.0f / 1048576.0f);
2189                 if (dopants)
2190                 {
2191                         lightcolorpants[0] = lightcolorbase[0] * ent->colormap_pantscolor[0];
2192                         lightcolorpants[1] = lightcolorbase[1] * ent->colormap_pantscolor[1];
2193                         lightcolorpants[2] = lightcolorbase[2] * ent->colormap_pantscolor[2];
2194                 }
2195                 else
2196                         VectorClear(lightcolorpants);
2197                 if (doshirt)
2198                 {
2199                         lightcolorshirt[0] = lightcolorbase[0] * ent->colormap_shirtcolor[0];
2200                         lightcolorshirt[1] = lightcolorbase[1] * ent->colormap_shirtcolor[1];
2201                         lightcolorshirt[2] = lightcolorbase[2] * ent->colormap_shirtcolor[2];
2202                 }
2203                 else
2204                         VectorClear(lightcolorshirt);
2205                 switch (r_shadow_rendermode)
2206                 {
2207                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2208                         R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2209                         break;
2210                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2211                         R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2212                         break;
2213                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2214                         R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2215                         break;
2216                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2217                         R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, lightcolorpants, lightcolorshirt, texture->basetexture, texture->skin.pants, texture->skin.shirt, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, dopants, doshirt);
2218                         break;
2219                 default:
2220                         Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2221                         break;
2222                 }
2223         }
2224         else
2225         {
2226                 switch (r_shadow_rendermode)
2227                 {
2228                 case R_SHADOW_RENDERMODE_VISIBLELIGHTING:
2229                         R_Shadow_RenderSurfacesLighting_VisibleLighting(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2230                         break;
2231                 case R_SHADOW_RENDERMODE_LIGHT_GLSL:
2232                         R_Shadow_RenderSurfacesLighting_Light_GLSL(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2233                         break;
2234                 case R_SHADOW_RENDERMODE_LIGHT_DOT3:
2235                         R_Shadow_RenderSurfacesLighting_Light_Dot3(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2236                         break;
2237                 case R_SHADOW_RENDERMODE_LIGHT_VERTEX:
2238                         R_Shadow_RenderSurfacesLighting_Light_Vertex(ent, texture, numsurfaces, surfacelist, lightcolorbase, vec3_origin, vec3_origin, texture->basetexture, r_texture_black, r_texture_black, texture->skin.nmap, texture->glosstexture, r_shadow_rtlight->specularscale * texture->specularscale, false, false);
2239                         break;
2240                 default:
2241                         Con_Printf("R_Shadow_RenderSurfacesLighting: unknown r_shadow_rendermode %i\n", r_shadow_rendermode);
2242                         break;
2243                 }
2244         }
2245 }
2246
2247 void R_RTLight_Update(dlight_t *light, int isstatic)
2248 {
2249         int j, k;
2250         float scale;
2251         rtlight_t *rtlight = &light->rtlight;
2252         R_RTLight_Uncompile(rtlight);
2253         memset(rtlight, 0, sizeof(*rtlight));
2254
2255         VectorCopy(light->origin, rtlight->shadoworigin);
2256         VectorCopy(light->color, rtlight->color);
2257         rtlight->radius = light->radius;
2258         //rtlight->cullradius = rtlight->radius;
2259         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
2260         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2261         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2262         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2263         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2264         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2265         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2266         rtlight->cubemapname[0] = 0;
2267         if (light->cubemapname[0])
2268                 strcpy(rtlight->cubemapname, light->cubemapname);
2269         else if (light->cubemapnum > 0)
2270                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
2271         rtlight->shadow = light->shadow;
2272         rtlight->corona = light->corona;
2273         rtlight->style = light->style;
2274         rtlight->isstatic = isstatic;
2275         rtlight->coronasizescale = light->coronasizescale;
2276         rtlight->ambientscale = light->ambientscale;
2277         rtlight->diffusescale = light->diffusescale;
2278         rtlight->specularscale = light->specularscale;
2279         rtlight->flags = light->flags;
2280         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
2281         // ConcatScale won't work here because this needs to scale rotate and
2282         // translate, not just rotate
2283         scale = 1.0f / rtlight->radius;
2284         for (k = 0;k < 3;k++)
2285                 for (j = 0;j < 4;j++)
2286                         rtlight->matrix_worldtolight.m[k][j] *= scale;
2287 }
2288
2289 // compiles rtlight geometry
2290 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
2291 void R_RTLight_Compile(rtlight_t *rtlight)
2292 {
2293         int shadowmeshes, shadowtris, numleafs, numleafpvsbytes, numsurfaces;
2294         entity_render_t *ent = r_refdef.worldentity;
2295         model_t *model = r_refdef.worldmodel;
2296         unsigned char *data;
2297
2298         // compile the light
2299         rtlight->compiled = true;
2300         rtlight->static_numleafs = 0;
2301         rtlight->static_numleafpvsbytes = 0;
2302         rtlight->static_leaflist = NULL;
2303         rtlight->static_leafpvs = NULL;
2304         rtlight->static_numsurfaces = 0;
2305         rtlight->static_surfacelist = NULL;
2306         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2307         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2308         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2309         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2310         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2311         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2312
2313         if (model && model->GetLightInfo)
2314         {
2315                 // this variable must be set for the CompileShadowVolume code
2316                 r_shadow_compilingrtlight = rtlight;
2317                 R_Shadow_EnlargeLeafSurfaceBuffer(model->brush.num_leafs, model->num_surfaces);
2318                 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);
2319                 numleafpvsbytes = (model->brush.num_leafs + 7) >> 3;
2320                 data = (unsigned char *)Mem_Alloc(r_shadow_mempool, sizeof(int) * numleafs + numleafpvsbytes + sizeof(int) * numsurfaces);
2321                 rtlight->static_numleafs = numleafs;
2322                 rtlight->static_numleafpvsbytes = numleafpvsbytes;
2323                 rtlight->static_leaflist = (int *)data;data += sizeof(int) * numleafs;
2324                 rtlight->static_leafpvs = (unsigned char *)data;data += numleafpvsbytes;
2325                 rtlight->static_numsurfaces = numsurfaces;
2326                 rtlight->static_surfacelist = (int *)data;data += sizeof(int) * numsurfaces;
2327                 if (numleafs)
2328                         memcpy(rtlight->static_leaflist, r_shadow_buffer_leaflist, rtlight->static_numleafs * sizeof(*rtlight->static_leaflist));
2329                 if (numleafpvsbytes)
2330                         memcpy(rtlight->static_leafpvs, r_shadow_buffer_leafpvs, rtlight->static_numleafpvsbytes);
2331                 if (numsurfaces)
2332                         memcpy(rtlight->static_surfacelist, r_shadow_buffer_surfacelist, rtlight->static_numsurfaces * sizeof(*rtlight->static_surfacelist));
2333                 if (model->CompileShadowVolume && rtlight->shadow)
2334                         model->CompileShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
2335                 // now we're done compiling the rtlight
2336                 r_shadow_compilingrtlight = NULL;
2337         }
2338
2339
2340         // use smallest available cullradius - box radius or light radius
2341         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
2342         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
2343
2344         shadowmeshes = 0;
2345         shadowtris = 0;
2346         if (rtlight->static_meshchain_shadow)
2347         {
2348                 shadowmesh_t *mesh;
2349                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2350                 {
2351                         shadowmeshes++;
2352                         shadowtris += mesh->numtriangles;
2353                 }
2354         }
2355
2356         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);
2357 }
2358
2359 void R_RTLight_Uncompile(rtlight_t *rtlight)
2360 {
2361         if (rtlight->compiled)
2362         {
2363                 if (rtlight->static_meshchain_shadow)
2364                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2365                 rtlight->static_meshchain_shadow = NULL;
2366                 // these allocations are grouped
2367                 if (rtlight->static_leaflist)
2368                         Mem_Free(rtlight->static_leaflist);
2369                 rtlight->static_numleafs = 0;
2370                 rtlight->static_numleafpvsbytes = 0;
2371                 rtlight->static_leaflist = NULL;
2372                 rtlight->static_leafpvs = NULL;
2373                 rtlight->static_numsurfaces = 0;
2374                 rtlight->static_surfacelist = NULL;
2375                 rtlight->compiled = false;
2376         }
2377 }
2378
2379 void R_Shadow_UncompileWorldLights(void)
2380 {
2381         dlight_t *light;
2382         for (light = r_shadow_worldlightchain;light;light = light->next)
2383                 R_RTLight_Uncompile(&light->rtlight);
2384 }
2385
2386 void R_Shadow_DrawEntityShadow(entity_render_t *ent, int numsurfaces, int *surfacelist)
2387 {
2388         vec3_t relativeshadoworigin, relativeshadowmins, relativeshadowmaxs;
2389         vec_t relativeshadowradius;
2390         if (ent == r_refdef.worldentity)
2391         {
2392                 if (r_shadow_rtlight->compiled && r_shadow_realtime_world_compile.integer && r_shadow_realtime_world_compileshadow.integer)
2393                 {
2394                         shadowmesh_t *mesh;
2395                         R_Mesh_Matrix(&ent->matrix);
2396                         for (mesh = r_shadow_rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2397                         {
2398                                 renderstats.lights_shadowtriangles += mesh->numtriangles;
2399                                 R_Mesh_VertexPointer(mesh->vertex3f);
2400                                 GL_LockArrays(0, mesh->numverts);
2401                                 if (r_shadow_rendermode == R_SHADOW_RENDERMODE_STENCIL)
2402                                 {
2403                                         // decrement stencil if backface is behind depthbuffer
2404                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2405                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2406                                         R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2407                                         // increment stencil if frontface is behind depthbuffer
2408                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2409                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2410                                 }
2411                                 R_Mesh_Draw(0, mesh->numverts, mesh->numtriangles, mesh->element3i);
2412                                 GL_LockArrays(0, 0);
2413                         }
2414                 }
2415                 else if (numsurfaces)
2416                 {
2417                         R_Mesh_Matrix(&ent->matrix);
2418                         ent->model->DrawShadowVolume(ent, r_shadow_rtlight->shadoworigin, r_shadow_rtlight->radius, numsurfaces, surfacelist, r_shadow_rtlight->cullmins, r_shadow_rtlight->cullmaxs);
2419                 }
2420         }
2421         else
2422         {
2423                 Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, relativeshadoworigin);
2424                 relativeshadowradius = r_shadow_rtlight->radius / ent->scale;
2425                 relativeshadowmins[0] = relativeshadoworigin[0] - relativeshadowradius;
2426                 relativeshadowmins[1] = relativeshadoworigin[1] - relativeshadowradius;
2427                 relativeshadowmins[2] = relativeshadoworigin[2] - relativeshadowradius;
2428                 relativeshadowmaxs[0] = relativeshadoworigin[0] + relativeshadowradius;
2429                 relativeshadowmaxs[1] = relativeshadoworigin[1] + relativeshadowradius;
2430                 relativeshadowmaxs[2] = relativeshadoworigin[2] + relativeshadowradius;
2431                 R_Mesh_Matrix(&ent->matrix);
2432                 ent->model->DrawShadowVolume(ent, relativeshadoworigin, relativeshadowradius, ent->model->nummodelsurfaces, ent->model->surfacelist, relativeshadowmins, relativeshadowmaxs);
2433         }
2434 }
2435
2436 void R_Shadow_SetupEntityLight(const entity_render_t *ent)
2437 {
2438         // set up properties for rendering light onto this entity
2439         Matrix4x4_Concat(&r_shadow_entitytolight, &r_shadow_rtlight->matrix_worldtolight, &ent->matrix);
2440         Matrix4x4_Concat(&r_shadow_entitytoattenuationxyz, &matrix_attenuationxyz, &r_shadow_entitytolight);
2441         Matrix4x4_Concat(&r_shadow_entitytoattenuationz, &matrix_attenuationz, &r_shadow_entitytolight);
2442         Matrix4x4_Transform(&ent->inversematrix, r_shadow_rtlight->shadoworigin, r_shadow_entitylightorigin);
2443         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, r_shadow_entityeyeorigin);
2444         R_Mesh_Matrix(&ent->matrix);
2445 }
2446
2447 void R_Shadow_DrawEntityLight(entity_render_t *ent, int numsurfaces, int *surfacelist)
2448 {
2449         R_Shadow_SetupEntityLight(ent);
2450         if (ent == r_refdef.worldentity)
2451                 ent->model->DrawLight(ent, numsurfaces, surfacelist);
2452         else
2453                 ent->model->DrawLight(ent, ent->model->nummodelsurfaces, ent->model->surfacelist);
2454 }
2455
2456 void R_DrawRTLight(rtlight_t *rtlight, qboolean visible)
2457 {
2458         int i, usestencil;
2459         float f;
2460         int numleafs, numsurfaces;
2461         int *leaflist, *surfacelist;
2462         unsigned char *leafpvs;
2463         int numlightentities;
2464         int numshadowentities;
2465         entity_render_t *lightentities[MAX_EDICTS];
2466         entity_render_t *shadowentities[MAX_EDICTS];
2467
2468         // skip lights that don't light because of ambientscale+diffusescale+specularscale being 0 (corona only lights)
2469         // skip lights that are basically invisible (color 0 0 0)
2470         if (VectorLength2(rtlight->color) * (rtlight->ambientscale + rtlight->diffusescale + rtlight->specularscale) < (1.0f / 1048576.0f))
2471                 return;
2472
2473         // loading is done before visibility checks because loading should happen
2474         // all at once at the start of a level, not when it stalls gameplay.
2475         // (especially important to benchmarks)
2476         // compile light
2477         if (rtlight->isstatic && !rtlight->compiled && r_shadow_realtime_world_compile.integer)
2478                 R_RTLight_Compile(rtlight);
2479         // load cubemap
2480         rtlight->currentcubemap = rtlight->cubemapname[0] ? R_Shadow_Cubemap(rtlight->cubemapname) : r_texture_whitecube;
2481
2482         // look up the light style value at this time
2483         f = (rtlight->style >= 0 ? r_refdef.lightstylevalue[rtlight->style] : 128) * (1.0f / 256.0f) * r_shadow_lightintensityscale.value;
2484         VectorScale(rtlight->color, f, rtlight->currentcolor);
2485         /*
2486         if (rtlight->selected)
2487         {
2488                 f = 2 + sin(realtime * M_PI * 4.0);
2489                 VectorScale(rtlight->currentcolor, f, rtlight->currentcolor);
2490         }
2491         */
2492
2493         // if lightstyle is currently off, don't draw the light
2494         if (VectorLength2(rtlight->currentcolor) < (1.0f / 1048576.0f))
2495                 return;
2496
2497         // if the light box is offscreen, skip it
2498         if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2499                 return;
2500
2501         if (rtlight->compiled && r_shadow_realtime_world_compile.integer)
2502         {
2503                 // compiled light, world available and can receive realtime lighting
2504                 // retrieve leaf information
2505                 numleafs = rtlight->static_numleafs;
2506                 leaflist = rtlight->static_leaflist;
2507                 leafpvs = rtlight->static_leafpvs;
2508                 numsurfaces = rtlight->static_numsurfaces;
2509                 surfacelist = rtlight->static_surfacelist;
2510         }
2511         else if (r_refdef.worldmodel && r_refdef.worldmodel->GetLightInfo)
2512         {
2513                 // dynamic light, world available and can receive realtime lighting
2514                 // calculate lit surfaces and leafs
2515                 R_Shadow_EnlargeLeafSurfaceBuffer(r_refdef.worldmodel->brush.num_leafs, r_refdef.worldmodel->num_surfaces);
2516                 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);
2517                 leaflist = r_shadow_buffer_leaflist;
2518                 leafpvs = r_shadow_buffer_leafpvs;
2519                 surfacelist = r_shadow_buffer_surfacelist;
2520                 // if the reduced leaf bounds are offscreen, skip it
2521                 if (R_CullBox(rtlight->cullmins, rtlight->cullmaxs))
2522                         return;
2523         }
2524         else
2525         {
2526                 // no world
2527                 numleafs = 0;
2528                 leaflist = NULL;
2529                 leafpvs = NULL;
2530                 numsurfaces = 0;
2531                 surfacelist = NULL;
2532         }
2533         // check if light is illuminating any visible leafs
2534         if (numleafs)
2535         {
2536                 for (i = 0;i < numleafs;i++)
2537                         if (r_worldleafvisible[leaflist[i]])
2538                                 break;
2539                 if (i == numleafs)
2540                         return;
2541         }
2542         // set up a scissor rectangle for this light
2543         if (R_Shadow_ScissorForBBox(rtlight->cullmins, rtlight->cullmaxs))
2544                 return;
2545
2546         // make a list of lit entities and shadow casting entities
2547         numlightentities = 0;
2548         numshadowentities = 0;
2549         // don't count the world unless some surfaces are actually lit
2550         if (numsurfaces)
2551         {
2552                 lightentities[numlightentities++] = r_refdef.worldentity;
2553                 shadowentities[numshadowentities++] = r_refdef.worldentity;
2554         }
2555         // add dynamic entities that are lit by the light
2556         if (r_drawentities.integer)
2557         {
2558                 for (i = 0;i < r_refdef.numentities;i++)
2559                 {
2560                         entity_render_t *ent = r_refdef.entities[i];
2561                         if (BoxesOverlap(ent->mins, ent->maxs, rtlight->cullmins, rtlight->cullmaxs)
2562                          && ent->model
2563                          && !(ent->flags & RENDER_TRANSPARENT)
2564                          && (r_refdef.worldmodel == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS == NULL || r_refdef.worldmodel->brush.BoxTouchingLeafPVS(r_refdef.worldmodel, leafpvs, ent->mins, ent->maxs)))
2565                         {
2566                                 // about the VectorDistance2 - light emitting entities should not cast their own shadow
2567                                 if ((ent->flags & RENDER_SHADOW) && ent->model->DrawShadowVolume && VectorDistance2(ent->origin, rtlight->shadoworigin) > 0.1)
2568                                         shadowentities[numshadowentities++] = ent;
2569                                 if (ent->visframe == r_framecount && (ent->flags & RENDER_LIGHT) && ent->model->DrawLight)
2570                                         lightentities[numlightentities++] = ent;
2571                         }
2572                 }
2573         }
2574
2575         // return if there's nothing at all to light
2576         if (!numlightentities)
2577                 return;
2578
2579         // don't let sound skip if going slow
2580         if (r_refdef.extraupdate)
2581                 S_ExtraUpdate ();
2582
2583         // make this the active rtlight for rendering purposes
2584         R_Shadow_RenderMode_ActiveLight(rtlight);
2585         // count this light in the r_speeds
2586         renderstats.lights++;
2587
2588         usestencil = false;
2589         if (numshadowentities && rtlight->shadow && (rtlight->isstatic ? r_rtworldshadows : r_rtdlightshadows))
2590         {
2591                 // draw stencil shadow volumes to mask off pixels that are in shadow
2592                 // so that they won't receive lighting
2593                 if (gl_stencil)
2594                 {
2595                         usestencil = true;
2596                         R_Shadow_RenderMode_StencilShadowVolumes();
2597                         for (i = 0;i < numshadowentities;i++)
2598                                 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2599                 }
2600
2601                 // optionally draw visible shape of the shadow volumes
2602                 // for performance analysis by level designers
2603                 if (r_showshadowvolumes.integer)
2604                 {
2605                         R_Shadow_RenderMode_VisibleShadowVolumes();
2606                         for (i = 0;i < numshadowentities;i++)
2607                                 R_Shadow_DrawEntityShadow(shadowentities[i], numsurfaces, surfacelist);
2608                 }
2609         }
2610
2611         if (numlightentities)
2612         {
2613                 // draw lighting in the unmasked areas
2614                 R_Shadow_RenderMode_Lighting(usestencil, false);
2615                 for (i = 0;i < numlightentities;i++)
2616                         R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2617
2618                 // optionally draw the illuminated areas
2619                 // for performance analysis by level designers
2620                 if (r_showlighting.integer)
2621                 {
2622                         R_Shadow_RenderMode_VisibleLighting(usestencil && !r_showdisabledepthtest.integer, false);
2623                         for (i = 0;i < numlightentities;i++)
2624                                 R_Shadow_DrawEntityLight(lightentities[i], numsurfaces, surfacelist);
2625                 }
2626         }
2627 }
2628
2629 void R_ShadowVolumeLighting(qboolean visible)
2630 {
2631         int lnum, flag;
2632         dlight_t *light;
2633
2634         if (r_refdef.worldmodel && strncmp(r_refdef.worldmodel->name, r_shadow_mapname, sizeof(r_shadow_mapname)))
2635                 R_Shadow_EditLights_Reload_f();
2636
2637         R_Shadow_RenderMode_Begin();
2638
2639         flag = r_rtworld ? LIGHTFLAG_REALTIMEMODE : LIGHTFLAG_NORMALMODE;
2640         if (r_shadow_debuglight.integer >= 0)
2641         {
2642                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2643                         if (lnum == r_shadow_debuglight.integer && (light->flags & flag))
2644                                 R_DrawRTLight(&light->rtlight, visible);
2645         }
2646         else
2647                 for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2648                         if (light->flags & flag)
2649                                 R_DrawRTLight(&light->rtlight, visible);
2650         if (r_rtdlight)
2651                 for (lnum = 0;lnum < r_refdef.numlights;lnum++)
2652                         R_DrawRTLight(&r_refdef.lights[lnum]->rtlight, visible);
2653
2654         R_Shadow_RenderMode_End();
2655 }
2656
2657 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2658 typedef struct suffixinfo_s
2659 {
2660         char *suffix;
2661         qboolean flipx, flipy, flipdiagonal;
2662 }
2663 suffixinfo_t;
2664 static suffixinfo_t suffix[3][6] =
2665 {
2666         {
2667                 {"px",   false, false, false},
2668                 {"nx",   false, false, false},
2669                 {"py",   false, false, false},
2670                 {"ny",   false, false, false},
2671                 {"pz",   false, false, false},
2672                 {"nz",   false, false, false}
2673         },
2674         {
2675                 {"posx", false, false, false},
2676                 {"negx", false, false, false},
2677                 {"posy", false, false, false},
2678                 {"negy", false, false, false},
2679                 {"posz", false, false, false},
2680                 {"negz", false, false, false}
2681         },
2682         {
2683                 {"rt",    true, false,  true},
2684                 {"lf",   false,  true,  true},
2685                 {"ft",    true,  true, false},
2686                 {"bk",   false, false, false},
2687                 {"up",    true, false,  true},
2688                 {"dn",    true, false,  true}
2689         }
2690 };
2691
2692 static int componentorder[4] = {0, 1, 2, 3};
2693
2694 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2695 {
2696         int i, j, cubemapsize;
2697         unsigned char *cubemappixels, *image_rgba;
2698         rtexture_t *cubemaptexture;
2699         char name[256];
2700         // must start 0 so the first loadimagepixels has no requested width/height
2701         cubemapsize = 0;
2702         cubemappixels = NULL;
2703         cubemaptexture = NULL;
2704         // keep trying different suffix groups (posx, px, rt) until one loads
2705         for (j = 0;j < 3 && !cubemappixels;j++)
2706         {
2707                 // load the 6 images in the suffix group
2708                 for (i = 0;i < 6;i++)
2709                 {
2710                         // generate an image name based on the base and and suffix
2711                         dpsnprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2712                         // load it
2713                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2714                         {
2715                                 // an image loaded, make sure width and height are equal
2716                                 if (image_width == image_height)
2717                                 {
2718                                         // if this is the first image to load successfully, allocate the cubemap memory
2719                                         if (!cubemappixels && image_width >= 1)
2720                                         {
2721                                                 cubemapsize = image_width;
2722                                                 // note this clears to black, so unavailable sides are black
2723                                                 cubemappixels = (unsigned char *)Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2724                                         }
2725                                         // copy the image with any flipping needed by the suffix (px and posx types don't need flipping)
2726                                         if (cubemappixels)
2727                                                 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);
2728                                 }
2729                                 else
2730                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2731                                 // free the image
2732                                 Mem_Free(image_rgba);
2733                         }
2734                 }
2735         }
2736         // if a cubemap loaded, upload it
2737         if (cubemappixels)
2738         {
2739                 if (!r_shadow_filters_texturepool)
2740                         r_shadow_filters_texturepool = R_AllocTexturePool();
2741                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2742                 Mem_Free(cubemappixels);
2743         }
2744         else
2745         {
2746                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2747                 for (j = 0;j < 3;j++)
2748                         for (i = 0;i < 6;i++)
2749                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2750                 Con_Print(" and was unable to find any of them.\n");
2751         }
2752         return cubemaptexture;
2753 }
2754
2755 rtexture_t *R_Shadow_Cubemap(const char *basename)
2756 {
2757         int i;
2758         for (i = 0;i < numcubemaps;i++)
2759                 if (!strcasecmp(cubemaps[i].basename, basename))
2760                         return cubemaps[i].texture;
2761         if (i >= MAX_CUBEMAPS)
2762                 return r_texture_whitecube;
2763         numcubemaps++;
2764         strcpy(cubemaps[i].basename, basename);
2765         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2766         if (!cubemaps[i].texture)
2767                 cubemaps[i].texture = r_texture_whitecube;
2768         return cubemaps[i].texture;
2769 }
2770
2771 void R_Shadow_FreeCubemaps(void)
2772 {
2773         numcubemaps = 0;
2774         R_FreeTexturePool(&r_shadow_filters_texturepool);
2775 }
2776
2777 dlight_t *R_Shadow_NewWorldLight(void)
2778 {
2779         dlight_t *light;
2780         light = (dlight_t *)Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2781         light->next = r_shadow_worldlightchain;
2782         r_shadow_worldlightchain = light;
2783         return light;
2784 }
2785
2786 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)
2787 {
2788         VectorCopy(origin, light->origin);
2789         light->angles[0] = angles[0] - 360 * floor(angles[0] / 360);
2790         light->angles[1] = angles[1] - 360 * floor(angles[1] / 360);
2791         light->angles[2] = angles[2] - 360 * floor(angles[2] / 360);
2792         light->color[0] = max(color[0], 0);
2793         light->color[1] = max(color[1], 0);
2794         light->color[2] = max(color[2], 0);
2795         light->radius = max(radius, 0);
2796         light->style = style;
2797         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2798         {
2799                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2800                 light->style = 0;
2801         }
2802         light->shadow = shadowenable;
2803         light->corona = corona;
2804         if (!cubemapname)
2805                 cubemapname = "";
2806         strlcpy(light->cubemapname, cubemapname, sizeof(light->cubemapname));
2807         light->coronasizescale = coronasizescale;
2808         light->ambientscale = ambientscale;
2809         light->diffusescale = diffusescale;
2810         light->specularscale = specularscale;
2811         light->flags = flags;
2812         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2813
2814         R_RTLight_Update(light, true);
2815 }
2816
2817 void R_Shadow_FreeWorldLight(dlight_t *light)
2818 {
2819         dlight_t **lightpointer;
2820         R_RTLight_Uncompile(&light->rtlight);
2821         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2822         if (*lightpointer != light)
2823                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain");
2824         *lightpointer = light->next;
2825         Mem_Free(light);
2826 }
2827
2828 void R_Shadow_ClearWorldLights(void)
2829 {
2830         while (r_shadow_worldlightchain)
2831                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2832         r_shadow_selectedlight = NULL;
2833         R_Shadow_FreeCubemaps();
2834 }
2835
2836 void R_Shadow_SelectLight(dlight_t *light)
2837 {
2838         if (r_shadow_selectedlight)
2839                 r_shadow_selectedlight->selected = false;
2840         r_shadow_selectedlight = light;
2841         if (r_shadow_selectedlight)
2842                 r_shadow_selectedlight->selected = true;
2843 }
2844
2845 void R_Shadow_DrawCursor_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2846 {
2847         float scale = r_editlights_cursorgrid.value * 0.5f;
2848         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, r_crosshairs[1]->tex, NULL, false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2849 }
2850
2851 void R_Shadow_DrawLightSprite_TransparentCallback(const entity_render_t *ent, int surfacenumber, const rtlight_t *rtlight)
2852 {
2853         float intensity;
2854         const dlight_t *light = (dlight_t *)ent;
2855         intensity = 0.5;
2856         if (light->selected)
2857                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2858         if (!light->shadow)
2859                 intensity *= 0.5f;
2860         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);
2861 }
2862
2863 void R_Shadow_DrawLightSprites(void)
2864 {
2865         int i;
2866         dlight_t *light;
2867
2868         for (i = 0, light = r_shadow_worldlightchain;light;i++, light = light->next)
2869                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSprite_TransparentCallback, (entity_render_t *)light, 1+(i % 5), &light->rtlight);
2870         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursor_TransparentCallback, NULL, 0, NULL);
2871 }
2872
2873 void R_Shadow_SelectLightInView(void)
2874 {
2875         float bestrating, rating, temp[3];
2876         dlight_t *best, *light;
2877         best = NULL;
2878         bestrating = 0;
2879         for (light = r_shadow_worldlightchain;light;light = light->next)
2880         {
2881                 VectorSubtract(light->origin, r_vieworigin, temp);
2882                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2883                 if (rating >= 0.95)
2884                 {
2885                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2886                         if (bestrating < rating && CL_TraceBox(light->origin, vec3_origin, vec3_origin, r_vieworigin, true, NULL, SUPERCONTENTS_SOLID, false).fraction == 1.0f)
2887                         {
2888                                 bestrating = rating;
2889                                 best = light;
2890                         }
2891                 }
2892         }
2893         R_Shadow_SelectLight(best);
2894 }
2895
2896 void R_Shadow_LoadWorldLights(void)
2897 {
2898         int n, a, style, shadow, flags;
2899         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH], cubemapname[MAX_QPATH];
2900         float origin[3], radius, color[3], angles[3], corona, coronasizescale, ambientscale, diffusescale, specularscale;
2901         if (r_refdef.worldmodel == NULL)
2902         {
2903                 Con_Print("No map loaded.\n");
2904                 return;
2905         }
2906         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
2907         strlcat (name, ".rtlights", sizeof (name));
2908         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
2909         if (lightsstring)
2910         {
2911                 s = lightsstring;
2912                 n = 0;
2913                 while (*s)
2914                 {
2915                         t = s;
2916                         /*
2917                         shadow = true;
2918                         for (;COM_Parse(t, true) && strcmp(
2919                         if (COM_Parse(t, true))
2920                         {
2921                                 if (com_token[0] == '!')
2922                                 {
2923                                         shadow = false;
2924                                         origin[0] = atof(com_token+1);
2925                                 }
2926                                 else
2927                                         origin[0] = atof(com_token);
2928                                 if (Com_Parse(t
2929                         }
2930                         */
2931                         t = s;
2932                         while (*s && *s != '\n' && *s != '\r')
2933                                 s++;
2934                         if (!*s)
2935                                 break;
2936                         tempchar = *s;
2937                         shadow = true;
2938                         // check for modifier flags
2939                         if (*t == '!')
2940                         {
2941                                 shadow = false;
2942                                 t++;
2943                         }
2944                         *s = 0;
2945                         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);
2946                         *s = tempchar;
2947                         if (a < 18)
2948                                 flags = LIGHTFLAG_REALTIMEMODE;
2949                         if (a < 17)
2950                                 specularscale = 1;
2951                         if (a < 16)
2952                                 diffusescale = 1;
2953                         if (a < 15)
2954                                 ambientscale = 0;
2955                         if (a < 14)
2956                                 coronasizescale = 0.25f;
2957                         if (a < 13)
2958                                 VectorClear(angles);
2959                         if (a < 10)
2960                                 corona = 0;
2961                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2962                                 cubemapname[0] = 0;
2963                         // remove quotes on cubemapname
2964                         if (cubemapname[0] == '"' && cubemapname[strlen(cubemapname) - 1] == '"')
2965                         {
2966                                 cubemapname[strlen(cubemapname)-1] = 0;
2967                                 strcpy(cubemapname, cubemapname + 1);
2968                         }
2969                         if (a < 8)
2970                         {
2971                                 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);
2972                                 break;
2973                         }
2974                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, angles, color, radius, corona, style, shadow, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
2975                         if (*s == '\r')
2976                                 s++;
2977                         if (*s == '\n')
2978                                 s++;
2979                         n++;
2980                 }
2981                 if (*s)
2982                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2983                 Mem_Free(lightsstring);
2984         }
2985 }
2986
2987 void R_Shadow_SaveWorldLights(void)
2988 {
2989         dlight_t *light;
2990         size_t bufchars, bufmaxchars;
2991         char *buf, *oldbuf;
2992         char name[MAX_QPATH];
2993         char line[MAX_INPUTLINE];
2994         if (!r_shadow_worldlightchain)
2995                 return;
2996         if (r_refdef.worldmodel == NULL)
2997         {
2998                 Con_Print("No map loaded.\n");
2999                 return;
3000         }
3001         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3002         strlcat (name, ".rtlights", sizeof (name));
3003         bufchars = bufmaxchars = 0;
3004         buf = NULL;
3005         for (light = r_shadow_worldlightchain;light;light = light->next)
3006         {
3007                 if (light->coronasizescale != 0.25f || light->ambientscale != 0 || light->diffusescale != 1 || light->specularscale != 1 || light->flags != LIGHTFLAG_REALTIMEMODE)
3008                         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);
3009                 else if (light->cubemapname[0] || light->corona || light->angles[0] || light->angles[1] || light->angles[2])
3010                         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]);
3011                 else
3012                         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);
3013                 if (bufchars + strlen(line) > bufmaxchars)
3014                 {
3015                         bufmaxchars = bufchars + strlen(line) + 2048;
3016                         oldbuf = buf;
3017                         buf = (char *)Mem_Alloc(tempmempool, bufmaxchars);
3018                         if (oldbuf)
3019                         {
3020                                 if (bufchars)
3021                                         memcpy(buf, oldbuf, bufchars);
3022                                 Mem_Free(oldbuf);
3023                         }
3024                 }
3025                 if (strlen(line))
3026                 {
3027                         memcpy(buf + bufchars, line, strlen(line));
3028                         bufchars += strlen(line);
3029                 }
3030         }
3031         if (bufchars)
3032                 FS_WriteFile(name, buf, (fs_offset_t)bufchars);
3033         if (buf)
3034                 Mem_Free(buf);
3035 }
3036
3037 void R_Shadow_LoadLightsFile(void)
3038 {
3039         int n, a, style;
3040         char tempchar, *lightsstring, *s, *t, name[MAX_QPATH];
3041         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
3042         if (r_refdef.worldmodel == NULL)
3043         {
3044                 Con_Print("No map loaded.\n");
3045                 return;
3046         }
3047         FS_StripExtension (r_refdef.worldmodel->name, name, sizeof (name));
3048         strlcat (name, ".lights", sizeof (name));
3049         lightsstring = (char *)FS_LoadFile(name, tempmempool, false, NULL);
3050         if (lightsstring)
3051         {
3052                 s = lightsstring;
3053                 n = 0;
3054                 while (*s)
3055                 {
3056                         t = s;
3057                         while (*s && *s != '\n' && *s != '\r')
3058                                 s++;
3059                         if (!*s)
3060                                 break;
3061                         tempchar = *s;
3062                         *s = 0;
3063                         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);
3064                         *s = tempchar;
3065                         if (a < 14)
3066                         {
3067                                 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);
3068                                 break;
3069                         }
3070                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
3071                         radius = bound(15, radius, 4096);
3072                         VectorScale(color, (2.0f / (8388608.0f)), color);
3073                         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), origin, vec3_origin, color, radius, 0, style, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3074                         if (*s == '\r')
3075                                 s++;
3076                         if (*s == '\n')
3077                                 s++;
3078                         n++;
3079                 }
3080                 if (*s)
3081                         Con_Printf("invalid lights file \"%s\"\n", name);
3082                 Mem_Free(lightsstring);
3083         }
3084 }
3085
3086 // tyrlite/hmap2 light types in the delay field
3087 typedef enum lighttype_e {LIGHTTYPE_MINUSX, LIGHTTYPE_RECIPX, LIGHTTYPE_RECIPXX, LIGHTTYPE_NONE, LIGHTTYPE_SUN, LIGHTTYPE_MINUSXX} lighttype_t;
3088
3089 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
3090 {
3091         int entnum, style, islight, skin, pflags, effects, type, n;
3092         char *entfiledata;
3093         const char *data;
3094         float origin[3], angles[3], radius, color[3], light[4], fadescale, lightscale, originhack[3], overridecolor[3], vec[4];
3095         char key[256], value[MAX_INPUTLINE];
3096
3097         if (r_refdef.worldmodel == NULL)
3098         {
3099                 Con_Print("No map loaded.\n");
3100                 return;
3101         }
3102         // try to load a .ent file first
3103         FS_StripExtension (r_refdef.worldmodel->name, key, sizeof (key));
3104         strlcat (key, ".ent", sizeof (key));
3105         data = entfiledata = (char *)FS_LoadFile(key, tempmempool, true, NULL);
3106         // and if that is not found, fall back to the bsp file entity string
3107         if (!data)
3108                 data = r_refdef.worldmodel->brush.entities;
3109         if (!data)
3110                 return;
3111         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
3112         {
3113                 type = LIGHTTYPE_MINUSX;
3114                 origin[0] = origin[1] = origin[2] = 0;
3115                 originhack[0] = originhack[1] = originhack[2] = 0;
3116                 angles[0] = angles[1] = angles[2] = 0;
3117                 color[0] = color[1] = color[2] = 1;
3118                 light[0] = light[1] = light[2] = 1;light[3] = 300;
3119                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
3120                 fadescale = 1;
3121                 lightscale = 1;
3122                 style = 0;
3123                 skin = 0;
3124                 pflags = 0;
3125                 effects = 0;
3126                 islight = false;
3127                 while (1)
3128                 {
3129                         if (!COM_ParseToken(&data, false))
3130                                 break; // error
3131                         if (com_token[0] == '}')
3132                                 break; // end of entity
3133                         if (com_token[0] == '_')
3134                                 strcpy(key, com_token + 1);
3135                         else
3136                                 strcpy(key, com_token);
3137                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
3138                                 key[strlen(key)-1] = 0;
3139                         if (!COM_ParseToken(&data, false))
3140                                 break; // error
3141                         strcpy(value, com_token);
3142
3143                         // now that we have the key pair worked out...
3144                         if (!strcmp("light", key))
3145                         {
3146                                 n = sscanf(value, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
3147                                 if (n == 1)
3148                                 {
3149                                         // quake
3150                                         light[0] = vec[0] * (1.0f / 256.0f);
3151                                         light[1] = vec[0] * (1.0f / 256.0f);
3152                                         light[2] = vec[0] * (1.0f / 256.0f);
3153                                         light[3] = vec[0];
3154                                 }
3155                                 else if (n == 4)
3156                                 {
3157                                         // halflife
3158                                         light[0] = vec[0] * (1.0f / 255.0f);
3159                                         light[1] = vec[1] * (1.0f / 255.0f);
3160                                         light[2] = vec[2] * (1.0f / 255.0f);
3161                                         light[3] = vec[3];
3162                                 }
3163                         }
3164                         else if (!strcmp("delay", key))
3165                                 type = atoi(value);
3166                         else if (!strcmp("origin", key))
3167                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
3168                         else if (!strcmp("angle", key))
3169                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
3170                         else if (!strcmp("angles", key))
3171                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
3172                         else if (!strcmp("color", key))
3173                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
3174                         else if (!strcmp("wait", key))
3175                                 fadescale = atof(value);
3176                         else if (!strcmp("classname", key))
3177                         {
3178                                 if (!strncmp(value, "light", 5))
3179                                 {
3180                                         islight = true;
3181                                         if (!strcmp(value, "light_fluoro"))
3182                                         {
3183                                                 originhack[0] = 0;
3184                                                 originhack[1] = 0;
3185                                                 originhack[2] = 0;
3186                                                 overridecolor[0] = 1;
3187                                                 overridecolor[1] = 1;
3188                                                 overridecolor[2] = 1;
3189                                         }
3190                                         if (!strcmp(value, "light_fluorospark"))
3191                                         {
3192                                                 originhack[0] = 0;
3193                                                 originhack[1] = 0;
3194                                                 originhack[2] = 0;
3195                                                 overridecolor[0] = 1;
3196                                                 overridecolor[1] = 1;
3197                                                 overridecolor[2] = 1;
3198                                         }
3199                                         if (!strcmp(value, "light_globe"))
3200                                         {
3201                                                 originhack[0] = 0;
3202                                                 originhack[1] = 0;
3203                                                 originhack[2] = 0;
3204                                                 overridecolor[0] = 1;
3205                                                 overridecolor[1] = 0.8;
3206                                                 overridecolor[2] = 0.4;
3207                                         }
3208                                         if (!strcmp(value, "light_flame_large_yellow"))
3209                                         {
3210                                                 originhack[0] = 0;
3211                                                 originhack[1] = 0;
3212                                                 originhack[2] = 0;
3213                                                 overridecolor[0] = 1;
3214                                                 overridecolor[1] = 0.5;
3215                                                 overridecolor[2] = 0.1;
3216                                         }
3217                                         if (!strcmp(value, "light_flame_small_yellow"))
3218                                         {
3219                                                 originhack[0] = 0;
3220                                                 originhack[1] = 0;
3221                                                 originhack[2] = 0;
3222                                                 overridecolor[0] = 1;
3223                                                 overridecolor[1] = 0.5;
3224                                                 overridecolor[2] = 0.1;
3225                                         }
3226                                         if (!strcmp(value, "light_torch_small_white"))
3227                                         {
3228                                                 originhack[0] = 0;
3229                                                 originhack[1] = 0;
3230                                                 originhack[2] = 0;
3231                                                 overridecolor[0] = 1;
3232                                                 overridecolor[1] = 0.5;
3233                                                 overridecolor[2] = 0.1;
3234                                         }
3235                                         if (!strcmp(value, "light_torch_small_walltorch"))
3236                                         {
3237                                                 originhack[0] = 0;
3238                                                 originhack[1] = 0;
3239                                                 originhack[2] = 0;
3240                                                 overridecolor[0] = 1;
3241                                                 overridecolor[1] = 0.5;
3242                                                 overridecolor[2] = 0.1;
3243                                         }
3244                                 }
3245                         }
3246                         else if (!strcmp("style", key))
3247                                 style = atoi(value);
3248                         else if (!strcmp("skin", key))
3249                                 skin = (int)atof(value);
3250                         else if (!strcmp("pflags", key))
3251                                 pflags = (int)atof(value);
3252                         else if (!strcmp("effects", key))
3253                                 effects = (int)atof(value);
3254                         else if (r_refdef.worldmodel->type == mod_brushq3)
3255                         {
3256                                 if (!strcmp("scale", key))
3257                                         lightscale = atof(value);
3258                                 if (!strcmp("fade", key))
3259                                         fadescale = atof(value);
3260                         }
3261                 }
3262                 if (!islight)
3263                         continue;
3264                 if (lightscale <= 0)
3265                         lightscale = 1;
3266                 if (fadescale <= 0)
3267                         fadescale = 1;
3268                 if (color[0] == color[1] && color[0] == color[2])
3269                 {
3270                         color[0] *= overridecolor[0];
3271                         color[1] *= overridecolor[1];
3272                         color[2] *= overridecolor[2];
3273                 }
3274                 radius = light[3] * r_editlights_quakelightsizescale.value * lightscale / fadescale;
3275                 color[0] = color[0] * light[0];
3276                 color[1] = color[1] * light[1];
3277                 color[2] = color[2] * light[2];
3278                 switch (type)
3279                 {
3280                 case LIGHTTYPE_MINUSX:
3281                         break;
3282                 case LIGHTTYPE_RECIPX:
3283                         radius *= 2;
3284                         VectorScale(color, (1.0f / 16.0f), color);
3285                         break;
3286                 case LIGHTTYPE_RECIPXX:
3287                         radius *= 2;
3288                         VectorScale(color, (1.0f / 16.0f), color);
3289                         break;
3290                 default:
3291                 case LIGHTTYPE_NONE:
3292                         break;
3293                 case LIGHTTYPE_SUN:
3294                         break;
3295                 case LIGHTTYPE_MINUSXX:
3296                         break;
3297                 }
3298                 VectorAdd(origin, originhack, origin);
3299                 if (radius >= 1)
3300                         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);
3301         }
3302         if (entfiledata)
3303                 Mem_Free(entfiledata);
3304 }
3305
3306
3307 void R_Shadow_SetCursorLocationForView(void)
3308 {
3309         vec_t dist, push;
3310         vec3_t dest, endpos;
3311         trace_t trace;
3312         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
3313         trace = CL_TraceBox(r_vieworigin, vec3_origin, vec3_origin, dest, true, NULL, SUPERCONTENTS_SOLID, false);
3314         if (trace.fraction < 1)
3315         {
3316                 dist = trace.fraction * r_editlights_cursordistance.value;
3317                 push = r_editlights_cursorpushback.value;
3318                 if (push > dist)
3319                         push = dist;
3320                 push = -push;
3321                 VectorMA(trace.endpos, push, r_viewforward, endpos);
3322                 VectorMA(endpos, r_editlights_cursorpushoff.value, trace.plane.normal, endpos);
3323         }
3324         else
3325         {
3326                 VectorClear( endpos );
3327         }
3328         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3329         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3330         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
3331 }
3332
3333 void R_Shadow_UpdateWorldLightSelection(void)
3334 {
3335         if (r_editlights.integer)
3336         {
3337                 R_Shadow_SetCursorLocationForView();
3338                 R_Shadow_SelectLightInView();
3339                 R_Shadow_DrawLightSprites();
3340         }
3341         else
3342                 R_Shadow_SelectLight(NULL);
3343 }
3344
3345 void R_Shadow_EditLights_Clear_f(void)
3346 {
3347         R_Shadow_ClearWorldLights();
3348 }
3349
3350 void R_Shadow_EditLights_Reload_f(void)
3351 {
3352         if (!r_refdef.worldmodel)
3353                 return;
3354         strlcpy(r_shadow_mapname, r_refdef.worldmodel->name, sizeof(r_shadow_mapname));
3355         R_Shadow_ClearWorldLights();
3356         R_Shadow_LoadWorldLights();
3357         if (r_shadow_worldlightchain == NULL)
3358         {
3359                 R_Shadow_LoadLightsFile();
3360                 if (r_shadow_worldlightchain == NULL)
3361                         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3362         }
3363 }
3364
3365 void R_Shadow_EditLights_Save_f(void)
3366 {
3367         if (!r_refdef.worldmodel)
3368                 return;
3369         R_Shadow_SaveWorldLights();
3370 }
3371
3372 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
3373 {
3374         R_Shadow_ClearWorldLights();
3375         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
3376 }
3377
3378 void R_Shadow_EditLights_ImportLightsFile_f(void)
3379 {
3380         R_Shadow_ClearWorldLights();
3381         R_Shadow_LoadLightsFile();
3382 }
3383
3384 void R_Shadow_EditLights_Spawn_f(void)
3385 {
3386         vec3_t color;
3387         if (!r_editlights.integer)
3388         {
3389                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3390                 return;
3391         }
3392         if (Cmd_Argc() != 1)
3393         {
3394                 Con_Print("r_editlights_spawn does not take parameters\n");
3395                 return;
3396         }
3397         color[0] = color[1] = color[2] = 1;
3398         R_Shadow_UpdateWorldLight(R_Shadow_NewWorldLight(), r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL, 0.25, 0, 1, 1, LIGHTFLAG_REALTIMEMODE);
3399 }
3400
3401 void R_Shadow_EditLights_Edit_f(void)
3402 {
3403         vec3_t origin, angles, color;
3404         vec_t radius, corona, coronasizescale, ambientscale, diffusescale, specularscale;
3405         int style, shadows, flags, normalmode, realtimemode;
3406         char cubemapname[MAX_INPUTLINE];
3407         if (!r_editlights.integer)
3408         {
3409                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3410                 return;
3411         }
3412         if (!r_shadow_selectedlight)
3413         {
3414                 Con_Print("No selected light.\n");
3415                 return;
3416         }
3417         VectorCopy(r_shadow_selectedlight->origin, origin);
3418         VectorCopy(r_shadow_selectedlight->angles, angles);
3419         VectorCopy(r_shadow_selectedlight->color, color);
3420         radius = r_shadow_selectedlight->radius;
3421         style = r_shadow_selectedlight->style;
3422         if (r_shadow_selectedlight->cubemapname)
3423                 strlcpy(cubemapname, r_shadow_selectedlight->cubemapname, sizeof(cubemapname));
3424         else
3425                 cubemapname[0] = 0;
3426         shadows = r_shadow_selectedlight->shadow;
3427         corona = r_shadow_selectedlight->corona;
3428         coronasizescale = r_shadow_selectedlight->coronasizescale;
3429         ambientscale = r_shadow_selectedlight->ambientscale;
3430         diffusescale = r_shadow_selectedlight->diffusescale;
3431         specularscale = r_shadow_selectedlight->specularscale;
3432         flags = r_shadow_selectedlight->flags;
3433         normalmode = (flags & LIGHTFLAG_NORMALMODE) != 0;
3434         realtimemode = (flags & LIGHTFLAG_REALTIMEMODE) != 0;
3435         if (!strcmp(Cmd_Argv(1), "origin"))
3436         {
3437                 if (Cmd_Argc() != 5)
3438                 {
3439                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3440                         return;
3441                 }
3442                 origin[0] = atof(Cmd_Argv(2));
3443                 origin[1] = atof(Cmd_Argv(3));
3444                 origin[2] = atof(Cmd_Argv(4));
3445         }
3446         else if (!strcmp(Cmd_Argv(1), "originx"))
3447         {
3448                 if (Cmd_Argc() != 3)
3449                 {
3450                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3451                         return;
3452                 }
3453                 origin[0] = atof(Cmd_Argv(2));
3454         }
3455         else if (!strcmp(Cmd_Argv(1), "originy"))
3456         {
3457                 if (Cmd_Argc() != 3)
3458                 {
3459                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3460                         return;
3461                 }
3462                 origin[1] = atof(Cmd_Argv(2));
3463         }
3464         else if (!strcmp(Cmd_Argv(1), "originz"))
3465         {
3466                 if (Cmd_Argc() != 3)
3467                 {
3468                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3469                         return;
3470                 }
3471                 origin[2] = atof(Cmd_Argv(2));
3472         }
3473         else if (!strcmp(Cmd_Argv(1), "move"))
3474         {
3475                 if (Cmd_Argc() != 5)
3476                 {
3477                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3478                         return;
3479                 }
3480                 origin[0] += atof(Cmd_Argv(2));
3481                 origin[1] += atof(Cmd_Argv(3));
3482                 origin[2] += atof(Cmd_Argv(4));
3483         }
3484         else if (!strcmp(Cmd_Argv(1), "movex"))
3485         {
3486                 if (Cmd_Argc() != 3)
3487                 {
3488                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3489                         return;
3490                 }
3491                 origin[0] += atof(Cmd_Argv(2));
3492         }
3493         else if (!strcmp(Cmd_Argv(1), "movey"))
3494         {
3495                 if (Cmd_Argc() != 3)
3496                 {
3497                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3498                         return;
3499                 }
3500                 origin[1] += atof(Cmd_Argv(2));
3501         }
3502         else if (!strcmp(Cmd_Argv(1), "movez"))
3503         {
3504                 if (Cmd_Argc() != 3)
3505                 {
3506                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3507                         return;
3508                 }
3509                 origin[2] += atof(Cmd_Argv(2));
3510         }
3511         else if (!strcmp(Cmd_Argv(1), "angles"))
3512         {
3513                 if (Cmd_Argc() != 5)
3514                 {
3515                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3516                         return;
3517                 }
3518                 angles[0] = atof(Cmd_Argv(2));
3519                 angles[1] = atof(Cmd_Argv(3));
3520                 angles[2] = atof(Cmd_Argv(4));
3521         }
3522         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3523         {
3524                 if (Cmd_Argc() != 3)
3525                 {
3526                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3527                         return;
3528                 }
3529                 angles[0] = atof(Cmd_Argv(2));
3530         }
3531         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3532         {
3533                 if (Cmd_Argc() != 3)
3534                 {
3535                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3536                         return;
3537                 }
3538                 angles[1] = atof(Cmd_Argv(2));
3539         }
3540         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3541         {
3542                 if (Cmd_Argc() != 3)
3543                 {
3544                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3545                         return;
3546                 }
3547                 angles[2] = atof(Cmd_Argv(2));
3548         }
3549         else if (!strcmp(Cmd_Argv(1), "color"))
3550         {
3551                 if (Cmd_Argc() != 5)
3552                 {
3553                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3554                         return;
3555                 }
3556                 color[0] = atof(Cmd_Argv(2));
3557                 color[1] = atof(Cmd_Argv(3));
3558                 color[2] = atof(Cmd_Argv(4));
3559         }
3560         else if (!strcmp(Cmd_Argv(1), "radius"))
3561         {
3562                 if (Cmd_Argc() != 3)
3563                 {
3564                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3565                         return;
3566                 }
3567                 radius = atof(Cmd_Argv(2));
3568         }
3569         else if (!strcmp(Cmd_Argv(1), "colorscale"))
3570         {
3571                 if (Cmd_Argc() == 3)
3572                 {
3573                         double scale = atof(Cmd_Argv(2));
3574                         color[0] *= scale;
3575                         color[1] *= scale;
3576                         color[2] *= scale;
3577                 }
3578                 else
3579                 {
3580                         if (Cmd_Argc() != 5)
3581                         {
3582                                 Con_Printf("usage: r_editlights_edit %s red green blue  (OR grey instead of red green blue)\n", Cmd_Argv(1));
3583                                 return;
3584                         }
3585                         color[0] *= atof(Cmd_Argv(2));
3586                         color[1] *= atof(Cmd_Argv(3));
3587                         color[2] *= atof(Cmd_Argv(4));
3588                 }
3589         }
3590         else if (!strcmp(Cmd_Argv(1), "radiusscale") || !strcmp(Cmd_Argv(1), "sizescale"))
3591         {
3592                 if (Cmd_Argc() != 3)
3593                 {
3594                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3595                         return;
3596                 }
3597                 radius *= atof(Cmd_Argv(2));
3598         }
3599         else if (!strcmp(Cmd_Argv(1), "style"))
3600         {
3601                 if (Cmd_Argc() != 3)
3602                 {
3603                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3604                         return;
3605                 }
3606                 style = atoi(Cmd_Argv(2));
3607         }
3608         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3609         {
3610                 if (Cmd_Argc() > 3)
3611                 {
3612                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3613                         return;
3614                 }
3615                 if (Cmd_Argc() == 3)
3616                         strcpy(cubemapname, Cmd_Argv(2));
3617                 else
3618                         cubemapname[0] = 0;
3619         }
3620         else if (!strcmp(Cmd_Argv(1), "shadows"))
3621         {
3622                 if (Cmd_Argc() != 3)
3623                 {
3624                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3625                         return;
3626                 }
3627                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3628         }
3629         else if (!strcmp(Cmd_Argv(1), "corona"))
3630         {
3631                 if (Cmd_Argc() != 3)
3632                 {
3633                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3634                         return;
3635                 }
3636                 corona = atof(Cmd_Argv(2));
3637         }
3638         else if (!strcmp(Cmd_Argv(1), "coronasize"))
3639         {
3640                 if (Cmd_Argc() != 3)
3641                 {
3642                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3643                         return;
3644                 }
3645                 coronasizescale = atof(Cmd_Argv(2));
3646         }
3647         else if (!strcmp(Cmd_Argv(1), "ambient"))
3648         {
3649                 if (Cmd_Argc() != 3)
3650                 {
3651                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3652                         return;
3653                 }
3654                 ambientscale = atof(Cmd_Argv(2));
3655         }
3656         else if (!strcmp(Cmd_Argv(1), "diffuse"))
3657         {
3658                 if (Cmd_Argc() != 3)
3659                 {
3660                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3661                         return;
3662                 }
3663                 diffusescale = atof(Cmd_Argv(2));
3664         }
3665         else if (!strcmp(Cmd_Argv(1), "specular"))
3666         {
3667                 if (Cmd_Argc() != 3)
3668                 {
3669                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3670                         return;
3671                 }
3672                 specularscale = atof(Cmd_Argv(2));
3673         }
3674         else if (!strcmp(Cmd_Argv(1), "normalmode"))
3675         {
3676                 if (Cmd_Argc() != 3)
3677                 {
3678                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3679                         return;
3680                 }
3681                 normalmode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3682         }
3683         else if (!strcmp(Cmd_Argv(1), "realtimemode"))
3684         {
3685                 if (Cmd_Argc() != 3)
3686                 {
3687                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3688                         return;
3689                 }
3690                 realtimemode = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3691         }
3692         else
3693         {
3694                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3695                 Con_Print("Selected light's properties:\n");
3696                 Con_Printf("Origin       : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3697                 Con_Printf("Angles       : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3698                 Con_Printf("Color        : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3699                 Con_Printf("Radius       : %f\n", r_shadow_selectedlight->radius);
3700                 Con_Printf("Corona       : %f\n", r_shadow_selectedlight->corona);
3701                 Con_Printf("Style        : %i\n", r_shadow_selectedlight->style);
3702                 Con_Printf("Shadows      : %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3703                 Con_Printf("Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);
3704                 Con_Printf("CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);
3705                 Con_Printf("Ambient      : %f\n", r_shadow_selectedlight->ambientscale);
3706                 Con_Printf("Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);
3707                 Con_Printf("Specular     : %f\n", r_shadow_selectedlight->specularscale);
3708                 Con_Printf("NormalMode   : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_NORMALMODE) ? "yes" : "no");
3709                 Con_Printf("RealTimeMode : %s\n", (r_shadow_selectedlight->flags & LIGHTFLAG_REALTIMEMODE) ? "yes" : "no");
3710                 return;
3711         }
3712         flags = (normalmode ? LIGHTFLAG_NORMALMODE : 0) | (realtimemode ? LIGHTFLAG_REALTIMEMODE : 0);
3713         R_Shadow_UpdateWorldLight(r_shadow_selectedlight, origin, angles, color, radius, corona, style, shadows, cubemapname, coronasizescale, ambientscale, diffusescale, specularscale, flags);
3714 }
3715
3716 void R_Shadow_EditLights_EditAll_f(void)
3717 {
3718         dlight_t *light;
3719
3720         if (!r_editlights.integer)
3721         {
3722                 Con_Print("Cannot edit lights when not in editing mode. Set r_editlights to 1.\n");
3723                 return;
3724         }
3725
3726         for (light = r_shadow_worldlightchain;light;light = light->next)
3727         {
3728                 R_Shadow_SelectLight(light);
3729                 R_Shadow_EditLights_Edit_f();
3730         }
3731 }
3732
3733 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3734 {
3735         int lightnumber, lightcount;
3736         dlight_t *light;
3737         float x, y;
3738         char temp[256];
3739         if (!r_editlights.integer)
3740                 return;
3741         x = 0;
3742         y = con_vislines;
3743         lightnumber = -1;
3744         lightcount = 0;
3745         for (lightcount = 0, light = r_shadow_worldlightchain;light;lightcount++, light = light->next)
3746                 if (light == r_shadow_selectedlight)
3747                         lightnumber = lightcount;
3748         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;
3749         if (r_shadow_selectedlight == NULL)
3750                 return;
3751         sprintf(temp, "Light #%i properties", lightnumber);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3752         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;
3753         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;
3754         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;
3755         sprintf(temp, "Radius       : %f\n", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3756         sprintf(temp, "Corona       : %f\n", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3757         sprintf(temp, "Style        : %i\n", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3758         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;
3759         sprintf(temp, "Cubemap      : %s\n", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3760         sprintf(temp, "CoronaSize   : %f\n", r_shadow_selectedlight->coronasizescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3761         sprintf(temp, "Ambient      : %f\n", r_shadow_selectedlight->ambientscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3762         sprintf(temp, "Diffuse      : %f\n", r_shadow_selectedlight->diffusescale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3763         sprintf(temp, "Specular     : %f\n", r_shadow_selectedlight->specularscale);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3764         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;
3765         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;
3766 }
3767
3768 void R_Shadow_EditLights_ToggleShadow_f(void)
3769 {
3770         if (!r_editlights.integer)
3771         {
3772                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3773                 return;
3774         }
3775         if (!r_shadow_selectedlight)
3776         {
3777                 Con_Print("No selected light.\n");
3778                 return;
3779         }
3780         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);
3781 }
3782
3783 void R_Shadow_EditLights_ToggleCorona_f(void)
3784 {
3785         if (!r_editlights.integer)
3786         {
3787                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3788                 return;
3789         }
3790         if (!r_shadow_selectedlight)
3791         {
3792                 Con_Print("No selected light.\n");
3793                 return;
3794         }
3795         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);
3796 }
3797
3798 void R_Shadow_EditLights_Remove_f(void)
3799 {
3800         if (!r_editlights.integer)
3801         {
3802                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3803                 return;
3804         }
3805         if (!r_shadow_selectedlight)
3806         {
3807                 Con_Print("No selected light.\n");
3808                 return;
3809         }
3810         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3811         r_shadow_selectedlight = NULL;
3812 }
3813
3814 void R_Shadow_EditLights_Help_f(void)
3815 {
3816         Con_Print(
3817 "Documentation on r_editlights system:\n"
3818 "Settings:\n"
3819 "r_editlights : enable/disable editing mode\n"
3820 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3821 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3822 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3823 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3824 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3825 "Commands:\n"
3826 "r_editlights_help : this help\n"
3827 "r_editlights_clear : remove all lights\n"
3828 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3829 "r_editlights_save : save to .rtlights file\n"
3830 "r_editlights_spawn : create a light with default settings\n"
3831 "r_editlights_edit command : edit selected light - more documentation below\n"
3832 "r_editlights_remove : remove selected light\n"
3833 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3834 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3835 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3836 "Edit commands:\n"
3837 "origin x y z : set light location\n"
3838 "originx x: set x component of light location\n"
3839 "originy y: set y component of light location\n"
3840 "originz z: set z component of light location\n"
3841 "move x y z : adjust light location\n"
3842 "movex x: adjust x component of light location\n"
3843 "movey y: adjust y component of light location\n"
3844 "movez z: adjust z component of light location\n"
3845 "angles x y z : set light angles\n"
3846 "anglesx x: set x component of light angles\n"
3847 "anglesy y: set y component of light angles\n"
3848 "anglesz z: set z component of light angles\n"
3849 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3850 "radius radius : set radius (size) of light\n"
3851 "colorscale grey : multiply color of light (1 does nothing)\n"
3852 "colorscale r g b : multiply color of light (1 1 1 does nothing)\n"
3853 "radiusscale scale : multiply radius (size) of light (1 does nothing)\n"
3854 "sizescale scale : multiply radius (size) of light (1 does nothing)\n"
3855 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3856 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3857 "shadows 1/0 : turn on/off shadows\n"
3858 "corona n : set corona intensity\n"
3859 "coronasize n : set corona size (0-1)\n"
3860 "ambient n : set ambient intensity (0-1)\n"
3861 "diffuse n : set diffuse intensity (0-1)\n"
3862 "specular n : set specular intensity (0-1)\n"
3863 "normalmode 1/0 : turn on/off rendering of this light in rtworld 0 mode\n"
3864 "realtimemode 1/0 : turn on/off rendering of this light in rtworld 1 mode\n"
3865 "<nothing> : print light properties to console\n"
3866         );
3867 }
3868
3869 void R_Shadow_EditLights_CopyInfo_f(void)
3870 {
3871         if (!r_editlights.integer)
3872         {
3873                 Con_Print("Cannot copy light info when not in editing mode.  Set r_editlights to 1.\n");
3874                 return;
3875         }
3876         if (!r_shadow_selectedlight)
3877         {
3878                 Con_Print("No selected light.\n");
3879                 return;
3880         }
3881         VectorCopy(r_shadow_selectedlight->angles, r_shadow_bufferlight.angles);
3882         VectorCopy(r_shadow_selectedlight->color, r_shadow_bufferlight.color);
3883         r_shadow_bufferlight.radius = r_shadow_selectedlight->radius;
3884         r_shadow_bufferlight.style = r_shadow_selectedlight->style;
3885         if (r_shadow_selectedlight->cubemapname)
3886                 strcpy(r_shadow_bufferlight.cubemapname, r_shadow_selectedlight->cubemapname);
3887         else
3888                 r_shadow_bufferlight.cubemapname[0] = 0;
3889         r_shadow_bufferlight.shadow = r_shadow_selectedlight->shadow;
3890         r_shadow_bufferlight.corona = r_shadow_selectedlight->corona;
3891         r_shadow_bufferlight.coronasizescale = r_shadow_selectedlight->coronasizescale;
3892         r_shadow_bufferlight.ambientscale = r_shadow_selectedlight->ambientscale;
3893         r_shadow_bufferlight.diffusescale = r_shadow_selectedlight->diffusescale;
3894         r_shadow_bufferlight.specularscale = r_shadow_selectedlight->specularscale;
3895         r_shadow_bufferlight.flags = r_shadow_selectedlight->flags;
3896 }
3897
3898 void R_Shadow_EditLights_PasteInfo_f(void)
3899 {
3900         if (!r_editlights.integer)
3901         {
3902                 Con_Print("Cannot paste light info when not in editing mode.  Set r_editlights to 1.\n");
3903                 return;
3904         }
3905         if (!r_shadow_selectedlight)
3906         {
3907                 Con_Print("No selected light.\n");
3908                 return;
3909         }
3910         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);
3911 }
3912
3913 void R_Shadow_EditLights_Init(void)
3914 {
3915         Cvar_RegisterVariable(&r_editlights);
3916         Cvar_RegisterVariable(&r_editlights_cursordistance);
3917         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3918         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3919         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3920         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3921         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f, "prints documentation on console commands and variables in rtlight editing system");
3922         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f, "removes all world lights (let there be darkness!)");
3923         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)");
3924         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f, "save .rtlights file for current level");
3925         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f, "creates a light with default properties (let there be light!)");
3926         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f, "changes a property on the selected light");
3927         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)");
3928         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f, "remove selected light");
3929         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f, "toggle on/off the shadow option on the selected light");
3930         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f, "toggle on/off the corona option on the selected light");
3931         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f, "load lights from .ent file or map entities (ignoring .rtlights or .lights file)");
3932         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f, "load lights from .lights file (ignoring .rtlights or .ent files and map entities)");
3933         Cmd_AddCommand("r_editlights_copyinfo", R_Shadow_EditLights_CopyInfo_f, "store a copy of all properties (except origin) of the selected light");
3934         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)");
3935 }
3936