]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
bsp model shadows are now enlarged by r_shadow_projectdistance (to cure problems...
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it), and to address
18 Creative's patent on this sort of technology we also draw the frontfaces
19 first, and backfaces second (decrement, increment).
20
21 Patent warning:
22 This algorithm may be covered by Creative's patent (US Patent #6384822)
23 on Carmack's Reverse paper (which I have not read), however that patent
24 seems to be about drawing a stencil shadow from a model in an otherwise
25 unshadowed scene, where as realtime lighting technology draws light where
26 shadows do not lie.
27
28
29
30 Terminology: Stencil Light Volume (sometimes called Light Volumes)
31 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
32 areas in shadow it contanis the areas in light, this can only be built
33 quickly for certain limited cases (such as portal visibility from a point),
34 but is quite useful for some effects (sunlight coming from sky polygons is
35 one possible example, translucent occluders is another example).
36
37
38
39 Terminology: Optimized Stencil Shadow Volume
40 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
41 no duplicate coverage of areas (no need to shadow an area twice), often this
42 greatly improves performance but is an operation too costly to use on moving
43 lights (however completely optimal Stencil Light Volumes can be constructed
44 in some ideal cases).
45
46
47
48 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
49 Per pixel evaluation of lighting equations, at a bare minimum this involves
50 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
51 vector and surface normal, using a texture of the surface bumps, called a
52 NormalMap) if supported by hardware; in our case there is support for cards
53 which are incapable of DOT3, the quality is quite poor however.  Additionally
54 it is desirable to have specular evaluation per pixel, per vertex
55 normalization of specular halfangle vectors causes noticable distortion but
56 is unavoidable on hardware without GL_ARB_fragment_program.
57
58
59
60 Terminology: Normalization CubeMap
61 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
62 encoded as RGB colors) for any possible direction, this technique allows per
63 pixel calculation of incidence vector for per pixel lighting purposes, which
64 would not otherwise be possible per pixel without GL_ARB_fragment_program.
65
66
67
68 Terminology: 2D Attenuation Texturing
69 A very crude approximation of light attenuation with distance which results
70 in cylindrical light shapes which fade vertically as a streak (some games
71 such as Doom3 allow this to be rotated to be less noticable in specific
72 cases), the technique is simply modulating lighting by two 2D textures (which
73 can be the same) on different axes of projection (XY and Z, typically), this
74 is the best technique available without 3D Attenuation Texturing or
75 GL_ARB_fragment_program technology.
76
77
78
79 Terminology: 3D Attenuation Texturing
80 A slightly crude approximation of light attenuation with distance, its flaws
81 are limited radius and resolution (performance tradeoffs).
82
83
84
85 Terminology: 3D Attenuation-Normalization Texturing
86 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
87 vectors shorter the lighting becomes darker, a very effective optimization of
88 diffuse lighting if 3D Attenuation Textures are already used.
89
90
91
92 Terminology: Light Cubemap Filtering
93 A technique for modeling non-uniform light distribution according to
94 direction, for example projecting a stained glass window image onto a wall,
95 this is done by texturing the lighting with a cubemap.
96
97
98
99 Terminology: Light Projection Filtering
100 A technique for modeling shadowing of light passing through translucent
101 surfaces, allowing stained glass windows and other effects to be done more
102 elegantly than possible with Light Cubemap Filtering by applying an occluder
103 texture to the lighting combined with a stencil light volume to limit the lit
104 area (this allows evaluating multiple translucent occluders in a scene).
105
106
107
108 Terminology: Doom3 Lighting
109 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
110 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
111 the (currently upcoming) game Doom3.
112 */
113
114 #include "quakedef.h"
115 #include "r_shadow.h"
116 #include "cl_collision.h"
117 #include "portals.h"
118 #include "image.h"
119
120 extern void R_Shadow_EditLights_Init(void);
121
122 #define SHADOWSTAGE_NONE 0
123 #define SHADOWSTAGE_STENCIL 1
124 #define SHADOWSTAGE_LIGHT 2
125 #define SHADOWSTAGE_STENCILTWOSIDE 3
126
127 int r_shadowstage = SHADOWSTAGE_NONE;
128 int r_shadow_reloadlights = false;
129
130 mempool_t *r_shadow_mempool;
131
132 int maxshadowelements;
133 int *shadowelements;
134
135 int maxshadowmark;
136 int numshadowmark;
137 int *shadowmark;
138 int *shadowmarklist;
139 int shadowmarkcount;
140
141 int maxvertexupdate;
142 int *vertexupdate;
143 int *vertexremap;
144 int vertexupdatenum;
145
146 int r_shadow_buffer_numclusterpvsbytes;
147 qbyte *r_shadow_buffer_clusterpvs;
148 int *r_shadow_buffer_clusterlist;
149
150 int r_shadow_buffer_numsurfacepvsbytes;
151 qbyte *r_shadow_buffer_surfacepvs;
152 int *r_shadow_buffer_surfacelist;
153
154 rtexturepool_t *r_shadow_texturepool;
155 rtexture_t *r_shadow_normalcubetexture;
156 rtexture_t *r_shadow_attenuation2dtexture;
157 rtexture_t *r_shadow_attenuation3dtexture;
158 rtexture_t *r_shadow_blankbumptexture;
159 rtexture_t *r_shadow_blankglosstexture;
160 rtexture_t *r_shadow_blankwhitetexture;
161
162 // used only for light filters (cubemaps)
163 rtexturepool_t *r_shadow_filters_texturepool;
164
165 cvar_t r_shadow_realtime_world_lightmaps = {0, "r_shadow_realtime_world_lightmaps", "0"};
166 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
167 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
168 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
169 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
170 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
171 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
172 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
173 cvar_t r_shadow_glossintensity = {0, "r_shadow_glossintensity", "1"};
174 cvar_t r_shadow_gloss2intensity = {0, "r_shadow_gloss2intensity", "0.25"};
175 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
176 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
177 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
178 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
179 cvar_t r_shadow_polygonfactor = {0, "r_shadow_polygonfactor", "0"};
180 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "1"};
181 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
182 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "1000000"};
183 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
184 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
185 cvar_t r_shadow_worldshadows = {0, "r_shadow_worldshadows", "1"};
186 cvar_t r_shadow_dlightshadows = {CVAR_SAVE, "r_shadow_dlightshadows", "1"};
187 cvar_t r_shadow_staticworldlights = {0, "r_shadow_staticworldlights", "1"};
188 cvar_t r_shadow_cull = {0, "r_shadow_cull", "1"};
189 cvar_t gl_ext_stenciltwoside = {0, "gl_ext_stenciltwoside", "1"};
190
191 int c_rt_lights, c_rt_clears, c_rt_scissored;
192 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
193 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
194
195 void R_Shadow_ClearWorldLights(void);
196 void R_Shadow_SaveWorldLights(void);
197 void R_Shadow_LoadWorldLights(void);
198 void R_Shadow_LoadLightsFile(void);
199 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
200
201 void r_shadow_start(void)
202 {
203         // allocate vertex processing arrays
204         r_shadow_mempool = Mem_AllocPool("R_Shadow");
205         maxshadowelements = 0;
206         shadowelements = NULL;
207         maxvertexupdate = 0;
208         vertexupdate = NULL;
209         vertexremap = NULL;
210         vertexupdatenum = 0;
211         maxshadowmark = 0;
212         numshadowmark = 0;
213         shadowmark = NULL;
214         shadowmarklist = NULL;
215         shadowmarkcount = 0;
216         r_shadow_buffer_numclusterpvsbytes = 0;
217         r_shadow_buffer_clusterpvs = NULL;
218         r_shadow_buffer_clusterlist = NULL;
219         r_shadow_buffer_numsurfacepvsbytes = 0;
220         r_shadow_buffer_surfacepvs = NULL;
221         r_shadow_buffer_surfacelist = NULL;
222         r_shadow_normalcubetexture = NULL;
223         r_shadow_attenuation2dtexture = NULL;
224         r_shadow_attenuation3dtexture = NULL;
225         r_shadow_blankbumptexture = NULL;
226         r_shadow_blankglosstexture = NULL;
227         r_shadow_blankwhitetexture = NULL;
228         r_shadow_texturepool = NULL;
229         r_shadow_filters_texturepool = NULL;
230         R_Shadow_ClearWorldLights();
231         r_shadow_reloadlights = true;
232 }
233
234 void r_shadow_shutdown(void)
235 {
236         R_Shadow_ClearWorldLights();
237         r_shadow_reloadlights = true;
238         r_shadow_normalcubetexture = NULL;
239         r_shadow_attenuation2dtexture = NULL;
240         r_shadow_attenuation3dtexture = NULL;
241         r_shadow_blankbumptexture = NULL;
242         r_shadow_blankglosstexture = NULL;
243         r_shadow_blankwhitetexture = NULL;
244         R_FreeTexturePool(&r_shadow_texturepool);
245         R_FreeTexturePool(&r_shadow_filters_texturepool);
246         maxshadowelements = 0;
247         shadowelements = NULL;
248         maxvertexupdate = 0;
249         vertexupdate = NULL;
250         vertexremap = NULL;
251         vertexupdatenum = 0;
252         maxshadowmark = 0;
253         numshadowmark = 0;
254         shadowmark = NULL;
255         shadowmarklist = NULL;
256         shadowmarkcount = 0;
257         r_shadow_buffer_numclusterpvsbytes = 0;
258         r_shadow_buffer_clusterpvs = NULL;
259         r_shadow_buffer_clusterlist = NULL;
260         r_shadow_buffer_numsurfacepvsbytes = 0;
261         r_shadow_buffer_surfacepvs = NULL;
262         r_shadow_buffer_surfacelist = NULL;
263         Mem_FreePool(&r_shadow_mempool);
264 }
265
266 void r_shadow_newmap(void)
267 {
268         R_Shadow_ClearWorldLights();
269         r_shadow_reloadlights = true;
270 }
271
272 void R_Shadow_Help_f(void)
273 {
274         Con_Printf(
275 "Documentation on r_shadow system:\n"
276 "Settings:\n"
277 "r_shadow_lightattenuationpower : used to generate attenuation texture\n"
278 "r_shadow_lightattenuationscale : used to generate attenuation texture\n"
279 "r_shadow_lightintensityscale : scale rendering brightness of all lights\n"
280 "r_shadow_realtime_world : use realtime world light rendering\n"
281 "r_shadow_realtime_dlight : use high quality dlight rendering\n"
282 "r_shadow_realtime_world_lightmaps : use lightmaps in addition to rtlights\n"
283 "r_shadow_visiblevolumes : useful for performance testing; bright = slow!\n"
284 "r_shadow_gloss 0/1/2 : no gloss, gloss textures only, force gloss\n"
285 "r_shadow_glossintensity : brightness of textured gloss\n"
286 "r_shadow_gloss2intensity : brightness of forced gloss\n"
287 "r_shadow_debuglight : render only this light number (-1 = all)\n"
288 "r_shadow_scissor : use scissor optimization\n"
289 "r_shadow_bumpscale_bumpmap : depth scale for bumpmap conversion\n"
290 "r_shadow_bumpscale_basetexture : base texture as bumpmap with this scale\n"
291 "r_shadow_polygonfactor : nudge shadow volumes closer/further\n"
292 "r_shadow_polygonoffset : nudge shadow volumes closer/further\n"
293 "r_shadow_portallight : use portal visibility for static light precomputation\n"
294 "r_shadow_projectdistance : shadow volume projection distance\n"
295 "r_shadow_texture3d : use 3d attenuation texture (if hardware supports)\n"
296 "r_shadow_singlepassvolumegeneration : selects shadow volume algorithm\n"
297 "r_shadow_worldshadows : enable world shadows\n"
298 "r_shadow_dlightshadows : enable dlight shadows\n"
299 "Commands:\n"
300 "r_shadow_help : this help\n"
301         );
302 }
303
304 void R_Shadow_Init(void)
305 {
306         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
307         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
308         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
309         Cvar_RegisterVariable(&r_shadow_realtime_world);
310         Cvar_RegisterVariable(&r_shadow_realtime_world_lightmaps);
311         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
312         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
313         Cvar_RegisterVariable(&r_shadow_gloss);
314         Cvar_RegisterVariable(&r_shadow_glossintensity);
315         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
316         Cvar_RegisterVariable(&r_shadow_debuglight);
317         Cvar_RegisterVariable(&r_shadow_scissor);
318         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
319         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
320         Cvar_RegisterVariable(&r_shadow_polygonfactor);
321         Cvar_RegisterVariable(&r_shadow_polygonoffset);
322         Cvar_RegisterVariable(&r_shadow_portallight);
323         Cvar_RegisterVariable(&r_shadow_projectdistance);
324         Cvar_RegisterVariable(&r_shadow_texture3d);
325         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
326         Cvar_RegisterVariable(&r_shadow_worldshadows);
327         Cvar_RegisterVariable(&r_shadow_dlightshadows);
328         Cvar_RegisterVariable(&r_shadow_staticworldlights);
329         Cvar_RegisterVariable(&r_shadow_cull);
330         Cvar_RegisterVariable(&gl_ext_stenciltwoside);
331         if (gamemode == GAME_TENEBRAE)
332         {
333                 Cvar_SetValue("r_shadow_gloss", 2);
334                 Cvar_SetValue("r_shadow_bumpscale_basetexture", 4);
335         }
336         Cmd_AddCommand("r_shadow_help", R_Shadow_Help_f);
337         R_Shadow_EditLights_Init();
338         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
339 }
340
341 matrix4x4_t matrix_attenuationxyz =
342 {
343         {
344                 {0.5, 0.0, 0.0, 0.5},
345                 {0.0, 0.5, 0.0, 0.5},
346                 {0.0, 0.0, 0.5, 0.5},
347                 {0.0, 0.0, 0.0, 1.0}
348         }
349 };
350
351 matrix4x4_t matrix_attenuationz =
352 {
353         {
354                 {0.0, 0.0, 0.5, 0.5},
355                 {0.0, 0.0, 0.0, 0.5},
356                 {0.0, 0.0, 0.0, 0.5},
357                 {0.0, 0.0, 0.0, 1.0}
358         }
359 };
360
361 int *R_Shadow_ResizeShadowElements(int numtris)
362 {
363         // make sure shadowelements is big enough for this volume
364         if (maxshadowelements < numtris * 24)
365         {
366                 maxshadowelements = numtris * 24;
367                 if (shadowelements)
368                         Mem_Free(shadowelements);
369                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
370         }
371         return shadowelements;
372 }
373
374 void R_Shadow_EnlargeClusterBuffer(int numclusters)
375 {
376         int numclusterpvsbytes = (((numclusters + 7) >> 3) + 255) & ~255;
377         if (r_shadow_buffer_numclusterpvsbytes < numclusterpvsbytes)
378         {
379                 if (r_shadow_buffer_clusterpvs)
380                         Mem_Free(r_shadow_buffer_clusterpvs);
381                 if (r_shadow_buffer_clusterlist)
382                         Mem_Free(r_shadow_buffer_clusterlist);
383                 r_shadow_buffer_numclusterpvsbytes = numclusterpvsbytes;
384                 r_shadow_buffer_clusterpvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes);
385                 r_shadow_buffer_clusterlist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numclusterpvsbytes * 8 * sizeof(*r_shadow_buffer_clusterlist));
386         }
387 }
388
389 void R_Shadow_EnlargeSurfaceBuffer(int numsurfaces)
390 {
391         int numsurfacepvsbytes = (((numsurfaces + 7) >> 3) + 255) & ~255;
392         if (r_shadow_buffer_numsurfacepvsbytes < numsurfacepvsbytes)
393         {
394                 if (r_shadow_buffer_surfacepvs)
395                         Mem_Free(r_shadow_buffer_surfacepvs);
396                 if (r_shadow_buffer_surfacelist)
397                         Mem_Free(r_shadow_buffer_surfacelist);
398                 r_shadow_buffer_numsurfacepvsbytes = numsurfacepvsbytes;
399                 r_shadow_buffer_surfacepvs = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes);
400                 r_shadow_buffer_surfacelist = Mem_Alloc(r_shadow_mempool, r_shadow_buffer_numsurfacepvsbytes * 8 * sizeof(*r_shadow_buffer_surfacelist));
401         }
402 }
403
404 void R_Shadow_PrepareShadowMark(int numtris)
405 {
406         // make sure shadowmark is big enough for this volume
407         if (maxshadowmark < numtris)
408         {
409                 maxshadowmark = numtris;
410                 if (shadowmark)
411                         Mem_Free(shadowmark);
412                 if (shadowmarklist)
413                         Mem_Free(shadowmarklist);
414                 shadowmark = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmark));
415                 shadowmarklist = Mem_Alloc(r_shadow_mempool, maxshadowmark * sizeof(*shadowmarklist));
416                 shadowmarkcount = 0;
417         }
418         shadowmarkcount++;
419         // if shadowmarkcount wrapped we clear the array and adjust accordingly
420         if (shadowmarkcount == 0)
421         {
422                 shadowmarkcount = 1;
423                 memset(shadowmark, 0, maxshadowmark * sizeof(*shadowmark));
424         }
425         numshadowmark = 0;
426 }
427
428 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)
429 {
430         int i, j, tris = 0, vr[3], t, outvertices = 0;
431         const int *e, *n;
432         float f, temp[3];
433
434         if (maxvertexupdate < innumvertices)
435         {
436                 maxvertexupdate = innumvertices;
437                 if (vertexupdate)
438                         Mem_Free(vertexupdate);
439                 if (vertexremap)
440                         Mem_Free(vertexremap);
441                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
442                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
443                 vertexupdatenum = 0;
444         }
445         vertexupdatenum++;
446         if (vertexupdatenum == 0)
447         {
448                 vertexupdatenum = 1;
449                 memset(vertexupdate, 0, maxvertexupdate * sizeof(int));
450                 memset(vertexremap, 0, maxvertexupdate * sizeof(int));
451         }
452         
453         for (i = 0;i < numshadowmarktris;i++)
454         {
455                 t = shadowmarktris[i];
456                 shadowmark[t] = shadowmarkcount;
457                 e = inelement3i + t * 3;
458                 // make sure the vertices are created
459                 for (j = 0;j < 3;j++)
460                 {
461                         if (vertexupdate[e[j]] != vertexupdatenum)
462                         {
463                                 vertexupdate[e[j]] = vertexupdatenum;
464                                 vertexremap[e[j]] = outvertices;
465                                 VectorSubtract(invertex3f + e[j] * 3, projectorigin, temp);
466                                 f = projectdistance / VectorLength(temp);
467                                 VectorCopy(invertex3f + e[j] * 3, outvertex3f);
468                                 VectorMA(projectorigin, f, temp, (outvertex3f + 3));
469                                 outvertex3f += 6;
470                                 outvertices += 2;
471                         }
472                 }
473                 // output the front and back triangles
474                 outelement3i[0] = vertexremap[e[0]];
475                 outelement3i[1] = vertexremap[e[1]];
476                 outelement3i[2] = vertexremap[e[2]];
477                 outelement3i[3] = vertexremap[e[2]] + 1;
478                 outelement3i[4] = vertexremap[e[1]] + 1;
479                 outelement3i[5] = vertexremap[e[0]] + 1;
480                 outelement3i += 6;
481                 tris += 2;
482         }
483
484         for (i = 0;i < numshadowmarktris;i++)
485         {
486                 t = shadowmarktris[i];
487                 e = inelement3i + t * 3;
488                 n = inneighbor3i + t * 3;
489                 // output the sides (facing outward from this triangle)
490                 if (shadowmark[n[0]] != shadowmarkcount)
491                 {
492                         vr[0] = vertexremap[e[0]];
493                         vr[1] = vertexremap[e[1]];
494                         outelement3i[0] = vr[1];
495                         outelement3i[1] = vr[0];
496                         outelement3i[2] = vr[0] + 1;
497                         outelement3i[3] = vr[1];
498                         outelement3i[4] = vr[0] + 1;
499                         outelement3i[5] = vr[1] + 1;
500                         outelement3i += 6;
501                         tris += 2;
502                 }
503                 if (shadowmark[n[1]] != shadowmarkcount)
504                 {
505                         vr[1] = vertexremap[e[1]];
506                         vr[2] = vertexremap[e[2]];
507                         outelement3i[0] = vr[2];
508                         outelement3i[1] = vr[1];
509                         outelement3i[2] = vr[1] + 1;
510                         outelement3i[3] = vr[2];
511                         outelement3i[4] = vr[1] + 1;
512                         outelement3i[5] = vr[2] + 1;
513                         outelement3i += 6;
514                         tris += 2;
515                 }
516                 if (shadowmark[n[2]] != shadowmarkcount)
517                 {
518                         vr[0] = vertexremap[e[0]];
519                         vr[2] = vertexremap[e[2]];
520                         outelement3i[0] = vr[0];
521                         outelement3i[1] = vr[2];
522                         outelement3i[2] = vr[2] + 1;
523                         outelement3i[3] = vr[0];
524                         outelement3i[4] = vr[2] + 1;
525                         outelement3i[5] = vr[0] + 1;
526                         outelement3i += 6;
527                         tris += 2;
528                 }
529         }
530         if (outnumvertices)
531                 *outnumvertices = outvertices;
532         return tris;
533 }
534
535 float varray_vertex3f2[65536*3];
536
537 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)
538 {
539         int tris, outverts;
540         if (projectdistance < 0.1)
541         {
542                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
543                 return;
544         }
545         if (!numverts || !nummarktris)
546                 return;
547         // make sure shadowelements is big enough for this volume
548         if (maxshadowelements < nummarktris * 24)
549                 R_Shadow_ResizeShadowElements((nummarktris + 256) * 24);
550         tris = R_Shadow_ConstructShadowVolume(numverts, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, projectorigin, projectdistance, nummarktris, marktris);
551         R_Shadow_RenderVolume(outverts, tris, varray_vertex3f2, shadowelements);
552 }
553
554 void R_Shadow_VolumeFromBox(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, const vec3_t mins, const vec3_t maxs)
555 {
556         int i;
557         const float *v[3];
558
559         // check which triangles are facing the , and then output
560         // triangle elements and vertices...  by clever use of elements we
561         // can construct the whole shadow from the unprojected vertices and
562         // the projected vertices
563
564         // identify lit faces within the bounding box
565         R_Shadow_PrepareShadowMark(numtris);
566         for (i = 0;i < numtris;i++)
567         {
568                 v[0] = invertex3f + elements[i*3+0] * 3;
569                 v[1] = invertex3f + elements[i*3+1] * 3;
570                 v[2] = invertex3f + elements[i*3+2] * 3;
571                 if (PointInfrontOfTriangle(projectorigin, v[0], v[1], v[2]) && maxs[0] > min(v[0][0], min(v[1][0], v[2][0])) && mins[0] < max(v[0][0], max(v[1][0], v[2][0])) && maxs[1] > min(v[0][1], min(v[1][1], v[2][1])) && mins[1] < max(v[0][1], max(v[1][1], v[2][1])) && maxs[2] > min(v[0][2], min(v[1][2], v[2][2])) && mins[2] < max(v[0][2], max(v[1][2], v[2][2])))
572                         shadowmarklist[numshadowmark++] = i;
573         }
574         R_Shadow_VolumeFromList(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, numshadowmark, shadowmarklist);
575 }
576
577 void R_Shadow_VolumeFromSphere(int numverts, int numtris, const float *invertex3f, const int *elements, const int *neighbors, const vec3_t projectorigin, float projectdistance, float radius)
578 {
579         vec3_t mins, maxs;
580         mins[0] = projectorigin[0] - radius;
581         mins[1] = projectorigin[1] - radius;
582         mins[2] = projectorigin[2] - radius;
583         maxs[0] = projectorigin[0] + radius;
584         maxs[1] = projectorigin[1] + radius;
585         maxs[2] = projectorigin[2] + radius;
586         R_Shadow_VolumeFromBox(numverts, numtris, invertex3f, elements, neighbors, projectorigin, projectdistance, mins, maxs);
587 }
588
589 void R_Shadow_RenderVolume(int numvertices, int numtriangles, const float *vertex3f, const int *element3i)
590 {
591         rmeshstate_t m;
592         if (r_shadow_compilingrtlight)
593         {
594                 // if we're compiling an rtlight, capture the mesh
595                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, r_shadow_compilingrtlight->static_meshchain_shadow, NULL, NULL, NULL, vertex3f, NULL, NULL, NULL, NULL, numtriangles, element3i);
596                 return;
597         }
598         memset(&m, 0, sizeof(m));
599         m.pointer_vertex = vertex3f;
600         R_Mesh_State(&m);
601         GL_LockArrays(0, numvertices);
602         if (r_shadowstage == SHADOWSTAGE_STENCIL)
603         {
604                 // increment stencil if backface is behind depthbuffer
605                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
606                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
607                 R_Mesh_Draw(numvertices, numtriangles, element3i);
608                 c_rt_shadowmeshes++;
609                 c_rt_shadowtris += numtriangles;
610                 // decrement stencil if frontface is behind depthbuffer
611                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
612                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
613         }
614         R_Mesh_Draw(numvertices, numtriangles, element3i);
615         c_rt_shadowmeshes++;
616         c_rt_shadowtris += numtriangles;
617         GL_LockArrays(0, 0);
618 }
619
620 float r_shadow_attenpower, r_shadow_attenscale;
621 static void R_Shadow_MakeTextures(void)
622 {
623         int x, y, z, d, side;
624         float v[3], s, t, intensity;
625         qbyte *data;
626         R_FreeTexturePool(&r_shadow_texturepool);
627         r_shadow_texturepool = R_AllocTexturePool();
628         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
629         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
630 #define NORMSIZE 64
631 #define ATTEN2DSIZE 64
632 #define ATTEN3DSIZE 32
633         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
634         data[0] = 128;
635         data[1] = 128;
636         data[2] = 255;
637         data[3] = 255;
638         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
639         data[0] = 255;
640         data[1] = 255;
641         data[2] = 255;
642         data[3] = 255;
643         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
644         data[0] = 255;
645         data[1] = 255;
646         data[2] = 255;
647         data[3] = 255;
648         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
649         if (gl_texturecubemap)
650         {
651                 for (side = 0;side < 6;side++)
652                 {
653                         for (y = 0;y < NORMSIZE;y++)
654                         {
655                                 for (x = 0;x < NORMSIZE;x++)
656                                 {
657                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
658                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
659                                         switch(side)
660                                         {
661                                         case 0:
662                                                 v[0] = 1;
663                                                 v[1] = -t;
664                                                 v[2] = -s;
665                                                 break;
666                                         case 1:
667                                                 v[0] = -1;
668                                                 v[1] = -t;
669                                                 v[2] = s;
670                                                 break;
671                                         case 2:
672                                                 v[0] = s;
673                                                 v[1] = 1;
674                                                 v[2] = t;
675                                                 break;
676                                         case 3:
677                                                 v[0] = s;
678                                                 v[1] = -1;
679                                                 v[2] = -t;
680                                                 break;
681                                         case 4:
682                                                 v[0] = s;
683                                                 v[1] = -t;
684                                                 v[2] = 1;
685                                                 break;
686                                         case 5:
687                                                 v[0] = -s;
688                                                 v[1] = -t;
689                                                 v[2] = -1;
690                                                 break;
691                                         }
692                                         intensity = 127.0f / sqrt(DotProduct(v, v));
693                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
694                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
695                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
696                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
697                                 }
698                         }
699                 }
700                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
701         }
702         else
703                 r_shadow_normalcubetexture = NULL;
704         for (y = 0;y < ATTEN2DSIZE;y++)
705         {
706                 for (x = 0;x < ATTEN2DSIZE;x++)
707                 {
708                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
709                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
710                         v[2] = 0;
711                         intensity = 1.0f - sqrt(DotProduct(v, v));
712                         if (intensity > 0)
713                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
714                         d = bound(0, intensity, 255);
715                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
716                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
717                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
718                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
719                 }
720         }
721         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
722         if (r_shadow_texture3d.integer)
723         {
724                 for (z = 0;z < ATTEN3DSIZE;z++)
725                 {
726                         for (y = 0;y < ATTEN3DSIZE;y++)
727                         {
728                                 for (x = 0;x < ATTEN3DSIZE;x++)
729                                 {
730                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
731                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
732                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
733                                         intensity = 1.0f - sqrt(DotProduct(v, v));
734                                         if (intensity > 0)
735                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
736                                         d = bound(0, intensity, 255);
737                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
738                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
739                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
740                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
741                                 }
742                         }
743                 }
744                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
745         }
746         Mem_Free(data);
747 }
748
749 void R_Shadow_Stage_Begin(void)
750 {
751         rmeshstate_t m;
752
753         if (r_shadow_texture3d.integer && !gl_texture3d)
754                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
755         if (gl_ext_stenciltwoside.integer && !gl_support_stenciltwoside)
756                 Cvar_SetValueQuick(&gl_ext_stenciltwoside, 0);
757
758         if (!r_shadow_attenuation2dtexture
759          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
760          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
761          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
762                 R_Shadow_MakeTextures();
763
764         memset(&m, 0, sizeof(m));
765         GL_BlendFunc(GL_ONE, GL_ZERO);
766         GL_DepthMask(false);
767         GL_DepthTest(true);
768         R_Mesh_State(&m);
769         GL_Color(0, 0, 0, 1);
770         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
771         qglEnable(GL_CULL_FACE);
772         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
773         r_shadowstage = SHADOWSTAGE_NONE;
774
775         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
776         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
777         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
778 }
779
780 void R_Shadow_LoadWorldLightsIfNeeded(void)
781 {
782         if (r_shadow_reloadlights && cl.worldmodel)
783         {
784                 R_Shadow_ClearWorldLights();
785                 r_shadow_reloadlights = false;
786                 R_Shadow_LoadWorldLights();
787                 if (r_shadow_worldlightchain == NULL)
788                 {
789                         R_Shadow_LoadLightsFile();
790                         if (r_shadow_worldlightchain == NULL)
791                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
792                 }
793         }
794 }
795
796 void R_Shadow_Stage_ShadowVolumes(void)
797 {
798         rmeshstate_t m;
799         memset(&m, 0, sizeof(m));
800         R_Mesh_State(&m);
801         GL_Color(1, 1, 1, 1);
802         GL_ColorMask(0, 0, 0, 0);
803         GL_BlendFunc(GL_ONE, GL_ZERO);
804         GL_DepthMask(false);
805         GL_DepthTest(true);
806         qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
807         //if (r_shadow_polygonoffset.value != 0)
808         //{
809         //      qglPolygonOffset(r_shadow_polygonfactor.value, r_shadow_polygonoffset.value);
810         //      qglEnable(GL_POLYGON_OFFSET_FILL);
811         //}
812         //else
813         //      qglDisable(GL_POLYGON_OFFSET_FILL);
814         qglDepthFunc(GL_LESS);
815         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
816         qglEnable(GL_STENCIL_TEST);
817         qglStencilFunc(GL_ALWAYS, 128, ~0);
818         if (gl_ext_stenciltwoside.integer)
819         {
820                 r_shadowstage = SHADOWSTAGE_STENCILTWOSIDE;
821                 qglDisable(GL_CULL_FACE);
822                 qglEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
823                 qglActiveStencilFaceEXT(GL_BACK); // quake is backwards, this is front faces
824                 qglStencilMask(~0);
825                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
826                 qglActiveStencilFaceEXT(GL_FRONT); // quake is backwards, this is back faces
827                 qglStencilMask(~0);
828                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
829         }
830         else
831         {
832                 r_shadowstage = SHADOWSTAGE_STENCIL;
833                 qglEnable(GL_CULL_FACE);
834                 qglStencilMask(~0);
835                 // this is changed by every shadow render so its value here is unimportant
836                 qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
837         }
838         GL_Clear(GL_STENCIL_BUFFER_BIT);
839         c_rt_clears++;
840         // LordHavoc note: many shadow volumes reside entirely inside the world
841         // (that is to say they are entirely bounded by their lit surfaces),
842         // which can be optimized by handling things as an inverted light volume,
843         // with the shadow boundaries of the world being simulated by an altered
844         // (129) bias to stencil clearing on such lights
845         // FIXME: generate inverted light volumes for use as shadow volumes and
846         // optimize for them as noted above
847 }
848
849 void R_Shadow_Stage_LightWithoutShadows(void)
850 {
851         rmeshstate_t m;
852         memset(&m, 0, sizeof(m));
853         R_Mesh_State(&m);
854         GL_BlendFunc(GL_ONE, GL_ONE);
855         GL_DepthMask(false);
856         GL_DepthTest(true);
857         qglPolygonOffset(0, 0);
858         //qglDisable(GL_POLYGON_OFFSET_FILL);
859         GL_Color(1, 1, 1, 1);
860         GL_ColorMask(1, 1, 1, 1);
861         qglDepthFunc(GL_EQUAL);
862         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
863         qglEnable(GL_CULL_FACE);
864         qglDisable(GL_STENCIL_TEST);
865         if (gl_support_stenciltwoside)
866                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
867         qglStencilMask(~0);
868         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
869         qglStencilFunc(GL_EQUAL, 128, ~0);
870         r_shadowstage = SHADOWSTAGE_LIGHT;
871         c_rt_lights++;
872 }
873
874 void R_Shadow_Stage_LightWithShadows(void)
875 {
876         rmeshstate_t m;
877         memset(&m, 0, sizeof(m));
878         R_Mesh_State(&m);
879         GL_BlendFunc(GL_ONE, GL_ONE);
880         GL_DepthMask(false);
881         GL_DepthTest(true);
882         qglPolygonOffset(0, 0);
883         //qglDisable(GL_POLYGON_OFFSET_FILL);
884         GL_Color(1, 1, 1, 1);
885         GL_ColorMask(1, 1, 1, 1);
886         qglDepthFunc(GL_EQUAL);
887         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
888         qglEnable(GL_STENCIL_TEST);
889         if (gl_support_stenciltwoside)
890                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
891         qglStencilMask(~0);
892         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
893         // only draw light where this geometry was already rendered AND the
894         // stencil is 128 (values other than this mean shadow)
895         qglStencilFunc(GL_EQUAL, 128, ~0);
896         r_shadowstage = SHADOWSTAGE_LIGHT;
897         c_rt_lights++;
898 }
899
900 void R_Shadow_Stage_End(void)
901 {
902         rmeshstate_t m;
903         memset(&m, 0, sizeof(m));
904         R_Mesh_State(&m);
905         GL_BlendFunc(GL_ONE, GL_ZERO);
906         GL_DepthMask(true);
907         GL_DepthTest(true);
908         qglPolygonOffset(0, 0);
909         //qglDisable(GL_POLYGON_OFFSET_FILL);
910         GL_Color(1, 1, 1, 1);
911         GL_ColorMask(1, 1, 1, 1);
912         GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
913         qglDepthFunc(GL_LEQUAL);
914         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
915         qglDisable(GL_STENCIL_TEST);
916         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
917         if (gl_support_stenciltwoside)
918                 qglDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
919         qglStencilMask(~0);
920         qglStencilFunc(GL_ALWAYS, 128, ~0);
921         r_shadowstage = SHADOWSTAGE_NONE;
922 }
923
924 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
925 {
926         int i, ix1, iy1, ix2, iy2;
927         float x1, y1, x2, y2, x, y, f;
928         vec3_t smins, smaxs;
929         vec4_t v, v2;
930         if (!r_shadow_scissor.integer)
931                 return false;
932         // if view is inside the box, just say yes it's visible
933         // LordHavoc: for some odd reason scissor seems broken without stencil
934         // (?!?  seems like a driver bug) so abort if gl_stencil is false
935         if (!gl_stencil || BoxesOverlap(r_vieworigin, r_vieworigin, mins, maxs))
936         {
937                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
938                 return false;
939         }
940         for (i = 0;i < 3;i++)
941         {
942                 if (r_viewforward[i] >= 0)
943                 {
944                         v[i] = mins[i];
945                         v2[i] = maxs[i];
946                 }
947                 else
948                 {
949                         v[i] = maxs[i];
950                         v2[i] = mins[i];
951                 }
952         }
953         f = DotProduct(r_viewforward, r_vieworigin) + 1;
954         if (DotProduct(r_viewforward, v2) <= f)
955         {
956                 // entirely behind nearclip plane
957                 return true;
958         }
959         if (DotProduct(r_viewforward, v) >= f)
960         {
961                 // entirely infront of nearclip plane
962                 x1 = y1 = x2 = y2 = 0;
963                 for (i = 0;i < 8;i++)
964                 {
965                         v[0] = (i & 1) ? mins[0] : maxs[0];
966                         v[1] = (i & 2) ? mins[1] : maxs[1];
967                         v[2] = (i & 4) ? mins[2] : maxs[2];
968                         v[3] = 1.0f;
969                         GL_TransformToScreen(v, v2);
970                         //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]);
971                         x = v2[0];
972                         y = v2[1];
973                         if (i)
974                         {
975                                 if (x1 > x) x1 = x;
976                                 if (x2 < x) x2 = x;
977                                 if (y1 > y) y1 = y;
978                                 if (y2 < y) y2 = y;
979                         }
980                         else
981                         {
982                                 x1 = x2 = x;
983                                 y1 = y2 = y;
984                         }
985                 }
986         }
987         else
988         {
989                 // clipped by nearclip plane
990                 // this is nasty and crude...
991                 // create viewspace bbox
992                 for (i = 0;i < 8;i++)
993                 {
994                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_vieworigin[0];
995                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_vieworigin[1];
996                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_vieworigin[2];
997                         v2[0] = -DotProduct(v, r_viewleft);
998                         v2[1] = DotProduct(v, r_viewup);
999                         v2[2] = DotProduct(v, r_viewforward);
1000                         if (i)
1001                         {
1002                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1003                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1004                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1005                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1006                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1007                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1008                         }
1009                         else
1010                         {
1011                                 smins[0] = smaxs[0] = v2[0];
1012                                 smins[1] = smaxs[1] = v2[1];
1013                                 smins[2] = smaxs[2] = v2[2];
1014                         }
1015                 }
1016                 // now we have a bbox in viewspace
1017                 // clip it to the view plane
1018                 if (smins[2] < 1)
1019                         smins[2] = 1;
1020                 // return true if that culled the box
1021                 if (smins[2] >= smaxs[2])
1022                         return true;
1023                 // ok some of it is infront of the view, transform each corner back to
1024                 // worldspace and then to screenspace and make screen rect
1025                 // initialize these variables just to avoid compiler warnings
1026                 x1 = y1 = x2 = y2 = 0;
1027                 for (i = 0;i < 8;i++)
1028                 {
1029                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1030                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1031                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1032                         v[0] = v2[0] * -r_viewleft[0] + v2[1] * r_viewup[0] + v2[2] * r_viewforward[0] + r_vieworigin[0];
1033                         v[1] = v2[0] * -r_viewleft[1] + v2[1] * r_viewup[1] + v2[2] * r_viewforward[1] + r_vieworigin[1];
1034                         v[2] = v2[0] * -r_viewleft[2] + v2[1] * r_viewup[2] + v2[2] * r_viewforward[2] + r_vieworigin[2];
1035                         v[3] = 1.0f;
1036                         GL_TransformToScreen(v, v2);
1037                         //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]);
1038                         x = v2[0];
1039                         y = v2[1];
1040                         if (i)
1041                         {
1042                                 if (x1 > x) x1 = x;
1043                                 if (x2 < x) x2 = x;
1044                                 if (y1 > y) y1 = y;
1045                                 if (y2 < y) y2 = y;
1046                         }
1047                         else
1048                         {
1049                                 x1 = x2 = x;
1050                                 y1 = y2 = y;
1051                         }
1052                 }
1053                 /*
1054                 // this code doesn't handle boxes with any points behind view properly
1055                 x1 = 1000;x2 = -1000;
1056                 y1 = 1000;y2 = -1000;
1057                 for (i = 0;i < 8;i++)
1058                 {
1059                         v[0] = (i & 1) ? mins[0] : maxs[0];
1060                         v[1] = (i & 2) ? mins[1] : maxs[1];
1061                         v[2] = (i & 4) ? mins[2] : maxs[2];
1062                         v[3] = 1.0f;
1063                         GL_TransformToScreen(v, v2);
1064                         //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]);
1065                         if (v2[2] > 0)
1066                         {
1067                                 x = v2[0];
1068                                 y = v2[1];
1069
1070                                 if (x1 > x) x1 = x;
1071                                 if (x2 < x) x2 = x;
1072                                 if (y1 > y) y1 = y;
1073                                 if (y2 < y) y2 = y;
1074                         }
1075                 }
1076                 */
1077         }
1078         ix1 = x1 - 1.0f;
1079         iy1 = y1 - 1.0f;
1080         ix2 = x2 + 1.0f;
1081         iy2 = y2 + 1.0f;
1082         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1083         if (ix1 < r_view_x) ix1 = r_view_x;
1084         if (iy1 < r_view_y) iy1 = r_view_y;
1085         if (ix2 > r_view_x + r_view_width) ix2 = r_view_x + r_view_width;
1086         if (iy2 > r_view_y + r_view_height) iy2 = r_view_y + r_view_height;
1087         if (ix2 <= ix1 || iy2 <= iy1)
1088                 return true;
1089         // set up the scissor rectangle
1090         GL_Scissor(ix1, vid.realheight - iy2, ix2 - ix1, iy2 - iy1);
1091         //qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1092         //qglEnable(GL_SCISSOR_TEST);
1093         c_rt_scissored++;
1094         return false;
1095 }
1096
1097 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1098 {
1099         float *color4f = varray_color4f;
1100         float dist, dot, intensity, v[3], n[3];
1101         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1102         {
1103                 Matrix4x4_Transform(m, vertex3f, v);
1104                 if ((dist = DotProduct(v, v)) < 1)
1105                 {
1106                         Matrix4x4_Transform3x3(m, normal3f, n);
1107                         if ((dot = DotProduct(n, v)) > 0)
1108                         {
1109                                 dist = sqrt(dist);
1110                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1111                                 VectorScale(lightcolor, intensity, color4f);
1112                                 color4f[3] = 1;
1113                         }
1114                         else
1115                         {
1116                                 VectorClear(color4f);
1117                                 color4f[3] = 1;
1118                         }
1119                 }
1120                 else
1121                 {
1122                         VectorClear(color4f);
1123                         color4f[3] = 1;
1124                 }
1125         }
1126 }
1127
1128 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1129 {
1130         float *color4f = varray_color4f;
1131         float dist, dot, intensity, v[3], n[3];
1132         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1133         {
1134                 Matrix4x4_Transform(m, vertex3f, v);
1135                 if ((dist = fabs(v[2])) < 1)
1136                 {
1137                         Matrix4x4_Transform3x3(m, normal3f, n);
1138                         if ((dot = DotProduct(n, v)) > 0)
1139                         {
1140                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1141                                 VectorScale(lightcolor, intensity, color4f);
1142                                 color4f[3] = 1;
1143                         }
1144                         else
1145                         {
1146                                 VectorClear(color4f);
1147                                 color4f[3] = 1;
1148                         }
1149                 }
1150                 else
1151                 {
1152                         VectorClear(color4f);
1153                         color4f[3] = 1;
1154                 }
1155         }
1156 }
1157
1158 // FIXME: this should be done in a vertex program when possible
1159 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1160 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1161 {
1162         do
1163         {
1164                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1165                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1166                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1167                 vertex3f += 3;
1168                 tc3f += 3;
1169         }
1170         while (--numverts);
1171 }
1172
1173 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1174 {
1175         do
1176         {
1177                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1178                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1179                 vertex3f += 3;
1180                 tc2f += 2;
1181         }
1182         while (--numverts);
1183 }
1184
1185 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)
1186 {
1187         int i;
1188         float lightdir[3];
1189         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1190         {
1191                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1192                 // the cubemap normalizes this for us
1193                 out3f[0] = DotProduct(svector3f, lightdir);
1194                 out3f[1] = DotProduct(tvector3f, lightdir);
1195                 out3f[2] = DotProduct(normal3f, lightdir);
1196         }
1197 }
1198
1199 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)
1200 {
1201         int i;
1202         float lightdir[3], eyedir[3], halfdir[3];
1203         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1204         {
1205                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1206                 VectorNormalizeFast(lightdir);
1207                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1208                 VectorNormalizeFast(eyedir);
1209                 VectorAdd(lightdir, eyedir, halfdir);
1210                 // the cubemap normalizes this for us
1211                 out3f[0] = DotProduct(svector3f, halfdir);
1212                 out3f[1] = DotProduct(tvector3f, halfdir);
1213                 out3f[2] = DotProduct(normal3f, halfdir);
1214         }
1215 }
1216
1217 #define USETEXMATRIX 1
1218 void R_Shadow_RenderLighting(int numverts, int numtriangles, const int *elements, const float *vertex3f, const float *svector3f, const float *tvector3f, const float *normal3f, const float *texcoord2f, const float *relativelightorigin, const float *relativeeyeorigin, const float *lightcolor, const matrix4x4_t *matrix_modeltolight, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *glosstexture, rtexture_t *lightcubemap, int lighting)
1219 {
1220         int renders;
1221         float color[3], color2[3], colorscale;
1222         rmeshstate_t m;
1223         if (!bumptexture)
1224                 bumptexture = r_shadow_blankbumptexture;
1225         if (!glosstexture)
1226                 glosstexture = r_shadow_blankglosstexture;
1227         GL_DepthMask(false);
1228         GL_DepthTest(true);
1229         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1230         {
1231                 if (lighting & LIGHTING_DIFFUSE)
1232                 {
1233                         GL_Color(1,1,1,1);
1234                         // colorscale accounts for how much we multiply the brightness during combine
1235                         // mult is how many times the final pass of the lighting will be
1236                         // performed to get more brightness than otherwise possible
1237                         // limit mult to 64 for sanity sake
1238                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1239                         {
1240                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1241                                 memset(&m, 0, sizeof(m));
1242                                 m.pointer_vertex = vertex3f;
1243                                 m.tex[0] = R_GetTexture(bumptexture);
1244                                 m.texcombinergb[0] = GL_REPLACE;
1245                                 m.pointer_texcoord[0] = texcoord2f;
1246                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1247                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1248                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1249                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1250                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1251 #if USETEXMATRIX
1252                                 m.pointer_texcoord3f[2] = vertex3f;
1253                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1254 #else
1255                                 m.pointer_texcoord3f[2] = varray_texcoord3f[2];
1256                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1257 #endif
1258                                 R_Mesh_State(&m);
1259                                 GL_ColorMask(0,0,0,1);
1260                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1261                                 GL_LockArrays(0, numverts);
1262                                 R_Mesh_Draw(numverts, numtriangles, elements);
1263                                 GL_LockArrays(0, 0);
1264                                 c_rt_lightmeshes++;
1265                                 c_rt_lighttris += numtriangles;
1266         
1267                                 memset(&m, 0, sizeof(m));
1268                                 m.pointer_vertex = vertex3f;
1269                                 m.tex[0] = R_GetTexture(basetexture);
1270                                 m.pointer_texcoord[0] = texcoord2f;
1271                                 if (lightcubemap)
1272                                 {
1273                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1274 #if USETEXMATRIX
1275                                         m.pointer_texcoord3f[1] = vertex3f;
1276                                         m.texmatrix[1] = *matrix_modeltolight;
1277 #else
1278                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1279                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1280 #endif
1281                                 }
1282                                 R_Mesh_State(&m);
1283                                 GL_LockArrays(0, numverts);
1284                                 GL_ColorMask(1,1,1,0);
1285                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1286                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1287                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1288                                 {
1289                                         color[0] = bound(0, color2[0], 1);
1290                                         color[1] = bound(0, color2[1], 1);
1291                                         color[2] = bound(0, color2[2], 1);
1292                                         GL_Color(color[0], color[1], color[2], 1);
1293                                         R_Mesh_Draw(numverts, numtriangles, elements);
1294                                         c_rt_lightmeshes++;
1295                                         c_rt_lighttris += numtriangles;
1296                                 }
1297                                 GL_LockArrays(0, 0);
1298                         }
1299                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1300                         {
1301                                 // 1/2/2 3D combine path (original Radeon)
1302                                 memset(&m, 0, sizeof(m));
1303                                 m.pointer_vertex = vertex3f;
1304                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1305 #if USETEXMATRIX
1306                                 m.pointer_texcoord3f[0] = vertex3f;
1307                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1308 #else
1309                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1310                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1311 #endif
1312                                 R_Mesh_State(&m);
1313                                 GL_ColorMask(0,0,0,1);
1314                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1315                                 GL_LockArrays(0, numverts);
1316                                 R_Mesh_Draw(numverts, numtriangles, elements);
1317                                 GL_LockArrays(0, 0);
1318                                 c_rt_lightmeshes++;
1319                                 c_rt_lighttris += numtriangles;
1320         
1321                                 memset(&m, 0, sizeof(m));
1322                                 m.pointer_vertex = vertex3f;
1323                                 m.tex[0] = R_GetTexture(bumptexture);
1324                                 m.texcombinergb[0] = GL_REPLACE;
1325                                 m.pointer_texcoord[0] = texcoord2f;
1326                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1327                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1328                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1329                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1330                                 R_Mesh_State(&m);
1331                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1332                                 GL_LockArrays(0, numverts);
1333                                 R_Mesh_Draw(numverts, numtriangles, elements);
1334                                 GL_LockArrays(0, 0);
1335                                 c_rt_lightmeshes++;
1336                                 c_rt_lighttris += numtriangles;
1337         
1338                                 memset(&m, 0, sizeof(m));
1339                                 m.pointer_vertex = vertex3f;
1340                                 m.tex[0] = R_GetTexture(basetexture);
1341                                 m.pointer_texcoord[0] = texcoord2f;
1342                                 if (lightcubemap)
1343                                 {
1344                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1345 #if USETEXMATRIX
1346                                         m.pointer_texcoord3f[1] = vertex3f;
1347                                         m.texmatrix[1] = *matrix_modeltolight;
1348 #else
1349                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1350                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1351 #endif
1352                                 }
1353                                 R_Mesh_State(&m);
1354                                 GL_LockArrays(0, numverts);
1355                                 GL_ColorMask(1,1,1,0);
1356                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1357                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1358                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1359                                 {
1360                                         color[0] = bound(0, color2[0], 1);
1361                                         color[1] = bound(0, color2[1], 1);
1362                                         color[2] = bound(0, color2[2], 1);
1363                                         GL_Color(color[0], color[1], color[2], 1);
1364                                         R_Mesh_Draw(numverts, numtriangles, elements);
1365                                         c_rt_lightmeshes++;
1366                                         c_rt_lighttris += numtriangles;
1367                                 }
1368                                 GL_LockArrays(0, 0);
1369                         }
1370                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1371                         {
1372                                 // 2/2 3D combine path (original Radeon)
1373                                 memset(&m, 0, sizeof(m));
1374                                 m.pointer_vertex = vertex3f;
1375                                 m.tex[0] = R_GetTexture(bumptexture);
1376                                 m.texcombinergb[0] = GL_REPLACE;
1377                                 m.pointer_texcoord[0] = texcoord2f;
1378                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1379                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1380                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1381                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1382                                 R_Mesh_State(&m);
1383                                 GL_ColorMask(0,0,0,1);
1384                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1385                                 GL_LockArrays(0, numverts);
1386                                 R_Mesh_Draw(numverts, numtriangles, elements);
1387                                 GL_LockArrays(0, 0);
1388                                 c_rt_lightmeshes++;
1389                                 c_rt_lighttris += numtriangles;
1390         
1391                                 memset(&m, 0, sizeof(m));
1392                                 m.pointer_vertex = vertex3f;
1393                                 m.tex[0] = R_GetTexture(basetexture);
1394                                 m.pointer_texcoord[0] = texcoord2f;
1395                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1396 #if USETEXMATRIX
1397                                 m.pointer_texcoord3f[1] = vertex3f;
1398                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1399 #else
1400                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1401                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1402 #endif
1403                                 R_Mesh_State(&m);
1404                                 GL_LockArrays(0, numverts);
1405                                 GL_ColorMask(1,1,1,0);
1406                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1407                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1408                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1409                                 {
1410                                         color[0] = bound(0, color2[0], 1);
1411                                         color[1] = bound(0, color2[1], 1);
1412                                         color[2] = bound(0, color2[2], 1);
1413                                         GL_Color(color[0], color[1], color[2], 1);
1414                                         R_Mesh_Draw(numverts, numtriangles, elements);
1415                                         c_rt_lightmeshes++;
1416                                         c_rt_lighttris += numtriangles;
1417                                 }
1418                                 GL_LockArrays(0, 0);
1419                         }
1420                         else if (r_textureunits.integer >= 4)
1421                         {
1422                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1423                                 memset(&m, 0, sizeof(m));
1424                                 m.pointer_vertex = vertex3f;
1425                                 m.tex[0] = R_GetTexture(bumptexture);
1426                                 m.texcombinergb[0] = GL_REPLACE;
1427                                 m.pointer_texcoord[0] = texcoord2f;
1428                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1429                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1430                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1431                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1432                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1433 #if USETEXMATRIX
1434                                 m.pointer_texcoord3f[2] = vertex3f;
1435                                 m.texmatrix[2] = *matrix_modeltoattenuationxyz;
1436 #else
1437                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
1438                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1439 #endif
1440                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1441 #if USETEXMATRIX
1442                                 m.pointer_texcoord3f[3] = vertex3f;
1443                                 m.texmatrix[3] = *matrix_modeltoattenuationz;
1444 #else
1445                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
1446                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1447 #endif
1448                                 R_Mesh_State(&m);
1449                                 GL_ColorMask(0,0,0,1);
1450                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1451                                 GL_LockArrays(0, numverts);
1452                                 R_Mesh_Draw(numverts, numtriangles, elements);
1453                                 GL_LockArrays(0, 0);
1454                                 c_rt_lightmeshes++;
1455                                 c_rt_lighttris += numtriangles;
1456         
1457                                 memset(&m, 0, sizeof(m));
1458                                 m.pointer_vertex = vertex3f;
1459                                 m.tex[0] = R_GetTexture(basetexture);
1460                                 m.pointer_texcoord[0] = texcoord2f;
1461                                 if (lightcubemap)
1462                                 {
1463                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1464 #if USETEXMATRIX
1465                                         m.pointer_texcoord3f[1] = vertex3f;
1466                                         m.texmatrix[1] = *matrix_modeltolight;
1467 #else
1468                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1469                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1470 #endif
1471                                 }
1472                                 R_Mesh_State(&m);
1473                                 GL_LockArrays(0, numverts);
1474                                 GL_ColorMask(1,1,1,0);
1475                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1476                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1477                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1478                                 {
1479                                         color[0] = bound(0, color2[0], 1);
1480                                         color[1] = bound(0, color2[1], 1);
1481                                         color[2] = bound(0, color2[2], 1);
1482                                         GL_Color(color[0], color[1], color[2], 1);
1483                                         R_Mesh_Draw(numverts, numtriangles, elements);
1484                                         c_rt_lightmeshes++;
1485                                         c_rt_lighttris += numtriangles;
1486                                 }
1487                                 GL_LockArrays(0, 0);
1488                         }
1489                         else
1490                         {
1491                                 // 2/2/2 2D combine path (any dot3 card)
1492                                 memset(&m, 0, sizeof(m));
1493                                 m.pointer_vertex = vertex3f;
1494                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1495 #if USETEXMATRIX
1496                                 m.pointer_texcoord3f[0] = vertex3f;
1497                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1498 #else
1499                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1500                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1501 #endif
1502                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1503 #if USETEXMATRIX
1504                                 m.pointer_texcoord3f[1] = vertex3f;
1505                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1506 #else
1507                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1508                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1509 #endif
1510                                 R_Mesh_State(&m);
1511                                 GL_ColorMask(0,0,0,1);
1512                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1513                                 GL_LockArrays(0, numverts);
1514                                 R_Mesh_Draw(numverts, numtriangles, elements);
1515                                 GL_LockArrays(0, 0);
1516                                 c_rt_lightmeshes++;
1517                                 c_rt_lighttris += numtriangles;
1518         
1519                                 memset(&m, 0, sizeof(m));
1520                                 m.pointer_vertex = vertex3f;
1521                                 m.tex[0] = R_GetTexture(bumptexture);
1522                                 m.texcombinergb[0] = GL_REPLACE;
1523                                 m.pointer_texcoord[0] = texcoord2f;
1524                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1525                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1526                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1527                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1528                                 R_Mesh_State(&m);
1529                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1530                                 GL_LockArrays(0, numverts);
1531                                 R_Mesh_Draw(numverts, numtriangles, elements);
1532                                 GL_LockArrays(0, 0);
1533                                 c_rt_lightmeshes++;
1534                                 c_rt_lighttris += numtriangles;
1535         
1536                                 memset(&m, 0, sizeof(m));
1537                                 m.pointer_vertex = vertex3f;
1538                                 m.tex[0] = R_GetTexture(basetexture);
1539                                 m.pointer_texcoord[0] = texcoord2f;
1540                                 if (lightcubemap)
1541                                 {
1542                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1543 #if USETEXMATRIX
1544                                         m.pointer_texcoord3f[1] = vertex3f;
1545                                         m.texmatrix[1] = *matrix_modeltolight;
1546 #else
1547                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1548                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1549 #endif
1550                                 }
1551                                 R_Mesh_State(&m);
1552                                 GL_LockArrays(0, numverts);
1553                                 GL_ColorMask(1,1,1,0);
1554                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1555                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1556                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1557                                 {
1558                                         color[0] = bound(0, color2[0], 1);
1559                                         color[1] = bound(0, color2[1], 1);
1560                                         color[2] = bound(0, color2[2], 1);
1561                                         GL_Color(color[0], color[1], color[2], 1);
1562                                         R_Mesh_Draw(numverts, numtriangles, elements);
1563                                         c_rt_lightmeshes++;
1564                                         c_rt_lighttris += numtriangles;
1565                                 }
1566                                 GL_LockArrays(0, 0);
1567                         }
1568                 }
1569                 if ((lighting & LIGHTING_SPECULAR) && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1570                 {
1571                         colorscale = r_shadow_glossintensity.value;
1572                         if (glosstexture == r_shadow_blankglosstexture)
1573                                 colorscale *= r_shadow_gloss2intensity.value;
1574                         GL_Color(1,1,1,1);
1575                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1576                         {
1577                                 // 2/0/0/1/2 3D combine blendsquare path
1578                                 memset(&m, 0, sizeof(m));
1579                                 m.pointer_vertex = vertex3f;
1580                                 m.tex[0] = R_GetTexture(bumptexture);
1581                                 m.pointer_texcoord[0] = texcoord2f;
1582                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1583                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1584                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1585                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1586                                 R_Mesh_State(&m);
1587                                 GL_ColorMask(0,0,0,1);
1588                                 // this squares the result
1589                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1590                                 GL_LockArrays(0, numverts);
1591                                 R_Mesh_Draw(numverts, numtriangles, elements);
1592                                 GL_LockArrays(0, 0);
1593                                 c_rt_lightmeshes++;
1594                                 c_rt_lighttris += numtriangles;
1595         
1596                                 memset(&m, 0, sizeof(m));
1597                                 m.pointer_vertex = vertex3f;
1598                                 R_Mesh_State(&m);
1599                                 GL_LockArrays(0, numverts);
1600                                 // square alpha in framebuffer a few times to make it shiny
1601                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1602                                 // these comments are a test run through this math for intensity 0.5
1603                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1604                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1605                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1606                                 R_Mesh_Draw(numverts, numtriangles, elements);
1607                                 c_rt_lightmeshes++;
1608                                 c_rt_lighttris += numtriangles;
1609                                 R_Mesh_Draw(numverts, numtriangles, elements);
1610                                 c_rt_lightmeshes++;
1611                                 c_rt_lighttris += numtriangles;
1612                                 GL_LockArrays(0, 0);
1613         
1614                                 memset(&m, 0, sizeof(m));
1615                                 m.pointer_vertex = vertex3f;
1616                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1617 #if USETEXMATRIX
1618                                 m.pointer_texcoord3f[0] = vertex3f;
1619                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1620 #else
1621                                 m.pointer_texcoord3f[0] = varray_texcoord3f[0];
1622                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1623 #endif
1624                                 R_Mesh_State(&m);
1625                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1626                                 GL_LockArrays(0, numverts);
1627                                 R_Mesh_Draw(numverts, numtriangles, elements);
1628                                 GL_LockArrays(0, 0);
1629                                 c_rt_lightmeshes++;
1630                                 c_rt_lighttris += numtriangles;
1631         
1632                                 memset(&m, 0, sizeof(m));
1633                                 m.pointer_vertex = vertex3f;
1634                                 m.tex[0] = R_GetTexture(glosstexture);
1635                                 m.pointer_texcoord[0] = texcoord2f;
1636                                 if (lightcubemap)
1637                                 {
1638                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1639 #if USETEXMATRIX
1640                                         m.pointer_texcoord3f[1] = vertex3f;
1641                                         m.texmatrix[1] = *matrix_modeltolight;
1642 #else
1643                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1644                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1645 #endif
1646                                 }
1647                                 R_Mesh_State(&m);
1648                                 GL_LockArrays(0, numverts);
1649                                 GL_ColorMask(1,1,1,0);
1650                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1651                                 VectorScale(lightcolor, colorscale, color2);
1652                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1653                                 {
1654                                         color[0] = bound(0, color2[0], 1);
1655                                         color[1] = bound(0, color2[1], 1);
1656                                         color[2] = bound(0, color2[2], 1);
1657                                         GL_Color(color[0], color[1], color[2], 1);
1658                                         R_Mesh_Draw(numverts, numtriangles, elements);
1659                                         c_rt_lightmeshes++;
1660                                         c_rt_lighttris += numtriangles;
1661                                 }
1662                                 GL_LockArrays(0, 0);
1663                         }
1664                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1665                         {
1666                                 // 2/0/0/2 3D combine blendsquare path
1667                                 memset(&m, 0, sizeof(m));
1668                                 m.pointer_vertex = vertex3f;
1669                                 m.tex[0] = R_GetTexture(bumptexture);
1670                                 m.pointer_texcoord[0] = texcoord2f;
1671                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1672                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1673                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1674                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1675                                 R_Mesh_State(&m);
1676                                 GL_ColorMask(0,0,0,1);
1677                                 // this squares the result
1678                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1679                                 GL_LockArrays(0, numverts);
1680                                 R_Mesh_Draw(numverts, numtriangles, elements);
1681                                 GL_LockArrays(0, 0);
1682                                 c_rt_lightmeshes++;
1683                                 c_rt_lighttris += numtriangles;
1684         
1685                                 memset(&m, 0, sizeof(m));
1686                                 m.pointer_vertex = vertex3f;
1687                                 R_Mesh_State(&m);
1688                                 GL_LockArrays(0, numverts);
1689                                 // square alpha in framebuffer a few times to make it shiny
1690                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1691                                 // these comments are a test run through this math for intensity 0.5
1692                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1693                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1694                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1695                                 R_Mesh_Draw(numverts, numtriangles, elements);
1696                                 c_rt_lightmeshes++;
1697                                 c_rt_lighttris += numtriangles;
1698                                 R_Mesh_Draw(numverts, numtriangles, elements);
1699                                 c_rt_lightmeshes++;
1700                                 c_rt_lighttris += numtriangles;
1701                                 GL_LockArrays(0, 0);
1702         
1703                                 memset(&m, 0, sizeof(m));
1704                                 m.pointer_vertex = vertex3f;
1705                                 m.tex[0] = R_GetTexture(glosstexture);
1706                                 m.pointer_texcoord[0] = texcoord2f;
1707                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1708 #if USETEXMATRIX
1709                                 m.pointer_texcoord3f[1] = vertex3f;
1710                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1711 #else
1712                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1713                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1714 #endif
1715                                 R_Mesh_State(&m);
1716                                 GL_LockArrays(0, numverts);
1717                                 GL_ColorMask(1,1,1,0);
1718                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1719                                 VectorScale(lightcolor, colorscale, color2);
1720                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1721                                 {
1722                                         color[0] = bound(0, color2[0], 1);
1723                                         color[1] = bound(0, color2[1], 1);
1724                                         color[2] = bound(0, color2[2], 1);
1725                                         GL_Color(color[0], color[1], color[2], 1);
1726                                         R_Mesh_Draw(numverts, numtriangles, elements);
1727                                         c_rt_lightmeshes++;
1728                                         c_rt_lighttris += numtriangles;
1729                                 }
1730                                 GL_LockArrays(0, 0);
1731                         }
1732                         else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1733                         {
1734                                 // 2/0/0/2/2 2D combine blendsquare path
1735                                 memset(&m, 0, sizeof(m));
1736                                 m.pointer_vertex = vertex3f;
1737                                 m.tex[0] = R_GetTexture(bumptexture);
1738                                 m.pointer_texcoord[0] = texcoord2f;
1739                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1740                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1741                                 m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1742                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1743                                 R_Mesh_State(&m);
1744                                 GL_ColorMask(0,0,0,1);
1745                                 // this squares the result
1746                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1747                                 GL_LockArrays(0, numverts);
1748                                 R_Mesh_Draw(numverts, numtriangles, elements);
1749                                 GL_LockArrays(0, 0);
1750                                 c_rt_lightmeshes++;
1751                                 c_rt_lighttris += numtriangles;
1752         
1753                                 memset(&m, 0, sizeof(m));
1754                                 m.pointer_vertex = vertex3f;
1755                                 R_Mesh_State(&m);
1756                                 GL_LockArrays(0, numverts);
1757                                 // square alpha in framebuffer a few times to make it shiny
1758                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1759                                 // these comments are a test run through this math for intensity 0.5
1760                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1761                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1762                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1763                                 R_Mesh_Draw(numverts, numtriangles, elements);
1764                                 c_rt_lightmeshes++;
1765                                 c_rt_lighttris += numtriangles;
1766                                 R_Mesh_Draw(numverts, numtriangles, elements);
1767                                 c_rt_lightmeshes++;
1768                                 c_rt_lighttris += numtriangles;
1769                                 GL_LockArrays(0, 0);
1770         
1771                                 memset(&m, 0, sizeof(m));
1772                                 m.pointer_vertex = vertex3f;
1773                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1774 #if USETEXMATRIX
1775                                 m.pointer_texcoord3f[0] = vertex3f;
1776                                 m.texmatrix[0] = *matrix_modeltoattenuationxyz;
1777 #else
1778                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1779                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1780 #endif
1781                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1782 #if USETEXMATRIX
1783                                 m.pointer_texcoord3f[1] = vertex3f;
1784                                 m.texmatrix[1] = *matrix_modeltoattenuationz;
1785 #else
1786                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1787                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1788 #endif
1789                                 R_Mesh_State(&m);
1790                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1791                                 GL_LockArrays(0, numverts);
1792                                 R_Mesh_Draw(numverts, numtriangles, elements);
1793                                 GL_LockArrays(0, 0);
1794                                 c_rt_lightmeshes++;
1795                                 c_rt_lighttris += numtriangles;
1796         
1797                                 memset(&m, 0, sizeof(m));
1798                                 m.pointer_vertex = vertex3f;
1799                                 m.tex[0] = R_GetTexture(glosstexture);
1800                                 m.pointer_texcoord[0] = texcoord2f;
1801                                 if (lightcubemap)
1802                                 {
1803                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1804 #if USETEXMATRIX
1805                                         m.pointer_texcoord3f[1] = vertex3f;
1806                                         m.texmatrix[1] = *matrix_modeltolight;
1807 #else
1808                                         m.pointer_texcoord3f[1] = varray_texcoord3f[1];
1809                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1810 #endif
1811                                 }
1812                                 R_Mesh_State(&m);
1813                                 GL_LockArrays(0, numverts);
1814                                 GL_ColorMask(1,1,1,0);
1815                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1816                                 VectorScale(lightcolor, colorscale, color2);
1817                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1818                                 {
1819                                         color[0] = bound(0, color2[0], 1);
1820                                         color[1] = bound(0, color2[1], 1);
1821                                         color[2] = bound(0, color2[2], 1);
1822                                         GL_Color(color[0], color[1], color[2], 1);
1823                                         R_Mesh_Draw(numverts, numtriangles, elements);
1824                                         c_rt_lightmeshes++;
1825                                         c_rt_lighttris += numtriangles;
1826                                 }
1827                                 GL_LockArrays(0, 0);
1828                         }
1829                 }
1830         }
1831         else
1832         {
1833                 if (lighting & LIGHTING_DIFFUSE)
1834                 {
1835                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1836                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1837                         memset(&m, 0, sizeof(m));
1838                         m.pointer_vertex = vertex3f;
1839                         m.pointer_color = varray_color4f;
1840                         m.tex[0] = R_GetTexture(basetexture);
1841                         m.pointer_texcoord[0] = texcoord2f;
1842                         if (r_textureunits.integer >= 2)
1843                         {
1844                                 // voodoo2
1845                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1846 #if USETEXMATRIX
1847                                 m.pointer_texcoord3f[1] = vertex3f;
1848                                 m.texmatrix[1] = *matrix_modeltoattenuationxyz;
1849 #else
1850                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1851                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1852 #endif
1853                         }
1854                         R_Mesh_State(&m);
1855                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1856                         {
1857                                 color[0] = bound(0, color2[0], 1);
1858                                 color[1] = bound(0, color2[1], 1);
1859                                 color[2] = bound(0, color2[2], 1);
1860                                 if (r_textureunits.integer >= 2)
1861                                         R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1862                                 else
1863                                         R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1864                                 GL_LockArrays(0, numverts);
1865                                 R_Mesh_Draw(numverts, numtriangles, elements);
1866                                 GL_LockArrays(0, 0);
1867                                 c_rt_lightmeshes++;
1868                                 c_rt_lighttris += numtriangles;
1869                         }
1870                 }
1871         }
1872 }
1873
1874 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1875 {
1876         int j, k;
1877         float scale;
1878         R_RTLight_Uncompile(rtlight);
1879         memset(rtlight, 0, sizeof(*rtlight));
1880
1881         VectorCopy(light->origin, rtlight->shadoworigin);
1882         VectorCopy(light->color, rtlight->color);
1883         rtlight->radius = light->radius;
1884         //rtlight->cullradius = rtlight->radius;
1885         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
1886         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1887         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1888         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1889         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1890         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1891         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1892         rtlight->cubemapname[0] = 0;
1893         if (light->cubemapname[0])
1894                 strcpy(rtlight->cubemapname, light->cubemapname);
1895         else if (light->cubemapnum > 0)
1896                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1897         rtlight->shadow = light->shadow;
1898         rtlight->corona = light->corona;
1899         rtlight->style = light->style;
1900         rtlight->isstatic = isstatic;
1901         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1902         // ConcatScale won't work here because this needs to scale rotate and
1903         // translate, not just rotate
1904         scale = 1.0f / rtlight->radius;
1905         for (k = 0;k < 3;k++)
1906                 for (j = 0;j < 4;j++)
1907                         rtlight->matrix_worldtolight.m[k][j] *= scale;
1908         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1909         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1910
1911         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1912         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1913         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.125f, rtlight->lightmap_light);
1914         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1915 }
1916
1917 rtlight_t *r_shadow_compilingrtlight;
1918
1919 // compiles rtlight geometry
1920 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1921 void R_RTLight_Compile(rtlight_t *rtlight)
1922 {
1923         int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
1924         entity_render_t *ent = &cl_entities[0].render;
1925         model_t *model = ent->model;
1926
1927         // compile the light
1928         rtlight->compiled = true;
1929         rtlight->static_numclusters = 0;
1930         rtlight->static_numclusterpvsbytes = 0;
1931         rtlight->static_clusterlist = NULL;
1932         rtlight->static_clusterpvs = NULL;
1933         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1934         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1935         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1936         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1937         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1938         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1939
1940         if (model && model->GetLightInfo)
1941         {
1942                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
1943                 r_shadow_compilingrtlight = rtlight;
1944                 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
1945                 R_Shadow_EnlargeSurfaceBuffer(model->numsurfaces); 
1946                 model->GetLightInfo(ent, rtlight->shadoworigin, rtlight->radius, rtlight->cullmins, rtlight->cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
1947                 if (numclusters)
1948                 {
1949                         rtlight->static_numclusters = numclusters;
1950                         rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
1951                         rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1952                         rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
1953                         memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1954                         memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
1955                 }
1956                 if (model->DrawShadowVolume && rtlight->shadow)
1957                 {
1958                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1959                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
1960                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1961                 }
1962                 if (model->DrawLight)
1963                 {
1964                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1965                         model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, numsurfaces, r_shadow_buffer_surfacelist);
1966                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1967                 }
1968                 // switch back to rendering when DrawShadowVolume or DrawLight is called
1969                 r_shadow_compilingrtlight = NULL;
1970         }
1971
1972
1973         // use smallest available cullradius - box radius or light radius
1974         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1975         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1976
1977         shadowmeshes = 0;
1978         shadowtris = 0;
1979         if (rtlight->static_meshchain_shadow)
1980         {
1981                 shadowmesh_t *mesh;
1982                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1983                 {
1984                         shadowmeshes++;
1985                         shadowtris += mesh->numtriangles;
1986                 }
1987         }
1988
1989         lightmeshes = 0;
1990         lighttris = 0;
1991         if (rtlight->static_meshchain_light)
1992         {
1993                 shadowmesh_t *mesh;
1994                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
1995                 {
1996                         lightmeshes++;
1997                         lighttris += mesh->numtriangles;
1998                 }
1999         }
2000
2001         Con_DPrintf("static light built: %f %f %f : %f %f %f box, %i shadow volume triangles (in %i meshes), %i light 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, lighttris, lightmeshes);
2002 }
2003
2004 void R_RTLight_Uncompile(rtlight_t *rtlight)
2005 {
2006         if (rtlight->compiled)
2007         {
2008                 if (rtlight->static_meshchain_shadow)
2009                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
2010                 rtlight->static_meshchain_shadow = NULL;
2011                 if (rtlight->static_meshchain_light)
2012                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
2013                 rtlight->static_meshchain_light = NULL;
2014                 if (rtlight->static_clusterlist)
2015                         Mem_Free(rtlight->static_clusterlist);
2016                 rtlight->static_clusterlist = NULL;
2017                 if (rtlight->static_clusterpvs)
2018                         Mem_Free(rtlight->static_clusterpvs);
2019                 rtlight->static_clusterpvs = NULL;
2020                 rtlight->static_numclusters = 0;
2021                 rtlight->static_numclusterpvsbytes = 0;
2022                 rtlight->compiled = false;
2023         }
2024 }
2025
2026 int shadowframecount = 0;
2027
2028 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
2029
2030 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
2031 {
2032         int i, shadow;
2033         entity_render_t *ent;
2034         float f;
2035         vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
2036         rtexture_t *cubemaptexture;
2037         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
2038         int numclusters, numsurfaces;
2039         int *clusterlist, *surfacelist;
2040         qbyte *clusterpvs;
2041         vec3_t cullmins, cullmaxs;
2042         shadowmesh_t *mesh;
2043         rmeshstate_t m;
2044
2045         if (d_lightstylevalue[rtlight->style] <= 0)
2046                 return;
2047         cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
2048         cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
2049         cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
2050         cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
2051         cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
2052         cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
2053         if (R_CullBox(cullmins, cullmaxs))
2054                 return;
2055         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
2056                 R_RTLight_Compile(rtlight);
2057         numclusters = 0;
2058         clusterlist = NULL;
2059         clusterpvs = NULL;
2060         numsurfaces = 0;
2061         surfacelist = NULL;
2062         if (rtlight->compiled && r_shadow_staticworldlights.integer)
2063         {
2064                 numclusters = rtlight->static_numclusters;
2065                 clusterlist = rtlight->static_clusterlist;
2066                 clusterpvs = rtlight->static_clusterpvs;
2067                 VectorCopy(rtlight->cullmins, cullmins);
2068                 VectorCopy(rtlight->cullmaxs, cullmaxs);
2069         }
2070         else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
2071         {
2072                 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
2073                 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->numsurfaces); 
2074                 cl.worldmodel->GetLightInfo(&cl_entities[0].render, rtlight->shadoworigin, rtlight->radius, cullmins, cullmaxs, r_shadow_buffer_clusterlist, r_shadow_buffer_clusterpvs, &numclusters, r_shadow_buffer_surfacelist, r_shadow_buffer_surfacepvs, &numsurfaces);
2075                 clusterlist = r_shadow_buffer_clusterlist;
2076                 clusterpvs = r_shadow_buffer_clusterpvs;
2077                 surfacelist = r_shadow_buffer_surfacelist;
2078         }
2079         if (numclusters)
2080         {
2081                 for (i = 0;i < numclusters;i++)
2082                         if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
2083                                 break;
2084                 if (i == numclusters)
2085                         return;
2086         }
2087         if (R_CullBox(cullmins, cullmaxs))
2088                 return;
2089         if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
2090                 return;
2091
2092         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2093         VectorScale(rtlight->color, f, lightcolor);
2094         /*
2095         if (rtlight->selected)
2096         {
2097                 f = 2 + sin(realtime * M_PI * 4.0);
2098                 VectorScale(lightcolor, f, lightcolor);
2099         }
2100         */
2101
2102         if (rtlight->cubemapname[0])
2103                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2104         else
2105                 cubemaptexture = NULL;
2106
2107         shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2108         if (shadow && (gl_stencil || visiblevolumes))
2109         {
2110                 if (!visiblevolumes)
2111                         R_Shadow_Stage_ShadowVolumes();
2112                 ent = &cl_entities[0].render;
2113                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2114                 {
2115                         memset(&m, 0, sizeof(m));
2116                         R_Mesh_Matrix(&ent->matrix);
2117                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2118                         {
2119                                 m.pointer_vertex = mesh->vertex3f;
2120                                 R_Mesh_State(&m);
2121                                 GL_LockArrays(0, mesh->numverts);
2122                                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2123                                 {
2124                                         // decrement stencil if frontface is behind depthbuffer
2125                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2126                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2127                                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2128                                         c_rtcached_shadowmeshes++;
2129                                         c_rtcached_shadowtris += mesh->numtriangles;
2130                                         // increment stencil if backface is behind depthbuffer
2131                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2132                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2133                                 }
2134                                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2135                                 c_rtcached_shadowmeshes++;
2136                                 c_rtcached_shadowtris += mesh->numtriangles;
2137                                 GL_LockArrays(0, 0);
2138                         }
2139                 }
2140                 else
2141                 {
2142                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2143                         ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2144                 }
2145                 if (r_drawentities.integer)
2146                 {
2147                         for (i = 0;i < r_refdef.numentities;i++)
2148                         {
2149                                 ent = r_refdef.entities[i];
2150                                 // rough checks
2151                                 if (r_shadow_cull.integer)
2152                                 {
2153                                         if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2154                                                 continue;
2155                                         if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2156                                                 continue;
2157                                 }
2158                                 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2159                                         continue;
2160                                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2161                                 // light emitting entities should not cast their own shadow
2162                                 if (VectorLength2(relativelightorigin) < 0.1)
2163                                         continue;
2164                                 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->numsurfaces, ent->model->surfacelist);
2165                         }
2166                 }
2167         }
2168
2169         if (!visiblevolumes)
2170         {
2171                 if (shadow && gl_stencil)
2172                         R_Shadow_Stage_LightWithShadows();
2173                 else
2174                         R_Shadow_Stage_LightWithoutShadows();
2175
2176                 ent = &cl_entities[0].render;
2177                 if (ent->model && ent->model->DrawLight)
2178                 {
2179                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2180                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2181                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2182                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2183                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2184                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2185                         {
2186                                 R_Mesh_Matrix(&ent->matrix);
2187                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2188                                         R_Shadow_RenderLighting(mesh->numverts, mesh->numtriangles, mesh->element3i, mesh->vertex3f, mesh->svector3f, mesh->tvector3f, mesh->normal3f, mesh->texcoord2f, relativelightorigin, relativeeyeorigin, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, mesh->map_diffuse, mesh->map_normal, mesh->map_specular, cubemaptexture, LIGHTING_DIFFUSE | LIGHTING_SPECULAR);
2189                         }
2190                         else
2191                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, numsurfaces, surfacelist);
2192                 }
2193                 if (r_drawentities.integer)
2194                 {
2195                         for (i = 0;i < r_refdef.numentities;i++)
2196                         {
2197                                 ent = r_refdef.entities[i];
2198                                 if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2199                                 {
2200                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2201                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2202                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2203                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2204                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2205                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->numsurfaces, ent->model->surfacelist);
2206                                 }
2207                         }
2208                 }
2209         }
2210 }
2211
2212 void R_ShadowVolumeLighting(int visiblevolumes)
2213 {
2214         int lnum;
2215         dlight_t *light;
2216         rmeshstate_t m;
2217
2218         if (visiblevolumes)
2219         {
2220                 memset(&m, 0, sizeof(m));
2221                 R_Mesh_State(&m);
2222
2223                 GL_BlendFunc(GL_ONE, GL_ONE);
2224                 GL_DepthMask(false);
2225                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2226                 qglDisable(GL_CULL_FACE);
2227                 GL_Color(0.0, 0.0125, 0.1, 1);
2228         }
2229         else
2230                 R_Shadow_Stage_Begin();
2231         shadowframecount++;
2232         if (r_shadow_realtime_world.integer)
2233         {
2234                 R_Shadow_LoadWorldLightsIfNeeded();
2235                 if (r_shadow_debuglight.integer >= 0)
2236                 {
2237                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2238                                 if (lnum == r_shadow_debuglight.integer)
2239                                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2240                 }
2241                 else
2242                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2243                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2244         }
2245         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2246                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2247                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2248
2249         if (visiblevolumes)
2250         {
2251                 qglEnable(GL_CULL_FACE);
2252                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2253         }
2254         else
2255                 R_Shadow_Stage_End();
2256 }
2257
2258 cvar_t r_editlights = {0, "r_editlights", "0"};
2259 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2260 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2261 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2262 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2263 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2264 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2265 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2266 dlight_t *r_shadow_worldlightchain;
2267 dlight_t *r_shadow_selectedlight;
2268 vec3_t r_editlights_cursorlocation;
2269
2270 typedef struct cubemapinfo_s
2271 {
2272         char basename[64];
2273         rtexture_t *texture;
2274 }
2275 cubemapinfo_t;
2276
2277 #define MAX_CUBEMAPS 128
2278 static int numcubemaps;
2279 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2280
2281 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2282 typedef struct suffixinfo_s
2283 {
2284         char *suffix;
2285         int flipx, flipy, flipdiagonal;
2286 }
2287 suffixinfo_t;
2288 static suffixinfo_t suffix[3][6] =
2289 {
2290         {
2291                 {"posx", false, false, false},
2292                 {"negx", false, false, false},
2293                 {"posy", false, false, false},
2294                 {"negy", false, false, false},
2295                 {"posz", false, false, false},
2296                 {"negz", false, false, false}
2297         },
2298         {
2299                 {"px", false, false, false},
2300                 {"nx", false, false, false},
2301                 {"py", false, false, false},
2302                 {"ny", false, false, false},
2303                 {"pz", false, false, false},
2304                 {"nz", false, false, false}
2305         },
2306         {
2307                 {"ft", true, false, true},
2308                 {"bk", false, true, true},
2309                 {"lf", true, true, false},
2310                 {"rt", false, false, false},
2311                 {"up", false, false, false},
2312                 {"dn", false, false, false}
2313         }
2314 };
2315
2316 static int componentorder[4] = {0, 1, 2, 3};
2317
2318 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2319 {
2320         int i, j, cubemapsize;
2321         qbyte *cubemappixels, *image_rgba;
2322         rtexture_t *cubemaptexture;
2323         char name[256];
2324         // must start 0 so the first loadimagepixels has no requested width/height
2325         cubemapsize = 0;
2326         cubemappixels = NULL;
2327         cubemaptexture = NULL;
2328         for (j = 0;j < 3 && !cubemappixels;j++)
2329         {
2330                 for (i = 0;i < 6;i++)
2331                 {
2332                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2333                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2334                         {
2335                                 if (image_width == image_height)
2336                                 {
2337                                         if (!cubemappixels && image_width >= 1)
2338                                         {
2339                                                 cubemapsize = image_width;
2340                                                 // note this clears to black, so unavailable sizes are black
2341                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2342                                         }
2343                                         if (cubemappixels)
2344                                                 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);
2345                                 }
2346                                 else
2347                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2348                                 Mem_Free(image_rgba);
2349                         }
2350                 }
2351         }
2352         if (cubemappixels)
2353         {
2354                 if (!r_shadow_filters_texturepool)
2355                         r_shadow_filters_texturepool = R_AllocTexturePool();
2356                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2357                 Mem_Free(cubemappixels);
2358         }
2359         else
2360         {
2361                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2362                 for (j = 0;j < 3;j++)
2363                         for (i = 0;i < 6;i++)
2364                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2365                 Con_Print(" and was unable to find any of them.\n");
2366         }
2367         return cubemaptexture;
2368 }
2369
2370 rtexture_t *R_Shadow_Cubemap(const char *basename)
2371 {
2372         int i;
2373         for (i = 0;i < numcubemaps;i++)
2374                 if (!strcasecmp(cubemaps[i].basename, basename))
2375                         return cubemaps[i].texture;
2376         if (i >= MAX_CUBEMAPS)
2377                 return NULL;
2378         numcubemaps++;
2379         strcpy(cubemaps[i].basename, basename);
2380         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2381         return cubemaps[i].texture;
2382 }
2383
2384 void R_Shadow_FreeCubemaps(void)
2385 {
2386         numcubemaps = 0;
2387         R_FreeTexturePool(&r_shadow_filters_texturepool);
2388 }
2389
2390 void R_Shadow_NewWorldLight(vec3_t origin, vec3_t angles, vec3_t color, vec_t radius, vec_t corona, int style, int shadowenable, const char *cubemapname)
2391 {
2392         dlight_t *light;
2393
2394         if (radius < 15 || DotProduct(color, color) < 0.03)
2395         {
2396                 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2397                 return;
2398         }
2399
2400         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2401         VectorCopy(origin, light->origin);
2402         VectorCopy(angles, light->angles);
2403         VectorCopy(color, light->color);
2404         light->radius = radius;
2405         light->style = style;
2406         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2407         {
2408                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2409                 light->style = 0;
2410         }
2411         light->shadow = shadowenable;
2412         light->corona = corona;
2413         if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2414                 strcpy(light->cubemapname, cubemapname);
2415         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2416         light->next = r_shadow_worldlightchain;
2417         r_shadow_worldlightchain = light;
2418
2419         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2420         if (r_shadow_staticworldlights.integer)
2421                 R_RTLight_Compile(&light->rtlight);
2422 }
2423
2424 void R_Shadow_FreeWorldLight(dlight_t *light)
2425 {
2426         dlight_t **lightpointer;
2427         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2428         if (*lightpointer != light)
2429                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2430         *lightpointer = light->next;
2431         R_RTLight_Uncompile(&light->rtlight);
2432         Mem_Free(light);
2433 }
2434
2435 void R_Shadow_ClearWorldLights(void)
2436 {
2437         while (r_shadow_worldlightchain)
2438                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2439         r_shadow_selectedlight = NULL;
2440         R_Shadow_FreeCubemaps();
2441 }
2442
2443 void R_Shadow_SelectLight(dlight_t *light)
2444 {
2445         if (r_shadow_selectedlight)
2446                 r_shadow_selectedlight->selected = false;
2447         r_shadow_selectedlight = light;
2448         if (r_shadow_selectedlight)
2449                 r_shadow_selectedlight->selected = true;
2450 }
2451
2452 rtexture_t *lighttextures[5];
2453
2454 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2455 {
2456         float scale = r_editlights_cursorgrid.value * 0.5f;
2457         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, r_viewright, r_viewup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2458 }
2459
2460 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2461 {
2462         float intensity;
2463         const dlight_t *light;
2464         light = calldata1;
2465         intensity = 0.5;
2466         if (light->selected)
2467                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2468         if (!light->shadow)
2469                 intensity *= 0.5f;
2470         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, r_viewright, r_viewup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2471 }
2472
2473 void R_Shadow_DrawLightSprites(void)
2474 {
2475         int i;
2476         cachepic_t *pic;
2477         dlight_t *light;
2478
2479         for (i = 0;i < 5;i++)
2480         {
2481                 lighttextures[i] = NULL;
2482                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2483                         lighttextures[i] = pic->tex;
2484         }
2485
2486         for (light = r_shadow_worldlightchain;light;light = light->next)
2487                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2488         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2489 }
2490
2491 void R_Shadow_SelectLightInView(void)
2492 {
2493         float bestrating, rating, temp[3];
2494         dlight_t *best, *light;
2495         best = NULL;
2496         bestrating = 0;
2497         for (light = r_shadow_worldlightchain;light;light = light->next)
2498         {
2499                 VectorSubtract(light->origin, r_vieworigin, temp);
2500                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2501                 if (rating >= 0.95)
2502                 {
2503                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2504                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2505                         {
2506                                 bestrating = rating;
2507                                 best = light;
2508                         }
2509                 }
2510         }
2511         R_Shadow_SelectLight(best);
2512 }
2513
2514 void R_Shadow_LoadWorldLights(void)
2515 {
2516         int n, a, style, shadow;
2517         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2518         float origin[3], radius, color[3], angles[3], corona;
2519         if (cl.worldmodel == NULL)
2520         {
2521                 Con_Print("No map loaded.\n");
2522                 return;
2523         }
2524         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2525         strlcat (name, ".rtlights", sizeof (name));
2526         lightsstring = FS_LoadFile(name, false);
2527         if (lightsstring)
2528         {
2529                 s = lightsstring;
2530                 n = 0;
2531                 while (*s)
2532                 {
2533                         t = s;
2534                         /*
2535                         shadow = true;
2536                         for (;COM_Parse(t, true) && strcmp(
2537                         if (COM_Parse(t, true))
2538                         {
2539                                 if (com_token[0] == '!')
2540                                 {
2541                                         shadow = false;
2542                                         origin[0] = atof(com_token+1);
2543                                 }
2544                                 else
2545                                         origin[0] = atof(com_token);
2546                                 if (Com_Parse(t
2547                         }
2548                         */
2549                         t = s;
2550                         while (*s && *s != '\n')
2551                                 s++;
2552                         if (!*s)
2553                                 break;
2554                         *s = 0;
2555                         shadow = true;
2556                         // check for modifier flags
2557                         if (*t == '!')
2558                         {
2559                                 shadow = false;
2560                                 t++;
2561                         }
2562                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s %f %f %f %f", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname, &corona, &angles[0], &angles[1], &angles[2]);
2563                         if (a < 13)
2564                                 VectorClear(angles);
2565                         if (a < 10)
2566                                 corona = 0;
2567                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2568                                 cubemapname[0] = 0;
2569                         *s = '\n';
2570                         if (a < 8)
2571                         {
2572                                 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])\n", a, n + 1);
2573                                 break;
2574                         }
2575                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2576                         radius *= r_editlights_rtlightssizescale.value;
2577                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2578                         s++;
2579                         n++;
2580                 }
2581                 if (*s)
2582                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2583                 Mem_Free(lightsstring);
2584         }
2585 }
2586
2587 void R_Shadow_SaveWorldLights(void)
2588 {
2589         dlight_t *light;
2590         int bufchars, bufmaxchars;
2591         char *buf, *oldbuf;
2592         char name[MAX_QPATH];
2593         char line[1024];
2594         if (!r_shadow_worldlightchain)
2595                 return;
2596         if (cl.worldmodel == NULL)
2597         {
2598                 Con_Print("No map loaded.\n");
2599                 return;
2600         }
2601         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2602         strlcat (name, ".rtlights", sizeof (name));
2603         bufchars = bufmaxchars = 0;
2604         buf = NULL;
2605         for (light = r_shadow_worldlightchain;light;light = light->next)
2606         {
2607                 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 / r_editlights_rtlightssizescale.value, light->color[0] / r_editlights_rtlightscolorscale.value, light->color[1] / r_editlights_rtlightscolorscale.value, light->color[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname[0] ? light->cubemapname : "\"\"", light->corona, light->angles[0], light->angles[1], light->angles[2]);
2608                 if (bufchars + (int) strlen(line) > bufmaxchars)
2609                 {
2610                         bufmaxchars = bufchars + strlen(line) + 2048;
2611                         oldbuf = buf;
2612                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2613                         if (oldbuf)
2614                         {
2615                                 if (bufchars)
2616                                         memcpy(buf, oldbuf, bufchars);
2617                                 Mem_Free(oldbuf);
2618                         }
2619                 }
2620                 if (strlen(line))
2621                 {
2622                         memcpy(buf + bufchars, line, strlen(line));
2623                         bufchars += strlen(line);
2624                 }
2625         }
2626         if (bufchars)
2627                 FS_WriteFile(name, buf, bufchars);
2628         if (buf)
2629                 Mem_Free(buf);
2630 }
2631
2632 void R_Shadow_LoadLightsFile(void)
2633 {
2634         int n, a, style;
2635         char name[MAX_QPATH], *lightsstring, *s, *t;
2636         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2637         if (cl.worldmodel == NULL)
2638         {
2639                 Con_Print("No map loaded.\n");
2640                 return;
2641         }
2642         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2643         strlcat (name, ".lights", sizeof (name));
2644         lightsstring = FS_LoadFile(name, false);
2645         if (lightsstring)
2646         {
2647                 s = lightsstring;
2648                 n = 0;
2649                 while (*s)
2650                 {
2651                         t = s;
2652                         while (*s && *s != '\n')
2653                                 s++;
2654                         if (!*s)
2655                                 break;
2656                         *s = 0;
2657                         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);
2658                         *s = '\n';
2659                         if (a < 14)
2660                         {
2661                                 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);
2662                                 break;
2663                         }
2664                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2665                         radius = bound(15, radius, 4096);
2666                         VectorScale(color, (2.0f / (8388608.0f)), color);
2667                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2668                         s++;
2669                         n++;
2670                 }
2671                 if (*s)
2672                         Con_Printf("invalid lights file \"%s\"\n", name);
2673                 Mem_Free(lightsstring);
2674         }
2675 }
2676
2677 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2678 {
2679         int entnum, style, islight, skin, pflags, effects;
2680         char key[256], value[1024];
2681         float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2682         const char *data;
2683
2684         if (cl.worldmodel == NULL)
2685         {
2686                 Con_Print("No map loaded.\n");
2687                 return;
2688         }
2689         data = cl.worldmodel->brush.entities;
2690         if (!data)
2691                 return;
2692         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2693         {
2694                 light = 0;
2695                 origin[0] = origin[1] = origin[2] = 0;
2696                 originhack[0] = originhack[1] = originhack[2] = 0;
2697                 angles[0] = angles[1] = angles[2] = 0;
2698                 color[0] = color[1] = color[2] = 1;
2699                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2700                 fadescale = 1;
2701                 lightscale = 1;
2702                 style = 0;
2703                 skin = 0;
2704                 pflags = 0;
2705                 effects = 0;
2706                 islight = false;
2707                 while (1)
2708                 {
2709                         if (!COM_ParseToken(&data, false))
2710                                 break; // error
2711                         if (com_token[0] == '}')
2712                                 break; // end of entity
2713                         if (com_token[0] == '_')
2714                                 strcpy(key, com_token + 1);
2715                         else
2716                                 strcpy(key, com_token);
2717                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2718                                 key[strlen(key)-1] = 0;
2719                         if (!COM_ParseToken(&data, false))
2720                                 break; // error
2721                         strcpy(value, com_token);
2722
2723                         // now that we have the key pair worked out...
2724                         if (!strcmp("light", key))
2725                                 light = atof(value);
2726                         else if (!strcmp("origin", key))
2727                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2728                         else if (!strcmp("angle", key))
2729                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2730                         else if (!strcmp("angles", key))
2731                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2732                         else if (!strcmp("color", key))
2733                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2734                         else if (!strcmp("wait", key))
2735                                 fadescale = atof(value);
2736                         else if (!strcmp("classname", key))
2737                         {
2738                                 if (!strncmp(value, "light", 5))
2739                                 {
2740                                         islight = true;
2741                                         if (!strcmp(value, "light_fluoro"))
2742                                         {
2743                                                 originhack[0] = 0;
2744                                                 originhack[1] = 0;
2745                                                 originhack[2] = 0;
2746                                                 overridecolor[0] = 1;
2747                                                 overridecolor[1] = 1;
2748                                                 overridecolor[2] = 1;
2749                                         }
2750                                         if (!strcmp(value, "light_fluorospark"))
2751                                         {
2752                                                 originhack[0] = 0;
2753                                                 originhack[1] = 0;
2754                                                 originhack[2] = 0;
2755                                                 overridecolor[0] = 1;
2756                                                 overridecolor[1] = 1;
2757                                                 overridecolor[2] = 1;
2758                                         }
2759                                         if (!strcmp(value, "light_globe"))
2760                                         {
2761                                                 originhack[0] = 0;
2762                                                 originhack[1] = 0;
2763                                                 originhack[2] = 0;
2764                                                 overridecolor[0] = 1;
2765                                                 overridecolor[1] = 0.8;
2766                                                 overridecolor[2] = 0.4;
2767                                         }
2768                                         if (!strcmp(value, "light_flame_large_yellow"))
2769                                         {
2770                                                 originhack[0] = 0;
2771                                                 originhack[1] = 0;
2772                                                 originhack[2] = 48;
2773                                                 overridecolor[0] = 1;
2774                                                 overridecolor[1] = 0.5;
2775                                                 overridecolor[2] = 0.1;
2776                                         }
2777                                         if (!strcmp(value, "light_flame_small_yellow"))
2778                                         {
2779                                                 originhack[0] = 0;
2780                                                 originhack[1] = 0;
2781                                                 originhack[2] = 40;
2782                                                 overridecolor[0] = 1;
2783                                                 overridecolor[1] = 0.5;
2784                                                 overridecolor[2] = 0.1;
2785                                         }
2786                                         if (!strcmp(value, "light_torch_small_white"))
2787                                         {
2788                                                 originhack[0] = 0;
2789                                                 originhack[1] = 0;
2790                                                 originhack[2] = 40;
2791                                                 overridecolor[0] = 1;
2792                                                 overridecolor[1] = 0.5;
2793                                                 overridecolor[2] = 0.1;
2794                                         }
2795                                         if (!strcmp(value, "light_torch_small_walltorch"))
2796                                         {
2797                                                 originhack[0] = 0;
2798                                                 originhack[1] = 0;
2799                                                 originhack[2] = 40;
2800                                                 overridecolor[0] = 1;
2801                                                 overridecolor[1] = 0.5;
2802                                                 overridecolor[2] = 0.1;
2803                                         }
2804                                 }
2805                         }
2806                         else if (!strcmp("style", key))
2807                                 style = atoi(value);
2808                         else if (cl.worldmodel->type == mod_brushq3)
2809                         {
2810                                 if (!strcmp("scale", key))
2811                                         lightscale = atof(value);
2812                                 if (!strcmp("fade", key))
2813                                         fadescale = atof(value);
2814                         }
2815                         else if (!strcmp("skin", key))
2816                                 skin = (int)atof(value);
2817                         else if (!strcmp("pflags", key))
2818                                 pflags = (int)atof(value);
2819                         else if (!strcmp("effects", key))
2820                                 effects = (int)atof(value);
2821                 }
2822                 if (light <= 0 && islight)
2823                         light = 300;
2824                 if (lightscale <= 0)
2825                         lightscale = 1;
2826                 if (fadescale <= 0)
2827                         fadescale = 1;
2828                 if (gamemode == GAME_TENEBRAE)
2829                 {
2830                         if (effects & EF_NODRAW)
2831                         {
2832                                 pflags |= PFLAGS_FULLDYNAMIC;
2833                                 effects &= ~EF_NODRAW;
2834                         }
2835                 }
2836                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2837                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2838                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2839                         VectorCopy(overridecolor, color);
2840                 VectorScale(color, light, color);
2841                 VectorAdd(origin, originhack, origin);
2842                 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2843                         R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2844         }
2845 }
2846
2847
2848 void R_Shadow_SetCursorLocationForView(void)
2849 {
2850         vec_t dist, push, frac;
2851         vec3_t dest, endpos, normal;
2852         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2853         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2854         if (frac < 1)
2855         {
2856                 dist = frac * r_editlights_cursordistance.value;
2857                 push = r_editlights_cursorpushback.value;
2858                 if (push > dist)
2859                         push = dist;
2860                 push = -push;
2861                 VectorMA(endpos, push, r_viewforward, endpos);
2862                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2863         }
2864         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2865         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2866         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2867 }
2868
2869 void R_Shadow_UpdateWorldLightSelection(void)
2870 {
2871         if (r_editlights.integer)
2872         {
2873                 R_Shadow_SetCursorLocationForView();
2874                 R_Shadow_SelectLightInView();
2875                 R_Shadow_DrawLightSprites();
2876         }
2877         else
2878                 R_Shadow_SelectLight(NULL);
2879 }
2880
2881 void R_Shadow_EditLights_Clear_f(void)
2882 {
2883         R_Shadow_ClearWorldLights();
2884 }
2885
2886 void R_Shadow_EditLights_Reload_f(void)
2887 {
2888         r_shadow_reloadlights = true;
2889 }
2890
2891 void R_Shadow_EditLights_Save_f(void)
2892 {
2893         if (cl.worldmodel)
2894                 R_Shadow_SaveWorldLights();
2895 }
2896
2897 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2898 {
2899         R_Shadow_ClearWorldLights();
2900         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2901 }
2902
2903 void R_Shadow_EditLights_ImportLightsFile_f(void)
2904 {
2905         R_Shadow_ClearWorldLights();
2906         R_Shadow_LoadLightsFile();
2907 }
2908
2909 void R_Shadow_EditLights_Spawn_f(void)
2910 {
2911         vec3_t color;
2912         if (!r_editlights.integer)
2913         {
2914                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2915                 return;
2916         }
2917         if (Cmd_Argc() != 1)
2918         {
2919                 Con_Print("r_editlights_spawn does not take parameters\n");
2920                 return;
2921         }
2922         color[0] = color[1] = color[2] = 1;
2923         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2924 }
2925
2926 void R_Shadow_EditLights_Edit_f(void)
2927 {
2928         vec3_t origin, angles, color;
2929         vec_t radius, corona;
2930         int style, shadows;
2931         char cubemapname[1024];
2932         if (!r_editlights.integer)
2933         {
2934                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2935                 return;
2936         }
2937         if (!r_shadow_selectedlight)
2938         {
2939                 Con_Print("No selected light.\n");
2940                 return;
2941         }
2942         VectorCopy(r_shadow_selectedlight->origin, origin);
2943         VectorCopy(r_shadow_selectedlight->angles, angles);
2944         VectorCopy(r_shadow_selectedlight->color, color);
2945         radius = r_shadow_selectedlight->radius;
2946         style = r_shadow_selectedlight->style;
2947         if (r_shadow_selectedlight->cubemapname)
2948                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2949         else
2950                 cubemapname[0] = 0;
2951         shadows = r_shadow_selectedlight->shadow;
2952         corona = r_shadow_selectedlight->corona;
2953         if (!strcmp(Cmd_Argv(1), "origin"))
2954         {
2955                 if (Cmd_Argc() != 5)
2956                 {
2957                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2958                         return;
2959                 }
2960                 origin[0] = atof(Cmd_Argv(2));
2961                 origin[1] = atof(Cmd_Argv(3));
2962                 origin[2] = atof(Cmd_Argv(4));
2963         }
2964         else if (!strcmp(Cmd_Argv(1), "originx"))
2965         {
2966                 if (Cmd_Argc() != 3)
2967                 {
2968                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2969                         return;
2970                 }
2971                 origin[0] = atof(Cmd_Argv(2));
2972         }
2973         else if (!strcmp(Cmd_Argv(1), "originy"))
2974         {
2975                 if (Cmd_Argc() != 3)
2976                 {
2977                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2978                         return;
2979                 }
2980                 origin[1] = atof(Cmd_Argv(2));
2981         }
2982         else if (!strcmp(Cmd_Argv(1), "originz"))
2983         {
2984                 if (Cmd_Argc() != 3)
2985                 {
2986                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2987                         return;
2988                 }
2989                 origin[2] = atof(Cmd_Argv(2));
2990         }
2991         else if (!strcmp(Cmd_Argv(1), "move"))
2992         {
2993                 if (Cmd_Argc() != 5)
2994                 {
2995                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2996                         return;
2997                 }
2998                 origin[0] += atof(Cmd_Argv(2));
2999                 origin[1] += atof(Cmd_Argv(3));
3000                 origin[2] += atof(Cmd_Argv(4));
3001         }
3002         else if (!strcmp(Cmd_Argv(1), "movex"))
3003         {
3004                 if (Cmd_Argc() != 3)
3005                 {
3006                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3007                         return;
3008                 }
3009                 origin[0] += atof(Cmd_Argv(2));
3010         }
3011         else if (!strcmp(Cmd_Argv(1), "movey"))
3012         {
3013                 if (Cmd_Argc() != 3)
3014                 {
3015                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3016                         return;
3017                 }
3018                 origin[1] += atof(Cmd_Argv(2));
3019         }
3020         else if (!strcmp(Cmd_Argv(1), "movez"))
3021         {
3022                 if (Cmd_Argc() != 3)
3023                 {
3024                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3025                         return;
3026                 }
3027                 origin[2] += atof(Cmd_Argv(2));
3028         }
3029         else if (!strcmp(Cmd_Argv(1), "angles"))
3030         {
3031                 if (Cmd_Argc() != 5)
3032                 {
3033                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
3034                         return;
3035                 }
3036                 angles[0] = atof(Cmd_Argv(2));
3037                 angles[1] = atof(Cmd_Argv(3));
3038                 angles[2] = atof(Cmd_Argv(4));
3039         }
3040         else if (!strcmp(Cmd_Argv(1), "anglesx"))
3041         {
3042                 if (Cmd_Argc() != 3)
3043                 {
3044                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3045                         return;
3046                 }
3047                 angles[0] = atof(Cmd_Argv(2));
3048         }
3049         else if (!strcmp(Cmd_Argv(1), "anglesy"))
3050         {
3051                 if (Cmd_Argc() != 3)
3052                 {
3053                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3054                         return;
3055                 }
3056                 angles[1] = atof(Cmd_Argv(2));
3057         }
3058         else if (!strcmp(Cmd_Argv(1), "anglesz"))
3059         {
3060                 if (Cmd_Argc() != 3)
3061                 {
3062                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3063                         return;
3064                 }
3065                 angles[2] = atof(Cmd_Argv(2));
3066         }
3067         else if (!strcmp(Cmd_Argv(1), "color"))
3068         {
3069                 if (Cmd_Argc() != 5)
3070                 {
3071                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
3072                         return;
3073                 }
3074                 color[0] = atof(Cmd_Argv(2));
3075                 color[1] = atof(Cmd_Argv(3));
3076                 color[2] = atof(Cmd_Argv(4));
3077         }
3078         else if (!strcmp(Cmd_Argv(1), "radius"))
3079         {
3080                 if (Cmd_Argc() != 3)
3081                 {
3082                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3083                         return;
3084                 }
3085                 radius = atof(Cmd_Argv(2));
3086         }
3087         else if (!strcmp(Cmd_Argv(1), "style"))
3088         {
3089                 if (Cmd_Argc() != 3)
3090                 {
3091                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3092                         return;
3093                 }
3094                 style = atoi(Cmd_Argv(2));
3095         }
3096         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3097         {
3098                 if (Cmd_Argc() > 3)
3099                 {
3100                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3101                         return;
3102                 }
3103                 if (Cmd_Argc() == 3)
3104                         strcpy(cubemapname, Cmd_Argv(2));
3105                 else
3106                         cubemapname[0] = 0;
3107         }
3108         else if (!strcmp(Cmd_Argv(1), "shadows"))
3109         {
3110                 if (Cmd_Argc() != 3)
3111                 {
3112                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3113                         return;
3114                 }
3115                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3116         }
3117         else if (!strcmp(Cmd_Argv(1), "corona"))
3118         {
3119                 if (Cmd_Argc() != 3)
3120                 {
3121                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3122                         return;
3123                 }
3124                 corona = atof(Cmd_Argv(2));
3125         }
3126         else
3127         {
3128                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3129                 Con_Print("Selected light's properties:\n");
3130                 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3131                 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3132                 Con_Printf("Color  : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3133                 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3134                 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3135                 Con_Printf("Style  : %i\n", r_shadow_selectedlight->style);
3136                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3137                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3138                 return;
3139         }
3140         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3141         r_shadow_selectedlight = NULL;
3142         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3143 }
3144
3145 extern int con_vislines;
3146 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3147 {
3148         float x, y;
3149         char temp[256];
3150         if (r_shadow_selectedlight == NULL)
3151                 return;
3152         x = 0;
3153         y = con_vislines;
3154         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3155         sprintf(temp, "Origin  %f %f %f", 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;
3156         sprintf(temp, "Angles  %f %f %f", 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;
3157         sprintf(temp, "Color   %f %f %f", 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;
3158         sprintf(temp, "Radius  %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3159         sprintf(temp, "Corona  %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3160         sprintf(temp, "Style   %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3161         sprintf(temp, "Shadows %s", r_shadow_selectedlight->shadow ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3162         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3163 }
3164
3165 void R_Shadow_EditLights_ToggleShadow_f(void)
3166 {
3167         if (!r_editlights.integer)
3168         {
3169                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3170                 return;
3171         }
3172         if (!r_shadow_selectedlight)
3173         {
3174                 Con_Print("No selected light.\n");
3175                 return;
3176         }
3177         R_Shadow_NewWorldLight(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);
3178         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3179         r_shadow_selectedlight = NULL;
3180 }
3181
3182 void R_Shadow_EditLights_ToggleCorona_f(void)
3183 {
3184         if (!r_editlights.integer)
3185         {
3186                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3187                 return;
3188         }
3189         if (!r_shadow_selectedlight)
3190         {
3191                 Con_Print("No selected light.\n");
3192                 return;
3193         }
3194         R_Shadow_NewWorldLight(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);
3195         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3196         r_shadow_selectedlight = NULL;
3197 }
3198
3199 void R_Shadow_EditLights_Remove_f(void)
3200 {
3201         if (!r_editlights.integer)
3202         {
3203                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3204                 return;
3205         }
3206         if (!r_shadow_selectedlight)
3207         {
3208                 Con_Print("No selected light.\n");
3209                 return;
3210         }
3211         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3212         r_shadow_selectedlight = NULL;
3213 }
3214
3215 void R_Shadow_EditLights_Help_f(void)
3216 {
3217         Con_Print(
3218 "Documentation on r_editlights system:\n"
3219 "Settings:\n"
3220 "r_editlights : enable/disable editing mode\n"
3221 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3222 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3223 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3224 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3225 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3226 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3227 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3228 "Commands:\n"
3229 "r_editlights_help : this help\n"
3230 "r_editlights_clear : remove all lights\n"
3231 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3232 "r_editlights_save : save to .rtlights file\n"
3233 "r_editlights_spawn : create a light with default settings\n"
3234 "r_editlights_edit command : edit selected light - more documentation below\n"
3235 "r_editlights_remove : remove selected light\n"
3236 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3237 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3238 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3239 "Edit commands:\n"
3240 "origin x y z : set light location\n"
3241 "originx x: set x component of light location\n"
3242 "originy y: set y component of light location\n"
3243 "originz z: set z component of light location\n"
3244 "move x y z : adjust light location\n"
3245 "movex x: adjust x component of light location\n"
3246 "movey y: adjust y component of light location\n"
3247 "movez z: adjust z component of light location\n"
3248 "angles x y z : set light angles\n"
3249 "anglesx x: set x component of light angles\n"
3250 "anglesy y: set y component of light angles\n"
3251 "anglesz z: set z component of light angles\n"
3252 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3253 "radius radius : set radius (size) of light\n"
3254 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3255 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3256 "shadows 1/0 : turn on/off shadows\n"
3257 "corona n : set corona intensity\n"
3258 "<nothing> : print light properties to console\n"
3259         );
3260 }
3261
3262 void R_Shadow_EditLights_Init(void)
3263 {
3264         Cvar_RegisterVariable(&r_editlights);
3265         Cvar_RegisterVariable(&r_editlights_cursordistance);
3266         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3267         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3268         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3269         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3270         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3271         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3272         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3273         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3274         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3275         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3276         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3277         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3278         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3279         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3280         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3281         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3282         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3283 }
3284