]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
2c077ffb8215db91eb2647131450faf4bfa969d3
[xonotic/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is 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", "10000"};
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.0},
356                 {0.0, 0.0, 0.0, 0.0},
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 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)
1218 {
1219         int renders;
1220         float color[3], color2[3], colorscale;
1221         rmeshstate_t m;
1222         if (!bumptexture)
1223                 bumptexture = r_shadow_blankbumptexture;
1224         if (!glosstexture)
1225                 glosstexture = r_shadow_blankglosstexture;
1226         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1227         {
1228                 if (lighting & LIGHTING_DIFFUSE)
1229                 {
1230                         GL_Color(1,1,1,1);
1231                         // colorscale accounts for how much we multiply the brightness during combine
1232                         // mult is how many times the final pass of the lighting will be
1233                         // performed to get more brightness than otherwise possible
1234                         // limit mult to 64 for sanity sake
1235                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1236                         {
1237                                 // 3/2 3D combine path (Geforce3, Radeon 8500)
1238                                 memset(&m, 0, sizeof(m));
1239                                 m.pointer_vertex = vertex3f;
1240                                 m.tex[0] = R_GetTexture(bumptexture);
1241                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1242                                 m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1243                                 m.texcombinergb[0] = GL_REPLACE;
1244                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1245                                 m.pointer_texcoord[0] = texcoord2f;
1246                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1247                                 m.pointer_texcoord[2] = varray_texcoord3f[2];
1248                                 R_Mesh_State(&m);
1249                                 GL_ColorMask(0,0,0,1);
1250                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1251                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1252                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1253                                 GL_LockArrays(0, numverts);
1254                                 R_Mesh_Draw(numverts, numtriangles, elements);
1255                                 GL_LockArrays(0, 0);
1256                                 c_rt_lightmeshes++;
1257                                 c_rt_lighttris += numtriangles;
1258         
1259                                 memset(&m, 0, sizeof(m));
1260                                 m.pointer_vertex = vertex3f;
1261                                 m.tex[0] = R_GetTexture(basetexture);
1262                                 m.pointer_texcoord[0] = texcoord2f;
1263                                 if (lightcubemap)
1264                                 {
1265                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1266                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1267                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1268                                 }
1269                                 R_Mesh_State(&m);
1270                                 GL_LockArrays(0, numverts);
1271                                 GL_ColorMask(1,1,1,0);
1272                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1273                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1274                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1275                                 {
1276                                         color[0] = bound(0, color2[0], 1);
1277                                         color[1] = bound(0, color2[1], 1);
1278                                         color[2] = bound(0, color2[2], 1);
1279                                         GL_Color(color[0], color[1], color[2], 1);
1280                                         R_Mesh_Draw(numverts, numtriangles, elements);
1281                                         c_rt_lightmeshes++;
1282                                         c_rt_lighttris += numtriangles;
1283                                 }
1284                                 GL_LockArrays(0, 0);
1285                         }
1286                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1287                         {
1288                                 // 1/2/2 3D combine path (original Radeon)
1289                                 memset(&m, 0, sizeof(m));
1290                                 m.pointer_vertex = vertex3f;
1291                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1292                                 m.pointer_texcoord[0] = varray_texcoord3f[0];
1293                                 R_Mesh_State(&m);
1294                                 GL_ColorMask(0,0,0,1);
1295                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1296                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1297                                 GL_LockArrays(0, numverts);
1298                                 R_Mesh_Draw(numverts, numtriangles, elements);
1299                                 GL_LockArrays(0, 0);
1300                                 c_rt_lightmeshes++;
1301                                 c_rt_lighttris += numtriangles;
1302         
1303                                 memset(&m, 0, sizeof(m));
1304                                 m.pointer_vertex = vertex3f;
1305                                 m.tex[0] = R_GetTexture(bumptexture);
1306                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1307                                 m.texcombinergb[0] = GL_REPLACE;
1308                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1309                                 m.pointer_texcoord[0] = texcoord2f;
1310                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1311                                 R_Mesh_State(&m);
1312                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1313                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1314                                 GL_LockArrays(0, numverts);
1315                                 R_Mesh_Draw(numverts, numtriangles, elements);
1316                                 GL_LockArrays(0, 0);
1317                                 c_rt_lightmeshes++;
1318                                 c_rt_lighttris += numtriangles;
1319         
1320                                 memset(&m, 0, sizeof(m));
1321                                 m.pointer_vertex = vertex3f;
1322                                 m.tex[0] = R_GetTexture(basetexture);
1323                                 m.pointer_texcoord[0] = texcoord2f;
1324                                 if (lightcubemap)
1325                                 {
1326                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1327                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1328                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1329                                 }
1330                                 R_Mesh_State(&m);
1331                                 GL_LockArrays(0, numverts);
1332                                 GL_ColorMask(1,1,1,0);
1333                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1334                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1335                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1336                                 {
1337                                         color[0] = bound(0, color2[0], 1);
1338                                         color[1] = bound(0, color2[1], 1);
1339                                         color[2] = bound(0, color2[2], 1);
1340                                         GL_Color(color[0], color[1], color[2], 1);
1341                                         R_Mesh_Draw(numverts, numtriangles, elements);
1342                                         c_rt_lightmeshes++;
1343                                         c_rt_lighttris += numtriangles;
1344                                 }
1345                                 GL_LockArrays(0, 0);
1346                         }
1347                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1348                         {
1349                                 // 2/2 3D combine path (original Radeon)
1350                                 memset(&m, 0, sizeof(m));
1351                                 m.pointer_vertex = vertex3f;
1352                                 m.tex[0] = R_GetTexture(bumptexture);
1353                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1354                                 m.texcombinergb[0] = GL_REPLACE;
1355                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1356                                 m.pointer_texcoord[0] = texcoord2f;
1357                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1358                                 R_Mesh_State(&m);
1359                                 GL_ColorMask(0,0,0,1);
1360                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1361                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1362                                 GL_LockArrays(0, numverts);
1363                                 R_Mesh_Draw(numverts, numtriangles, elements);
1364                                 GL_LockArrays(0, 0);
1365                                 c_rt_lightmeshes++;
1366                                 c_rt_lighttris += numtriangles;
1367         
1368                                 memset(&m, 0, sizeof(m));
1369                                 m.pointer_vertex = vertex3f;
1370                                 m.tex[0] = R_GetTexture(basetexture);
1371                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1372                                 m.pointer_texcoord[0] = texcoord2f;
1373                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1374                                 R_Mesh_State(&m);
1375                                 GL_LockArrays(0, numverts);
1376                                 GL_ColorMask(1,1,1,0);
1377                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1378                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1379                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1380                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1381                                 {
1382                                         color[0] = bound(0, color2[0], 1);
1383                                         color[1] = bound(0, color2[1], 1);
1384                                         color[2] = bound(0, color2[2], 1);
1385                                         GL_Color(color[0], color[1], color[2], 1);
1386                                         R_Mesh_Draw(numverts, numtriangles, elements);
1387                                         c_rt_lightmeshes++;
1388                                         c_rt_lighttris += numtriangles;
1389                                 }
1390                                 GL_LockArrays(0, 0);
1391                         }
1392                         else if (r_textureunits.integer >= 4)
1393                         {
1394                                 // 4/2 2D combine path (Geforce3, Radeon 8500)
1395                                 memset(&m, 0, sizeof(m));
1396                                 m.pointer_vertex = vertex3f;
1397                                 m.tex[0] = R_GetTexture(bumptexture);
1398                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1399                                 m.texcombinergb[0] = GL_REPLACE;
1400                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1401                                 m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1402                                 m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1403                                 m.pointer_texcoord[0] = texcoord2f;
1404                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1405                                 m.pointer_texcoord[2] = varray_texcoord2f[2];
1406                                 m.pointer_texcoord[3] = varray_texcoord2f[3];
1407                                 R_Mesh_State(&m);
1408                                 GL_ColorMask(0,0,0,1);
1409                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1410                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1411                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1412                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1413                                 GL_LockArrays(0, numverts);
1414                                 R_Mesh_Draw(numverts, numtriangles, elements);
1415                                 GL_LockArrays(0, 0);
1416                                 c_rt_lightmeshes++;
1417                                 c_rt_lighttris += numtriangles;
1418         
1419                                 memset(&m, 0, sizeof(m));
1420                                 m.pointer_vertex = vertex3f;
1421                                 m.tex[0] = R_GetTexture(basetexture);
1422                                 m.pointer_texcoord[0] = texcoord2f;
1423                                 if (lightcubemap)
1424                                 {
1425                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1426                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1427                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1428                                 }
1429                                 R_Mesh_State(&m);
1430                                 GL_LockArrays(0, numverts);
1431                                 GL_ColorMask(1,1,1,0);
1432                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1433                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1434                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1435                                 {
1436                                         color[0] = bound(0, color2[0], 1);
1437                                         color[1] = bound(0, color2[1], 1);
1438                                         color[2] = bound(0, color2[2], 1);
1439                                         GL_Color(color[0], color[1], color[2], 1);
1440                                         R_Mesh_Draw(numverts, numtriangles, elements);
1441                                         c_rt_lightmeshes++;
1442                                         c_rt_lighttris += numtriangles;
1443                                 }
1444                                 GL_LockArrays(0, 0);
1445                         }
1446                         else
1447                         {
1448                                 // 2/2/2 2D combine path (any dot3 card)
1449                                 memset(&m, 0, sizeof(m));
1450                                 m.pointer_vertex = vertex3f;
1451                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1452                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1453                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1454                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1455                                 R_Mesh_State(&m);
1456                                 GL_ColorMask(0,0,0,1);
1457                                 GL_BlendFunc(GL_ONE, GL_ZERO);
1458                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1459                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1460                                 GL_LockArrays(0, numverts);
1461                                 R_Mesh_Draw(numverts, numtriangles, elements);
1462                                 GL_LockArrays(0, 0);
1463                                 c_rt_lightmeshes++;
1464                                 c_rt_lighttris += numtriangles;
1465         
1466                                 memset(&m, 0, sizeof(m));
1467                                 m.pointer_vertex = vertex3f;
1468                                 m.tex[0] = R_GetTexture(bumptexture);
1469                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1470                                 m.texcombinergb[0] = GL_REPLACE;
1471                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1472                                 m.pointer_texcoord[0] = texcoord2f;
1473                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1474                                 R_Mesh_State(&m);
1475                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1476                                 R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1477                                 GL_LockArrays(0, numverts);
1478                                 R_Mesh_Draw(numverts, numtriangles, elements);
1479                                 GL_LockArrays(0, 0);
1480                                 c_rt_lightmeshes++;
1481                                 c_rt_lighttris += numtriangles;
1482         
1483                                 memset(&m, 0, sizeof(m));
1484                                 m.pointer_vertex = vertex3f;
1485                                 m.tex[0] = R_GetTexture(basetexture);
1486                                 m.pointer_texcoord[0] = texcoord2f;
1487                                 if (lightcubemap)
1488                                 {
1489                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1490                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1491                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1492                                 }
1493                                 R_Mesh_State(&m);
1494                                 GL_LockArrays(0, numverts);
1495                                 GL_ColorMask(1,1,1,0);
1496                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1497                                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1498                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1499                                 {
1500                                         color[0] = bound(0, color2[0], 1);
1501                                         color[1] = bound(0, color2[1], 1);
1502                                         color[2] = bound(0, color2[2], 1);
1503                                         GL_Color(color[0], color[1], color[2], 1);
1504                                         R_Mesh_Draw(numverts, numtriangles, elements);
1505                                         c_rt_lightmeshes++;
1506                                         c_rt_lighttris += numtriangles;
1507                                 }
1508                                 GL_LockArrays(0, 0);
1509                         }
1510                 }
1511                 if ((lighting & LIGHTING_SPECULAR) && (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture)))
1512                 {
1513                         colorscale = r_shadow_glossintensity.value;
1514                         if (glosstexture == r_shadow_blankglosstexture)
1515                                 colorscale *= r_shadow_gloss2intensity.value;
1516                         GL_Color(1,1,1,1);
1517                         if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1518                         {
1519                                 // 2/0/0/1/2 3D combine blendsquare path
1520                                 memset(&m, 0, sizeof(m));
1521                                 m.pointer_vertex = vertex3f;
1522                                 m.tex[0] = R_GetTexture(bumptexture);
1523                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1524                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1525                                 m.pointer_texcoord[0] = texcoord2f;
1526                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1527                                 R_Mesh_State(&m);
1528                                 GL_ColorMask(0,0,0,1);
1529                                 // this squares the result
1530                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1531                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1532                                 GL_LockArrays(0, numverts);
1533                                 R_Mesh_Draw(numverts, numtriangles, elements);
1534                                 GL_LockArrays(0, 0);
1535                                 c_rt_lightmeshes++;
1536                                 c_rt_lighttris += numtriangles;
1537         
1538                                 memset(&m, 0, sizeof(m));
1539                                 m.pointer_vertex = vertex3f;
1540                                 R_Mesh_State(&m);
1541                                 GL_LockArrays(0, numverts);
1542                                 // square alpha in framebuffer a few times to make it shiny
1543                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1544                                 // these comments are a test run through this math for intensity 0.5
1545                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1546                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1547                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1548                                 R_Mesh_Draw(numverts, numtriangles, elements);
1549                                 c_rt_lightmeshes++;
1550                                 c_rt_lighttris += numtriangles;
1551                                 R_Mesh_Draw(numverts, numtriangles, elements);
1552                                 c_rt_lightmeshes++;
1553                                 c_rt_lighttris += numtriangles;
1554                                 GL_LockArrays(0, 0);
1555         
1556                                 memset(&m, 0, sizeof(m));
1557                                 m.pointer_vertex = vertex3f;
1558                                 m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1559                                 m.pointer_texcoord[0] = varray_texcoord3f[0];
1560                                 R_Mesh_State(&m);
1561                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1562                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1563                                 GL_LockArrays(0, numverts);
1564                                 R_Mesh_Draw(numverts, numtriangles, elements);
1565                                 GL_LockArrays(0, 0);
1566                                 c_rt_lightmeshes++;
1567                                 c_rt_lighttris += numtriangles;
1568         
1569                                 memset(&m, 0, sizeof(m));
1570                                 m.pointer_vertex = vertex3f;
1571                                 m.tex[0] = R_GetTexture(glosstexture);
1572                                 if (lightcubemap)
1573                                 {
1574                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1575                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1576                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1577                                 }
1578                                 m.pointer_texcoord[0] = texcoord2f;
1579                                 R_Mesh_State(&m);
1580                                 GL_LockArrays(0, numverts);
1581                                 GL_ColorMask(1,1,1,0);
1582                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1583                                 VectorScale(lightcolor, colorscale, color2);
1584                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1585                                 {
1586                                         color[0] = bound(0, color2[0], 1);
1587                                         color[1] = bound(0, color2[1], 1);
1588                                         color[2] = bound(0, color2[2], 1);
1589                                         GL_Color(color[0], color[1], color[2], 1);
1590                                         R_Mesh_Draw(numverts, numtriangles, elements);
1591                                         c_rt_lightmeshes++;
1592                                         c_rt_lighttris += numtriangles;
1593                                 }
1594                                 GL_LockArrays(0, 0);
1595                         }
1596                         else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1597                         {
1598                                 // 2/0/0/2 3D combine blendsquare path
1599                                 memset(&m, 0, sizeof(m));
1600                                 m.pointer_vertex = vertex3f;
1601                                 m.tex[0] = R_GetTexture(bumptexture);
1602                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1603                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1604                                 m.pointer_texcoord[0] = texcoord2f;
1605                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1606                                 R_Mesh_State(&m);
1607                                 GL_ColorMask(0,0,0,1);
1608                                 // this squares the result
1609                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1610                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1611                                 GL_LockArrays(0, numverts);
1612                                 R_Mesh_Draw(numverts, numtriangles, elements);
1613                                 GL_LockArrays(0, 0);
1614                                 c_rt_lightmeshes++;
1615                                 c_rt_lighttris += numtriangles;
1616         
1617                                 memset(&m, 0, sizeof(m));
1618                                 m.pointer_vertex = vertex3f;
1619                                 R_Mesh_State(&m);
1620                                 GL_LockArrays(0, numverts);
1621                                 // square alpha in framebuffer a few times to make it shiny
1622                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1623                                 // these comments are a test run through this math for intensity 0.5
1624                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1625                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1626                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1627                                 R_Mesh_Draw(numverts, numtriangles, elements);
1628                                 c_rt_lightmeshes++;
1629                                 c_rt_lighttris += numtriangles;
1630                                 R_Mesh_Draw(numverts, numtriangles, elements);
1631                                 c_rt_lightmeshes++;
1632                                 c_rt_lighttris += numtriangles;
1633                                 GL_LockArrays(0, 0);
1634         
1635                                 memset(&m, 0, sizeof(m));
1636                                 m.pointer_vertex = vertex3f;
1637                                 m.tex[0] = R_GetTexture(glosstexture);
1638                                 m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1639                                 m.pointer_texcoord[0] = texcoord2f;
1640                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1641                                 R_Mesh_State(&m);
1642                                 GL_LockArrays(0, numverts);
1643                                 GL_ColorMask(1,1,1,0);
1644                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1645                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1646                                 VectorScale(lightcolor, colorscale, color2);
1647                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1648                                 {
1649                                         color[0] = bound(0, color2[0], 1);
1650                                         color[1] = bound(0, color2[1], 1);
1651                                         color[2] = bound(0, color2[2], 1);
1652                                         GL_Color(color[0], color[1], color[2], 1);
1653                                         R_Mesh_Draw(numverts, numtriangles, elements);
1654                                         c_rt_lightmeshes++;
1655                                         c_rt_lighttris += numtriangles;
1656                                 }
1657                                 GL_LockArrays(0, 0);
1658                         }
1659                         else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1660                         {
1661                                 // 2/0/0/2/2 2D combine blendsquare path
1662                                 memset(&m, 0, sizeof(m));
1663                                 m.pointer_vertex = vertex3f;
1664                                 m.tex[0] = R_GetTexture(bumptexture);
1665                                 m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1666                                 m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1667                                 m.pointer_texcoord[0] = texcoord2f;
1668                                 m.pointer_texcoord[1] = varray_texcoord3f[1];
1669                                 R_Mesh_State(&m);
1670                                 GL_ColorMask(0,0,0,1);
1671                                 // this squares the result
1672                                 GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1673                                 R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1674                                 GL_LockArrays(0, numverts);
1675                                 R_Mesh_Draw(numverts, numtriangles, elements);
1676                                 GL_LockArrays(0, 0);
1677                                 c_rt_lightmeshes++;
1678                                 c_rt_lighttris += numtriangles;
1679         
1680                                 memset(&m, 0, sizeof(m));
1681                                 m.pointer_vertex = vertex3f;
1682                                 R_Mesh_State(&m);
1683                                 GL_LockArrays(0, numverts);
1684                                 // square alpha in framebuffer a few times to make it shiny
1685                                 GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1686                                 // these comments are a test run through this math for intensity 0.5
1687                                 // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1688                                 // 0.25 * 0.25 = 0.0625 (this is another pass)
1689                                 // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1690                                 R_Mesh_Draw(numverts, numtriangles, elements);
1691                                 c_rt_lightmeshes++;
1692                                 c_rt_lighttris += numtriangles;
1693                                 R_Mesh_Draw(numverts, numtriangles, elements);
1694                                 c_rt_lightmeshes++;
1695                                 c_rt_lighttris += numtriangles;
1696                                 GL_LockArrays(0, 0);
1697         
1698                                 memset(&m, 0, sizeof(m));
1699                                 m.pointer_vertex = vertex3f;
1700                                 m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1701                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1702                                 m.pointer_texcoord[0] = varray_texcoord2f[0];
1703                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1704                                 R_Mesh_State(&m);
1705                                 GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1706                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1707                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1708                                 GL_LockArrays(0, numverts);
1709                                 R_Mesh_Draw(numverts, numtriangles, elements);
1710                                 GL_LockArrays(0, 0);
1711                                 c_rt_lightmeshes++;
1712                                 c_rt_lighttris += numtriangles;
1713         
1714                                 memset(&m, 0, sizeof(m));
1715                                 m.pointer_vertex = vertex3f;
1716                                 m.tex[0] = R_GetTexture(glosstexture);
1717                                 if (lightcubemap)
1718                                 {
1719                                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1720                                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1721                                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltolight);
1722                                 }
1723                                 m.pointer_texcoord[0] = texcoord2f;
1724                                 R_Mesh_State(&m);
1725                                 GL_LockArrays(0, numverts);
1726                                 GL_ColorMask(1,1,1,0);
1727                                 GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1728                                 VectorScale(lightcolor, colorscale, color2);
1729                                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1730                                 {
1731                                         color[0] = bound(0, color2[0], 1);
1732                                         color[1] = bound(0, color2[1], 1);
1733                                         color[2] = bound(0, color2[2], 1);
1734                                         GL_Color(color[0], color[1], color[2], 1);
1735                                         R_Mesh_Draw(numverts, numtriangles, elements);
1736                                         c_rt_lightmeshes++;
1737                                         c_rt_lighttris += numtriangles;
1738                                 }
1739                                 GL_LockArrays(0, 0);
1740                         }
1741                 }
1742         }
1743         else
1744         {
1745                 if (lighting & LIGHTING_DIFFUSE)
1746                 {
1747                         GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1748                         GL_DepthMask(false);
1749                         GL_DepthTest(true);
1750                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1751                         memset(&m, 0, sizeof(m));
1752                         m.pointer_vertex = vertex3f;
1753                         m.pointer_color = varray_color4f;
1754                         m.tex[0] = R_GetTexture(basetexture);
1755                         m.pointer_texcoord[0] = texcoord2f;
1756                         if (r_textureunits.integer >= 2)
1757                         {
1758                                 // voodoo2
1759                                 m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1760                                 m.pointer_texcoord[1] = varray_texcoord2f[1];
1761                                 R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1762                         }
1763                         R_Mesh_State(&m);
1764                         GL_LockArrays(0, numverts);
1765                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1766                         {
1767                                 color[0] = bound(0, color2[0], 1);
1768                                 color[1] = bound(0, color2[1], 1);
1769                                 color[2] = bound(0, color2[2], 1);
1770                                 if (r_textureunits.integer >= 2)
1771                                         R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1772                                 else
1773                                         R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltolight);
1774                                 R_Mesh_Draw(numverts, numtriangles, elements);
1775                                 c_rt_lightmeshes++;
1776                                 c_rt_lighttris += numtriangles;
1777                         }
1778                         GL_LockArrays(0, 0);
1779                 }
1780         }
1781 }
1782
1783 void R_RTLight_UpdateFromDLight(rtlight_t *rtlight, const dlight_t *light, int isstatic)
1784 {
1785         int j, k;
1786         float scale;
1787         R_RTLight_Uncompile(rtlight);
1788         memset(rtlight, 0, sizeof(*rtlight));
1789
1790         VectorCopy(light->origin, rtlight->shadoworigin);
1791         VectorCopy(light->color, rtlight->color);
1792         rtlight->radius = light->radius;
1793         //rtlight->cullradius = rtlight->radius;
1794         //rtlight->cullradius2 = rtlight->radius * rtlight->radius;
1795         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1796         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1797         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1798         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1799         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1800         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1801         rtlight->cubemapname[0] = 0;
1802         if (light->cubemapname[0])
1803                 strcpy(rtlight->cubemapname, light->cubemapname);
1804         else if (light->cubemapnum > 0)
1805                 sprintf(rtlight->cubemapname, "cubemaps/%i", light->cubemapnum);
1806         rtlight->shadow = light->shadow;
1807         rtlight->corona = light->corona;
1808         rtlight->style = light->style;
1809         rtlight->isstatic = isstatic;
1810         Matrix4x4_Invert_Simple(&rtlight->matrix_worldtolight, &light->matrix);
1811         // ConcatScale won't work here because this needs to scale rotate and
1812         // translate, not just rotate
1813         scale = 1.0f / rtlight->radius;
1814         for (k = 0;k < 3;k++)
1815                 for (j = 0;j < 4;j++)
1816                         rtlight->matrix_worldtolight.m[k][j] *= scale;
1817         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationxyz, &matrix_attenuationxyz, &rtlight->matrix_worldtolight);
1818         Matrix4x4_Concat(&rtlight->matrix_worldtoattenuationz, &matrix_attenuationz, &rtlight->matrix_worldtolight);
1819
1820         rtlight->lightmap_cullradius = bound(0, rtlight->radius, 2048.0f);
1821         rtlight->lightmap_cullradius2 = rtlight->lightmap_cullradius * rtlight->lightmap_cullradius;
1822         VectorScale(rtlight->color, rtlight->radius * d_lightstylevalue[rtlight->style] * 0.25f, rtlight->lightmap_light);
1823         rtlight->lightmap_subtract = 1.0f / rtlight->lightmap_cullradius2;
1824 }
1825
1826 rtlight_t *r_shadow_compilingrtlight;
1827
1828 // compiles rtlight geometry
1829 // (undone by R_FreeCompiledRTLight, which R_UpdateLight calls)
1830 void R_RTLight_Compile(rtlight_t *rtlight)
1831 {
1832         int shadowmeshes, shadowtris, lightmeshes, lighttris, numclusters, numsurfaces;
1833         entity_render_t *ent = &cl_entities[0].render;
1834         model_t *model = ent->model;
1835
1836         // compile the light
1837         rtlight->compiled = true;
1838         rtlight->static_numclusters = 0;
1839         rtlight->static_numclusterpvsbytes = 0;
1840         rtlight->static_clusterlist = NULL;
1841         rtlight->static_clusterpvs = NULL;
1842         rtlight->cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1843         rtlight->cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1844         rtlight->cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1845         rtlight->cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1846         rtlight->cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1847         rtlight->cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1848
1849         if (model && model->GetLightInfo)
1850         {
1851                 // this variable directs the DrawShadowVolume and DrawLight code to capture into the mesh chain instead of rendering
1852                 r_shadow_compilingrtlight = rtlight;
1853                 R_Shadow_EnlargeClusterBuffer(model->brush.num_pvsclusters);
1854                 R_Shadow_EnlargeSurfaceBuffer(model->numsurfaces); 
1855                 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);
1856                 if (numclusters)
1857                 {
1858                         rtlight->static_numclusters = numclusters;
1859                         rtlight->static_numclusterpvsbytes = (model->brush.num_pvsclusters + 7) >> 3;
1860                         rtlight->static_clusterlist = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1861                         rtlight->static_clusterpvs = Mem_Alloc(r_shadow_mempool, rtlight->static_numclusterpvsbytes);
1862                         memcpy(rtlight->static_clusterlist, r_shadow_buffer_clusterlist, rtlight->static_numclusters * sizeof(*rtlight->static_clusterlist));
1863                         memcpy(rtlight->static_clusterpvs, r_shadow_buffer_clusterpvs, rtlight->static_numclusterpvsbytes);
1864                 }
1865                 if (model->DrawShadowVolume && rtlight->shadow)
1866                 {
1867                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, false, false, true);
1868                         model->DrawShadowVolume(ent, rtlight->shadoworigin, rtlight->radius, numsurfaces, r_shadow_buffer_surfacelist);
1869                         rtlight->static_meshchain_shadow = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_shadow, false, false);
1870                 }
1871                 if (model->DrawLight)
1872                 {
1873                         rtlight->static_meshchain_light = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768, 32768, NULL, NULL, NULL, true, false, true);
1874                         model->DrawLight(ent, rtlight->shadoworigin, vec3_origin, rtlight->radius, vec3_origin, &r_identitymatrix, &r_identitymatrix, &r_identitymatrix, NULL, numsurfaces, r_shadow_buffer_surfacelist);
1875                         rtlight->static_meshchain_light = Mod_ShadowMesh_Finish(r_shadow_mempool, rtlight->static_meshchain_light, true, false);
1876                 }
1877                 // switch back to rendering when DrawShadowVolume or DrawLight is called
1878                 r_shadow_compilingrtlight = NULL;
1879         }
1880
1881
1882         // use smallest available cullradius - box radius or light radius
1883         //rtlight->cullradius = RadiusFromBoundsAndOrigin(rtlight->cullmins, rtlight->cullmaxs, rtlight->shadoworigin);
1884         //rtlight->cullradius = min(rtlight->cullradius, rtlight->radius);
1885
1886         shadowmeshes = 0;
1887         shadowtris = 0;
1888         if (rtlight->static_meshchain_shadow)
1889         {
1890                 shadowmesh_t *mesh;
1891                 for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
1892                 {
1893                         shadowmeshes++;
1894                         shadowtris += mesh->numtriangles;
1895                 }
1896         }
1897
1898         lightmeshes = 0;
1899         lighttris = 0;
1900         if (rtlight->static_meshchain_light)
1901         {
1902                 shadowmesh_t *mesh;
1903                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
1904                 {
1905                         lightmeshes++;
1906                         lighttris += mesh->numtriangles;
1907                 }
1908         }
1909
1910         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);
1911 }
1912
1913 void R_RTLight_Uncompile(rtlight_t *rtlight)
1914 {
1915         if (rtlight->compiled)
1916         {
1917                 if (rtlight->static_meshchain_shadow)
1918                         Mod_ShadowMesh_Free(rtlight->static_meshchain_shadow);
1919                 rtlight->static_meshchain_shadow = NULL;
1920                 if (rtlight->static_meshchain_light)
1921                         Mod_ShadowMesh_Free(rtlight->static_meshchain_light);
1922                 rtlight->static_meshchain_light = NULL;
1923                 if (rtlight->static_clusterlist)
1924                         Mem_Free(rtlight->static_clusterlist);
1925                 rtlight->static_clusterlist = NULL;
1926                 if (rtlight->static_clusterpvs)
1927                         Mem_Free(rtlight->static_clusterpvs);
1928                 rtlight->static_clusterpvs = NULL;
1929                 rtlight->static_numclusters = 0;
1930                 rtlight->static_numclusterpvsbytes = 0;
1931                 rtlight->compiled = false;
1932         }
1933 }
1934
1935 int shadowframecount = 0;
1936
1937 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, dlight_t *light);
1938
1939 void R_DrawRTLight(rtlight_t *rtlight, int visiblevolumes)
1940 {
1941         int i, shadow;
1942         entity_render_t *ent;
1943         float f;
1944         vec3_t relativelightorigin, relativeeyeorigin, lightcolor;
1945         rtexture_t *cubemaptexture;
1946         matrix4x4_t matrix_modeltolight, matrix_modeltoattenuationxyz, matrix_modeltoattenuationz;
1947         int numclusters, numsurfaces;
1948         int *clusterlist, *surfacelist;
1949         qbyte *clusterpvs;
1950         vec3_t cullmins, cullmaxs;
1951         shadowmesh_t *mesh;
1952         rmeshstate_t m;
1953
1954         if (d_lightstylevalue[rtlight->style] <= 0)
1955                 return;
1956         cullmins[0] = rtlight->shadoworigin[0] - rtlight->radius;
1957         cullmins[1] = rtlight->shadoworigin[1] - rtlight->radius;
1958         cullmins[2] = rtlight->shadoworigin[2] - rtlight->radius;
1959         cullmaxs[0] = rtlight->shadoworigin[0] + rtlight->radius;
1960         cullmaxs[1] = rtlight->shadoworigin[1] + rtlight->radius;
1961         cullmaxs[2] = rtlight->shadoworigin[2] + rtlight->radius;
1962         if (R_CullBox(cullmins, cullmaxs))
1963                 return;
1964         if (rtlight->isstatic && !rtlight->compiled && r_shadow_staticworldlights.integer)
1965                 R_RTLight_Compile(rtlight);
1966         numclusters = 0;
1967         clusterlist = NULL;
1968         clusterpvs = NULL;
1969         numsurfaces = 0;
1970         surfacelist = NULL;
1971         if (rtlight->compiled && r_shadow_staticworldlights.integer)
1972         {
1973                 numclusters = rtlight->static_numclusters;
1974                 clusterlist = rtlight->static_clusterlist;
1975                 clusterpvs = rtlight->static_clusterpvs;
1976                 VectorCopy(rtlight->cullmins, cullmins);
1977                 VectorCopy(rtlight->cullmaxs, cullmaxs);
1978         }
1979         else if (cl.worldmodel && cl.worldmodel->GetLightInfo)
1980         {
1981                 R_Shadow_EnlargeClusterBuffer(cl.worldmodel->brush.num_pvsclusters);
1982                 R_Shadow_EnlargeSurfaceBuffer(cl.worldmodel->numsurfaces); 
1983                 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);
1984                 clusterlist = r_shadow_buffer_clusterlist;
1985                 clusterpvs = r_shadow_buffer_clusterpvs;
1986                 surfacelist = r_shadow_buffer_surfacelist;
1987         }
1988         if (numclusters)
1989         {
1990                 for (i = 0;i < numclusters;i++)
1991                         if (CHECKPVSBIT(r_pvsbits, clusterlist[i]))
1992                                 break;
1993                 if (i == numclusters)
1994                         return;
1995         }
1996         if (R_CullBox(cullmins, cullmaxs))
1997                 return;
1998         if (R_Shadow_ScissorForBBox(cullmins, cullmaxs))
1999                 return;
2000
2001         f = d_lightstylevalue[rtlight->style] * (1.0f / 256.0f);
2002         VectorScale(rtlight->color, f, lightcolor);
2003         /*
2004         if (rtlight->selected)
2005         {
2006                 f = 2 + sin(realtime * M_PI * 4.0);
2007                 VectorScale(lightcolor, f, lightcolor);
2008         }
2009         */
2010
2011         if (rtlight->cubemapname[0])
2012                 cubemaptexture = R_Shadow_Cubemap(rtlight->cubemapname);
2013         else
2014                 cubemaptexture = NULL;
2015
2016         shadow = rtlight->shadow && (rtlight->isstatic ? r_shadow_worldshadows.integer : r_shadow_dlightshadows.integer);
2017         if (shadow && (gl_stencil || visiblevolumes))
2018         {
2019                 if (!visiblevolumes)
2020                         R_Shadow_Stage_ShadowVolumes();
2021                 ent = &cl_entities[0].render;
2022                 if (r_shadow_staticworldlights.integer && rtlight->compiled)
2023                 {
2024                         memset(&m, 0, sizeof(m));
2025                         R_Mesh_Matrix(&ent->matrix);
2026                         for (mesh = rtlight->static_meshchain_shadow;mesh;mesh = mesh->next)
2027                         {
2028                                 m.pointer_vertex = mesh->vertex3f;
2029                                 R_Mesh_State(&m);
2030                                 GL_LockArrays(0, mesh->numverts);
2031                                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
2032                                 {
2033                                         // decrement stencil if frontface is behind depthbuffer
2034                                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
2035                                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
2036                                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2037                                         c_rtcached_shadowmeshes++;
2038                                         c_rtcached_shadowtris += mesh->numtriangles;
2039                                         // increment stencil if backface is behind depthbuffer
2040                                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
2041                                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
2042                                 }
2043                                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
2044                                 c_rtcached_shadowmeshes++;
2045                                 c_rtcached_shadowtris += mesh->numtriangles;
2046                                 GL_LockArrays(0, 0);
2047                         }
2048                 }
2049                 else
2050                 {
2051                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2052                         ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, numsurfaces, surfacelist);
2053                 }
2054                 if (r_drawentities.integer)
2055                 {
2056                         for (i = 0;i < r_refdef.numentities;i++)
2057                         {
2058                                 ent = r_refdef.entities[i];
2059                                 // rough checks
2060                                 if (r_shadow_cull.integer)
2061                                 {
2062                                         if (!BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs))
2063                                                 continue;
2064                                         if (cl.worldmodel != NULL && cl.worldmodel->brush.BoxTouchingPVS != NULL && !cl.worldmodel->brush.BoxTouchingPVS(cl.worldmodel, clusterpvs, ent->mins, ent->maxs))
2065                                                 continue;
2066                                 }
2067                                 if (!(ent->flags & RENDER_SHADOW) || !ent->model || !ent->model->DrawShadowVolume)
2068                                         continue;
2069                                 Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2070                                 ent->model->DrawShadowVolume(ent, relativelightorigin, rtlight->radius, ent->model->numsurfaces, ent->model->surfacelist);
2071                         }
2072                 }
2073         }
2074
2075         if (!visiblevolumes)
2076         {
2077                 if (shadow && gl_stencil)
2078                         R_Shadow_Stage_LightWithShadows();
2079                 else
2080                         R_Shadow_Stage_LightWithoutShadows();
2081
2082                 ent = &cl_entities[0].render;
2083                 if (ent->model && ent->model->DrawLight)
2084                 {
2085                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2086                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2087                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2088                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2089                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2090                         if (r_shadow_staticworldlights.integer && rtlight->compiled)
2091                         {
2092                                 R_Mesh_Matrix(&ent->matrix);
2093                                 for (mesh = rtlight->static_meshchain_light;mesh;mesh = mesh->next)
2094                                         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);
2095                         }
2096                         else
2097                                 ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, numsurfaces, surfacelist);
2098                 }
2099                 if (r_drawentities.integer)
2100                 {
2101                         for (i = 0;i < r_refdef.numentities;i++)
2102                         {
2103                                 ent = r_refdef.entities[i];
2104                                 if (ent->visframe == r_framecount && BoxesOverlap(ent->mins, ent->maxs, cullmins, cullmaxs) && ent->model && ent->model->DrawLight && (ent->flags & RENDER_LIGHT))
2105                                 {
2106                                         Matrix4x4_Transform(&ent->inversematrix, rtlight->shadoworigin, relativelightorigin);
2107                                         Matrix4x4_Transform(&ent->inversematrix, r_vieworigin, relativeeyeorigin);
2108                                         Matrix4x4_Concat(&matrix_modeltolight, &rtlight->matrix_worldtolight, &ent->matrix);
2109                                         Matrix4x4_Concat(&matrix_modeltoattenuationxyz, &rtlight->matrix_worldtoattenuationxyz, &ent->matrix);
2110                                         Matrix4x4_Concat(&matrix_modeltoattenuationz, &rtlight->matrix_worldtoattenuationz, &ent->matrix);
2111                                         ent->model->DrawLight(ent, relativelightorigin, relativeeyeorigin, rtlight->radius, lightcolor, &matrix_modeltolight, &matrix_modeltoattenuationxyz, &matrix_modeltoattenuationz, cubemaptexture, ent->model->numsurfaces, ent->model->surfacelist);
2112                                 }
2113                         }
2114                 }
2115         }
2116 }
2117
2118 void R_ShadowVolumeLighting(int visiblevolumes)
2119 {
2120         int lnum;
2121         dlight_t *light;
2122         rmeshstate_t m;
2123
2124         if (visiblevolumes)
2125         {
2126                 memset(&m, 0, sizeof(m));
2127                 R_Mesh_State(&m);
2128
2129                 GL_BlendFunc(GL_ONE, GL_ONE);
2130                 GL_DepthMask(false);
2131                 GL_DepthTest(r_shadow_visiblevolumes.integer < 2);
2132                 qglDisable(GL_CULL_FACE);
2133                 GL_Color(0.0, 0.0125, 0.1, 1);
2134         }
2135         else
2136                 R_Shadow_Stage_Begin();
2137         shadowframecount++;
2138         if (r_shadow_realtime_world.integer)
2139         {
2140                 R_Shadow_LoadWorldLightsIfNeeded();
2141                 if (r_shadow_debuglight.integer >= 0)
2142                 {
2143                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2144                                 if (lnum == r_shadow_debuglight.integer)
2145                                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2146                 }
2147                 else
2148                         for (lnum = 0, light = r_shadow_worldlightchain;light;lnum++, light = light->next)
2149                                 R_DrawRTLight(&light->rtlight, visiblevolumes);
2150         }
2151         if (r_shadow_realtime_world.integer || r_shadow_realtime_dlight.integer)
2152                 for (lnum = 0, light = r_dlight;lnum < r_numdlights;lnum++, light++)
2153                         R_DrawRTLight(&light->rtlight, visiblevolumes);
2154
2155         if (visiblevolumes)
2156         {
2157                 qglEnable(GL_CULL_FACE);
2158                 GL_Scissor(r_view_x, r_view_y, r_view_width, r_view_height);
2159         }
2160         else
2161                 R_Shadow_Stage_End();
2162 }
2163
2164 cvar_t r_editlights = {0, "r_editlights", "0"};
2165 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
2166 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
2167 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
2168 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
2169 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
2170 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
2171 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
2172 dlight_t *r_shadow_worldlightchain;
2173 dlight_t *r_shadow_selectedlight;
2174 vec3_t r_editlights_cursorlocation;
2175
2176 typedef struct cubemapinfo_s
2177 {
2178         char basename[64];
2179         rtexture_t *texture;
2180 }
2181 cubemapinfo_t;
2182
2183 #define MAX_CUBEMAPS 128
2184 static int numcubemaps;
2185 static cubemapinfo_t cubemaps[MAX_CUBEMAPS];
2186
2187 //static char *suffix[6] = {"ft", "bk", "rt", "lf", "up", "dn"};
2188 typedef struct suffixinfo_s
2189 {
2190         char *suffix;
2191         int flipx, flipy, flipdiagonal;
2192 }
2193 suffixinfo_t;
2194 static suffixinfo_t suffix[3][6] =
2195 {
2196         {
2197                 {"posx", false, false, false},
2198                 {"negx", false, false, false},
2199                 {"posy", false, false, false},
2200                 {"negy", false, false, false},
2201                 {"posz", false, false, false},
2202                 {"negz", false, false, false}
2203         },
2204         {
2205                 {"px", false, false, false},
2206                 {"nx", false, false, false},
2207                 {"py", false, false, false},
2208                 {"ny", false, false, false},
2209                 {"pz", false, false, false},
2210                 {"nz", false, false, false}
2211         },
2212         {
2213                 {"ft", true, false, true},
2214                 {"bk", false, true, true},
2215                 {"lf", true, true, false},
2216                 {"rt", false, false, false},
2217                 {"up", false, false, false},
2218                 {"dn", false, false, false}
2219         }
2220 };
2221
2222 static int componentorder[4] = {0, 1, 2, 3};
2223
2224 rtexture_t *R_Shadow_LoadCubemap(const char *basename)
2225 {
2226         int i, j, cubemapsize;
2227         qbyte *cubemappixels, *image_rgba;
2228         rtexture_t *cubemaptexture;
2229         char name[256];
2230         // must start 0 so the first loadimagepixels has no requested width/height
2231         cubemapsize = 0;
2232         cubemappixels = NULL;
2233         cubemaptexture = NULL;
2234         for (j = 0;j < 3 && !cubemappixels;j++)
2235         {
2236                 for (i = 0;i < 6;i++)
2237                 {
2238                         snprintf(name, sizeof(name), "%s%s", basename, suffix[j][i].suffix);
2239                         if ((image_rgba = loadimagepixels(name, false, cubemapsize, cubemapsize)))
2240                         {
2241                                 if (image_width == image_height)
2242                                 {
2243                                         if (!cubemappixels && image_width >= 1)
2244                                         {
2245                                                 cubemapsize = image_width;
2246                                                 // note this clears to black, so unavailable sizes are black
2247                                                 cubemappixels = Mem_Alloc(tempmempool, 6*cubemapsize*cubemapsize*4);
2248                                         }
2249                                         if (cubemappixels)
2250                                                 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);
2251                                 }
2252                                 else
2253                                         Con_Printf("Cubemap image \"%s\" (%ix%i) is not square, OpenGL requires square cubemaps.\n", name, image_width, image_height);
2254                                 Mem_Free(image_rgba);
2255                         }
2256                 }
2257         }
2258         if (cubemappixels)
2259         {
2260                 if (!r_shadow_filters_texturepool)
2261                         r_shadow_filters_texturepool = R_AllocTexturePool();
2262                 cubemaptexture = R_LoadTextureCubeMap(r_shadow_filters_texturepool, basename, cubemapsize, cubemappixels, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
2263                 Mem_Free(cubemappixels);
2264         }
2265         else
2266         {
2267                 Con_Printf("Failed to load Cubemap \"%s\", tried ", basename);
2268                 for (j = 0;j < 3;j++)
2269                         for (i = 0;i < 6;i++)
2270                                 Con_Printf("%s\"%s%s.tga\"", j + i > 0 ? ", " : "", basename, suffix[j][i].suffix);
2271                 Con_Print(" and was unable to find any of them.\n");
2272         }
2273         return cubemaptexture;
2274 }
2275
2276 rtexture_t *R_Shadow_Cubemap(const char *basename)
2277 {
2278         int i;
2279         for (i = 0;i < numcubemaps;i++)
2280                 if (!strcasecmp(cubemaps[i].basename, basename))
2281                         return cubemaps[i].texture;
2282         if (i >= MAX_CUBEMAPS)
2283                 return NULL;
2284         numcubemaps++;
2285         strcpy(cubemaps[i].basename, basename);
2286         cubemaps[i].texture = R_Shadow_LoadCubemap(cubemaps[i].basename);
2287         return cubemaps[i].texture;
2288 }
2289
2290 void R_Shadow_FreeCubemaps(void)
2291 {
2292         numcubemaps = 0;
2293         R_FreeTexturePool(&r_shadow_filters_texturepool);
2294 }
2295
2296 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)
2297 {
2298         dlight_t *light;
2299
2300         if (radius < 15 || DotProduct(color, color) < 0.03)
2301         {
2302                 Con_Print("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
2303                 return;
2304         }
2305
2306         light = Mem_Alloc(r_shadow_mempool, sizeof(dlight_t));
2307         VectorCopy(origin, light->origin);
2308         VectorCopy(angles, light->angles);
2309         VectorCopy(color, light->color);
2310         light->radius = radius;
2311         light->style = style;
2312         if (light->style < 0 || light->style >= MAX_LIGHTSTYLES)
2313         {
2314                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", light->style, MAX_LIGHTSTYLES);
2315                 light->style = 0;
2316         }
2317         light->shadow = shadowenable;
2318         light->corona = corona;
2319         if (cubemapname && cubemapname[0] && strlen(cubemapname) < sizeof(light->cubemapname))
2320                 strcpy(light->cubemapname, cubemapname);
2321         Matrix4x4_CreateFromQuakeEntity(&light->matrix, light->origin[0], light->origin[1], light->origin[2], light->angles[0], light->angles[1], light->angles[2], 1);
2322         light->next = r_shadow_worldlightchain;
2323         r_shadow_worldlightchain = light;
2324
2325         R_RTLight_UpdateFromDLight(&light->rtlight, light, true);
2326         if (r_shadow_staticworldlights.integer)
2327                 R_RTLight_Compile(&light->rtlight);
2328 }
2329
2330 void R_Shadow_FreeWorldLight(dlight_t *light)
2331 {
2332         dlight_t **lightpointer;
2333         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2334         if (*lightpointer != light)
2335                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2336         *lightpointer = light->next;
2337         R_RTLight_Uncompile(&light->rtlight);
2338         Mem_Free(light);
2339 }
2340
2341 void R_Shadow_ClearWorldLights(void)
2342 {
2343         while (r_shadow_worldlightchain)
2344                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2345         r_shadow_selectedlight = NULL;
2346         R_Shadow_FreeCubemaps();
2347 }
2348
2349 void R_Shadow_SelectLight(dlight_t *light)
2350 {
2351         if (r_shadow_selectedlight)
2352                 r_shadow_selectedlight->selected = false;
2353         r_shadow_selectedlight = light;
2354         if (r_shadow_selectedlight)
2355                 r_shadow_selectedlight->selected = true;
2356 }
2357
2358 rtexture_t *lighttextures[5];
2359
2360 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2361 {
2362         float scale = r_editlights_cursorgrid.value * 0.5f;
2363         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);
2364 }
2365
2366 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2367 {
2368         float intensity;
2369         const dlight_t *light;
2370         light = calldata1;
2371         intensity = 0.5;
2372         if (light->selected)
2373                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2374         if (!light->shadow)
2375                 intensity *= 0.5f;
2376         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);
2377 }
2378
2379 void R_Shadow_DrawLightSprites(void)
2380 {
2381         int i;
2382         cachepic_t *pic;
2383         dlight_t *light;
2384
2385         for (i = 0;i < 5;i++)
2386         {
2387                 lighttextures[i] = NULL;
2388                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2389                         lighttextures[i] = pic->tex;
2390         }
2391
2392         for (light = r_shadow_worldlightchain;light;light = light->next)
2393                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2394         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2395 }
2396
2397 void R_Shadow_SelectLightInView(void)
2398 {
2399         float bestrating, rating, temp[3];
2400         dlight_t *best, *light;
2401         best = NULL;
2402         bestrating = 0;
2403         for (light = r_shadow_worldlightchain;light;light = light->next)
2404         {
2405                 VectorSubtract(light->origin, r_vieworigin, temp);
2406                 rating = (DotProduct(temp, r_viewforward) / sqrt(DotProduct(temp, temp)));
2407                 if (rating >= 0.95)
2408                 {
2409                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2410                         if (bestrating < rating && CL_TraceLine(light->origin, r_vieworigin, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
2411                         {
2412                                 bestrating = rating;
2413                                 best = light;
2414                         }
2415                 }
2416         }
2417         R_Shadow_SelectLight(best);
2418 }
2419
2420 void R_Shadow_LoadWorldLights(void)
2421 {
2422         int n, a, style, shadow;
2423         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2424         float origin[3], radius, color[3], angles[3], corona;
2425         if (cl.worldmodel == NULL)
2426         {
2427                 Con_Print("No map loaded.\n");
2428                 return;
2429         }
2430         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2431         strlcat (name, ".rtlights", sizeof (name));
2432         lightsstring = FS_LoadFile(name, false);
2433         if (lightsstring)
2434         {
2435                 s = lightsstring;
2436                 n = 0;
2437                 while (*s)
2438                 {
2439                         t = s;
2440                         /*
2441                         shadow = true;
2442                         for (;COM_Parse(t, true) && strcmp(
2443                         if (COM_Parse(t, true))
2444                         {
2445                                 if (com_token[0] == '!')
2446                                 {
2447                                         shadow = false;
2448                                         origin[0] = atof(com_token+1);
2449                                 }
2450                                 else
2451                                         origin[0] = atof(com_token);
2452                                 if (Com_Parse(t
2453                         }
2454                         */
2455                         t = s;
2456                         while (*s && *s != '\n')
2457                                 s++;
2458                         if (!*s)
2459                                 break;
2460                         *s = 0;
2461                         shadow = true;
2462                         // check for modifier flags
2463                         if (*t == '!')
2464                         {
2465                                 shadow = false;
2466                                 t++;
2467                         }
2468                         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]);
2469                         if (a < 13)
2470                                 VectorClear(angles);
2471                         if (a < 10)
2472                                 corona = 0;
2473                         if (a < 9 || !strcmp(cubemapname, "\"\""))
2474                                 cubemapname[0] = 0;
2475                         *s = '\n';
2476                         if (a < 8)
2477                         {
2478                                 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);
2479                                 break;
2480                         }
2481                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2482                         radius *= r_editlights_rtlightssizescale.value;
2483                         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadow, cubemapname);
2484                         s++;
2485                         n++;
2486                 }
2487                 if (*s)
2488                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2489                 Mem_Free(lightsstring);
2490         }
2491 }
2492
2493 void R_Shadow_SaveWorldLights(void)
2494 {
2495         dlight_t *light;
2496         int bufchars, bufmaxchars;
2497         char *buf, *oldbuf;
2498         char name[MAX_QPATH];
2499         char line[1024];
2500         if (!r_shadow_worldlightchain)
2501                 return;
2502         if (cl.worldmodel == NULL)
2503         {
2504                 Con_Print("No map loaded.\n");
2505                 return;
2506         }
2507         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2508         strlcat (name, ".rtlights", sizeof (name));
2509         bufchars = bufmaxchars = 0;
2510         buf = NULL;
2511         for (light = r_shadow_worldlightchain;light;light = light->next)
2512         {
2513                 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]);
2514                 if (bufchars + (int) strlen(line) > bufmaxchars)
2515                 {
2516                         bufmaxchars = bufchars + strlen(line) + 2048;
2517                         oldbuf = buf;
2518                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2519                         if (oldbuf)
2520                         {
2521                                 if (bufchars)
2522                                         memcpy(buf, oldbuf, bufchars);
2523                                 Mem_Free(oldbuf);
2524                         }
2525                 }
2526                 if (strlen(line))
2527                 {
2528                         memcpy(buf + bufchars, line, strlen(line));
2529                         bufchars += strlen(line);
2530                 }
2531         }
2532         if (bufchars)
2533                 FS_WriteFile(name, buf, bufchars);
2534         if (buf)
2535                 Mem_Free(buf);
2536 }
2537
2538 void R_Shadow_LoadLightsFile(void)
2539 {
2540         int n, a, style;
2541         char name[MAX_QPATH], *lightsstring, *s, *t;
2542         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2543         if (cl.worldmodel == NULL)
2544         {
2545                 Con_Print("No map loaded.\n");
2546                 return;
2547         }
2548         FS_StripExtension (cl.worldmodel->name, name, sizeof (name));
2549         strlcat (name, ".lights", sizeof (name));
2550         lightsstring = FS_LoadFile(name, false);
2551         if (lightsstring)
2552         {
2553                 s = lightsstring;
2554                 n = 0;
2555                 while (*s)
2556                 {
2557                         t = s;
2558                         while (*s && *s != '\n')
2559                                 s++;
2560                         if (!*s)
2561                                 break;
2562                         *s = 0;
2563                         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);
2564                         *s = '\n';
2565                         if (a < 14)
2566                         {
2567                                 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);
2568                                 break;
2569                         }
2570                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2571                         radius = bound(15, radius, 4096);
2572                         VectorScale(color, (2.0f / (8388608.0f)), color);
2573                         R_Shadow_NewWorldLight(origin, vec3_origin, color, radius, 0, style, true, NULL);
2574                         s++;
2575                         n++;
2576                 }
2577                 if (*s)
2578                         Con_Printf("invalid lights file \"%s\"\n", name);
2579                 Mem_Free(lightsstring);
2580         }
2581 }
2582
2583 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2584 {
2585         int entnum, style, islight, skin, pflags, effects;
2586         char key[256], value[1024];
2587         float origin[3], angles[3], radius, color[3], light, fadescale, lightscale, originhack[3], overridecolor[3];
2588         const char *data;
2589
2590         if (cl.worldmodel == NULL)
2591         {
2592                 Con_Print("No map loaded.\n");
2593                 return;
2594         }
2595         data = cl.worldmodel->brush.entities;
2596         if (!data)
2597                 return;
2598         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2599         {
2600                 light = 0;
2601                 origin[0] = origin[1] = origin[2] = 0;
2602                 originhack[0] = originhack[1] = originhack[2] = 0;
2603                 angles[0] = angles[1] = angles[2] = 0;
2604                 color[0] = color[1] = color[2] = 1;
2605                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2606                 fadescale = 1;
2607                 lightscale = 1;
2608                 style = 0;
2609                 skin = 0;
2610                 pflags = 0;
2611                 effects = 0;
2612                 islight = false;
2613                 while (1)
2614                 {
2615                         if (!COM_ParseToken(&data, false))
2616                                 break; // error
2617                         if (com_token[0] == '}')
2618                                 break; // end of entity
2619                         if (com_token[0] == '_')
2620                                 strcpy(key, com_token + 1);
2621                         else
2622                                 strcpy(key, com_token);
2623                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2624                                 key[strlen(key)-1] = 0;
2625                         if (!COM_ParseToken(&data, false))
2626                                 break; // error
2627                         strcpy(value, com_token);
2628
2629                         // now that we have the key pair worked out...
2630                         if (!strcmp("light", key))
2631                                 light = atof(value);
2632                         else if (!strcmp("origin", key))
2633                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2634                         else if (!strcmp("angle", key))
2635                                 angles[0] = 0, angles[1] = atof(value), angles[2] = 0;
2636                         else if (!strcmp("angles", key))
2637                                 sscanf(value, "%f %f %f", &angles[0], &angles[1], &angles[2]);
2638                         else if (!strcmp("color", key))
2639                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2640                         else if (!strcmp("wait", key))
2641                                 fadescale = atof(value);
2642                         else if (!strcmp("classname", key))
2643                         {
2644                                 if (!strncmp(value, "light", 5))
2645                                 {
2646                                         islight = true;
2647                                         if (!strcmp(value, "light_fluoro"))
2648                                         {
2649                                                 originhack[0] = 0;
2650                                                 originhack[1] = 0;
2651                                                 originhack[2] = 0;
2652                                                 overridecolor[0] = 1;
2653                                                 overridecolor[1] = 1;
2654                                                 overridecolor[2] = 1;
2655                                         }
2656                                         if (!strcmp(value, "light_fluorospark"))
2657                                         {
2658                                                 originhack[0] = 0;
2659                                                 originhack[1] = 0;
2660                                                 originhack[2] = 0;
2661                                                 overridecolor[0] = 1;
2662                                                 overridecolor[1] = 1;
2663                                                 overridecolor[2] = 1;
2664                                         }
2665                                         if (!strcmp(value, "light_globe"))
2666                                         {
2667                                                 originhack[0] = 0;
2668                                                 originhack[1] = 0;
2669                                                 originhack[2] = 0;
2670                                                 overridecolor[0] = 1;
2671                                                 overridecolor[1] = 0.8;
2672                                                 overridecolor[2] = 0.4;
2673                                         }
2674                                         if (!strcmp(value, "light_flame_large_yellow"))
2675                                         {
2676                                                 originhack[0] = 0;
2677                                                 originhack[1] = 0;
2678                                                 originhack[2] = 48;
2679                                                 overridecolor[0] = 1;
2680                                                 overridecolor[1] = 0.5;
2681                                                 overridecolor[2] = 0.1;
2682                                         }
2683                                         if (!strcmp(value, "light_flame_small_yellow"))
2684                                         {
2685                                                 originhack[0] = 0;
2686                                                 originhack[1] = 0;
2687                                                 originhack[2] = 40;
2688                                                 overridecolor[0] = 1;
2689                                                 overridecolor[1] = 0.5;
2690                                                 overridecolor[2] = 0.1;
2691                                         }
2692                                         if (!strcmp(value, "light_torch_small_white"))
2693                                         {
2694                                                 originhack[0] = 0;
2695                                                 originhack[1] = 0;
2696                                                 originhack[2] = 40;
2697                                                 overridecolor[0] = 1;
2698                                                 overridecolor[1] = 0.5;
2699                                                 overridecolor[2] = 0.1;
2700                                         }
2701                                         if (!strcmp(value, "light_torch_small_walltorch"))
2702                                         {
2703                                                 originhack[0] = 0;
2704                                                 originhack[1] = 0;
2705                                                 originhack[2] = 40;
2706                                                 overridecolor[0] = 1;
2707                                                 overridecolor[1] = 0.5;
2708                                                 overridecolor[2] = 0.1;
2709                                         }
2710                                 }
2711                         }
2712                         else if (!strcmp("style", key))
2713                                 style = atoi(value);
2714                         else if (cl.worldmodel->type == mod_brushq3)
2715                         {
2716                                 if (!strcmp("scale", key))
2717                                         lightscale = atof(value);
2718                                 if (!strcmp("fade", key))
2719                                         fadescale = atof(value);
2720                         }
2721                         else if (!strcmp("skin", key))
2722                                 skin = (int)atof(value);
2723                         else if (!strcmp("pflags", key))
2724                                 pflags = (int)atof(value);
2725                         else if (!strcmp("effects", key))
2726                                 effects = (int)atof(value);
2727                 }
2728                 if (light <= 0 && islight)
2729                         light = 300;
2730                 if (lightscale <= 0)
2731                         lightscale = 1;
2732                 if (fadescale <= 0)
2733                         fadescale = 1;
2734                 if (gamemode == GAME_TENEBRAE)
2735                 {
2736                         if (effects & EF_NODRAW)
2737                         {
2738                                 pflags |= PFLAGS_FULLDYNAMIC;
2739                                 effects &= ~EF_NODRAW;
2740                         }
2741                 }
2742                 radius = min(light * r_editlights_quakelightsizescale.value * lightscale / fadescale, 1048576);
2743                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2744                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2745                         VectorCopy(overridecolor, color);
2746                 VectorScale(color, light, color);
2747                 VectorAdd(origin, originhack, origin);
2748                 if (radius >= 15 && !(pflags & PFLAGS_FULLDYNAMIC))
2749                         R_Shadow_NewWorldLight(origin, angles, color, radius, (pflags & PFLAGS_CORONA) != 0, style, (pflags & PFLAGS_NOSHADOW) == 0, skin >= 16 ? va("cubemaps/%i", skin) : NULL);
2750         }
2751 }
2752
2753
2754 void R_Shadow_SetCursorLocationForView(void)
2755 {
2756         vec_t dist, push, frac;
2757         vec3_t dest, endpos, normal;
2758         VectorMA(r_vieworigin, r_editlights_cursordistance.value, r_viewforward, dest);
2759         frac = CL_TraceLine(r_vieworigin, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2760         if (frac < 1)
2761         {
2762                 dist = frac * r_editlights_cursordistance.value;
2763                 push = r_editlights_cursorpushback.value;
2764                 if (push > dist)
2765                         push = dist;
2766                 push = -push;
2767                 VectorMA(endpos, push, r_viewforward, endpos);
2768                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2769         }
2770         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2771         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2772         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2773 }
2774
2775 void R_Shadow_UpdateWorldLightSelection(void)
2776 {
2777         if (r_editlights.integer)
2778         {
2779                 R_Shadow_SetCursorLocationForView();
2780                 R_Shadow_SelectLightInView();
2781                 R_Shadow_DrawLightSprites();
2782         }
2783         else
2784                 R_Shadow_SelectLight(NULL);
2785 }
2786
2787 void R_Shadow_EditLights_Clear_f(void)
2788 {
2789         R_Shadow_ClearWorldLights();
2790 }
2791
2792 void R_Shadow_EditLights_Reload_f(void)
2793 {
2794         r_shadow_reloadlights = true;
2795 }
2796
2797 void R_Shadow_EditLights_Save_f(void)
2798 {
2799         if (cl.worldmodel)
2800                 R_Shadow_SaveWorldLights();
2801 }
2802
2803 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2804 {
2805         R_Shadow_ClearWorldLights();
2806         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2807 }
2808
2809 void R_Shadow_EditLights_ImportLightsFile_f(void)
2810 {
2811         R_Shadow_ClearWorldLights();
2812         R_Shadow_LoadLightsFile();
2813 }
2814
2815 void R_Shadow_EditLights_Spawn_f(void)
2816 {
2817         vec3_t color;
2818         if (!r_editlights.integer)
2819         {
2820                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2821                 return;
2822         }
2823         if (Cmd_Argc() != 1)
2824         {
2825                 Con_Print("r_editlights_spawn does not take parameters\n");
2826                 return;
2827         }
2828         color[0] = color[1] = color[2] = 1;
2829         R_Shadow_NewWorldLight(r_editlights_cursorlocation, vec3_origin, color, 200, 0, 0, true, NULL);
2830 }
2831
2832 void R_Shadow_EditLights_Edit_f(void)
2833 {
2834         vec3_t origin, angles, color;
2835         vec_t radius, corona;
2836         int style, shadows;
2837         char cubemapname[1024];
2838         if (!r_editlights.integer)
2839         {
2840                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2841                 return;
2842         }
2843         if (!r_shadow_selectedlight)
2844         {
2845                 Con_Print("No selected light.\n");
2846                 return;
2847         }
2848         VectorCopy(r_shadow_selectedlight->origin, origin);
2849         VectorCopy(r_shadow_selectedlight->angles, angles);
2850         VectorCopy(r_shadow_selectedlight->color, color);
2851         radius = r_shadow_selectedlight->radius;
2852         style = r_shadow_selectedlight->style;
2853         if (r_shadow_selectedlight->cubemapname)
2854                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2855         else
2856                 cubemapname[0] = 0;
2857         shadows = r_shadow_selectedlight->shadow;
2858         corona = r_shadow_selectedlight->corona;
2859         if (!strcmp(Cmd_Argv(1), "origin"))
2860         {
2861                 if (Cmd_Argc() != 5)
2862                 {
2863                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2864                         return;
2865                 }
2866                 origin[0] = atof(Cmd_Argv(2));
2867                 origin[1] = atof(Cmd_Argv(3));
2868                 origin[2] = atof(Cmd_Argv(4));
2869         }
2870         else if (!strcmp(Cmd_Argv(1), "originx"))
2871         {
2872                 if (Cmd_Argc() != 3)
2873                 {
2874                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2875                         return;
2876                 }
2877                 origin[0] = atof(Cmd_Argv(2));
2878         }
2879         else if (!strcmp(Cmd_Argv(1), "originy"))
2880         {
2881                 if (Cmd_Argc() != 3)
2882                 {
2883                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2884                         return;
2885                 }
2886                 origin[1] = atof(Cmd_Argv(2));
2887         }
2888         else if (!strcmp(Cmd_Argv(1), "originz"))
2889         {
2890                 if (Cmd_Argc() != 3)
2891                 {
2892                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2893                         return;
2894                 }
2895                 origin[2] = atof(Cmd_Argv(2));
2896         }
2897         else if (!strcmp(Cmd_Argv(1), "move"))
2898         {
2899                 if (Cmd_Argc() != 5)
2900                 {
2901                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2902                         return;
2903                 }
2904                 origin[0] += atof(Cmd_Argv(2));
2905                 origin[1] += atof(Cmd_Argv(3));
2906                 origin[2] += atof(Cmd_Argv(4));
2907         }
2908         else if (!strcmp(Cmd_Argv(1), "movex"))
2909         {
2910                 if (Cmd_Argc() != 3)
2911                 {
2912                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2913                         return;
2914                 }
2915                 origin[0] += atof(Cmd_Argv(2));
2916         }
2917         else if (!strcmp(Cmd_Argv(1), "movey"))
2918         {
2919                 if (Cmd_Argc() != 3)
2920                 {
2921                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2922                         return;
2923                 }
2924                 origin[1] += atof(Cmd_Argv(2));
2925         }
2926         else if (!strcmp(Cmd_Argv(1), "movez"))
2927         {
2928                 if (Cmd_Argc() != 3)
2929                 {
2930                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2931                         return;
2932                 }
2933                 origin[2] += atof(Cmd_Argv(2));
2934         }
2935         else if (!strcmp(Cmd_Argv(1), "angles"))
2936         {
2937                 if (Cmd_Argc() != 5)
2938                 {
2939                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(1));
2940                         return;
2941                 }
2942                 angles[0] = atof(Cmd_Argv(2));
2943                 angles[1] = atof(Cmd_Argv(3));
2944                 angles[2] = atof(Cmd_Argv(4));
2945         }
2946         else if (!strcmp(Cmd_Argv(1), "anglesx"))
2947         {
2948                 if (Cmd_Argc() != 3)
2949                 {
2950                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2951                         return;
2952                 }
2953                 angles[0] = atof(Cmd_Argv(2));
2954         }
2955         else if (!strcmp(Cmd_Argv(1), "anglesy"))
2956         {
2957                 if (Cmd_Argc() != 3)
2958                 {
2959                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2960                         return;
2961                 }
2962                 angles[1] = atof(Cmd_Argv(2));
2963         }
2964         else if (!strcmp(Cmd_Argv(1), "anglesz"))
2965         {
2966                 if (Cmd_Argc() != 3)
2967                 {
2968                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2969                         return;
2970                 }
2971                 angles[2] = atof(Cmd_Argv(2));
2972         }
2973         else if (!strcmp(Cmd_Argv(1), "color"))
2974         {
2975                 if (Cmd_Argc() != 5)
2976                 {
2977                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(1));
2978                         return;
2979                 }
2980                 color[0] = atof(Cmd_Argv(2));
2981                 color[1] = atof(Cmd_Argv(3));
2982                 color[2] = atof(Cmd_Argv(4));
2983         }
2984         else if (!strcmp(Cmd_Argv(1), "radius"))
2985         {
2986                 if (Cmd_Argc() != 3)
2987                 {
2988                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2989                         return;
2990                 }
2991                 radius = atof(Cmd_Argv(2));
2992         }
2993         else if (!strcmp(Cmd_Argv(1), "style"))
2994         {
2995                 if (Cmd_Argc() != 3)
2996                 {
2997                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
2998                         return;
2999                 }
3000                 style = atoi(Cmd_Argv(2));
3001         }
3002         else if (!strcmp(Cmd_Argv(1), "cubemap"))
3003         {
3004                 if (Cmd_Argc() > 3)
3005                 {
3006                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3007                         return;
3008                 }
3009                 if (Cmd_Argc() == 3)
3010                         strcpy(cubemapname, Cmd_Argv(2));
3011                 else
3012                         cubemapname[0] = 0;
3013         }
3014         else if (!strcmp(Cmd_Argv(1), "shadows"))
3015         {
3016                 if (Cmd_Argc() != 3)
3017                 {
3018                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3019                         return;
3020                 }
3021                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
3022         }
3023         else if (!strcmp(Cmd_Argv(1), "corona"))
3024         {
3025                 if (Cmd_Argc() != 3)
3026                 {
3027                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(1));
3028                         return;
3029                 }
3030                 corona = atof(Cmd_Argv(2));
3031         }
3032         else
3033         {
3034                 Con_Print("usage: r_editlights_edit [property] [value]\n");
3035                 Con_Print("Selected light's properties:\n");
3036                 Con_Printf("Origin : %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
3037                 Con_Printf("Angles : %f %f %f\n", r_shadow_selectedlight->angles[0], r_shadow_selectedlight->angles[1], r_shadow_selectedlight->angles[2]);
3038                 Con_Printf("Color  : %f %f %f\n", r_shadow_selectedlight->color[0], r_shadow_selectedlight->color[1], r_shadow_selectedlight->color[2]);
3039                 Con_Printf("Radius : %f\n", r_shadow_selectedlight->radius);
3040                 Con_Printf("Corona : %f\n", r_shadow_selectedlight->corona);
3041                 Con_Printf("Style  : %i\n", r_shadow_selectedlight->style);
3042                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->shadow ? "yes" : "no");
3043                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
3044                 return;
3045         }
3046         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3047         r_shadow_selectedlight = NULL;
3048         R_Shadow_NewWorldLight(origin, angles, color, radius, corona, style, shadows, cubemapname);
3049 }
3050
3051 extern int con_vislines;
3052 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
3053 {
3054         float x, y;
3055         char temp[256];
3056         if (r_shadow_selectedlight == NULL)
3057                 return;
3058         x = 0;
3059         y = con_vislines;
3060         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3061         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;
3062         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;
3063         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;
3064         sprintf(temp, "Radius  %f", r_shadow_selectedlight->radius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3065         sprintf(temp, "Corona  %f", r_shadow_selectedlight->corona);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3066         sprintf(temp, "Style   %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3067         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;
3068         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
3069 }
3070
3071 void R_Shadow_EditLights_ToggleShadow_f(void)
3072 {
3073         if (!r_editlights.integer)
3074         {
3075                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3076                 return;
3077         }
3078         if (!r_shadow_selectedlight)
3079         {
3080                 Con_Print("No selected light.\n");
3081                 return;
3082         }
3083         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);
3084         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3085         r_shadow_selectedlight = NULL;
3086 }
3087
3088 void R_Shadow_EditLights_ToggleCorona_f(void)
3089 {
3090         if (!r_editlights.integer)
3091         {
3092                 Con_Print("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
3093                 return;
3094         }
3095         if (!r_shadow_selectedlight)
3096         {
3097                 Con_Print("No selected light.\n");
3098                 return;
3099         }
3100         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);
3101         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3102         r_shadow_selectedlight = NULL;
3103 }
3104
3105 void R_Shadow_EditLights_Remove_f(void)
3106 {
3107         if (!r_editlights.integer)
3108         {
3109                 Con_Print("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
3110                 return;
3111         }
3112         if (!r_shadow_selectedlight)
3113         {
3114                 Con_Print("No selected light.\n");
3115                 return;
3116         }
3117         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
3118         r_shadow_selectedlight = NULL;
3119 }
3120
3121 void R_Shadow_EditLights_Help_f(void)
3122 {
3123         Con_Print(
3124 "Documentation on r_editlights system:\n"
3125 "Settings:\n"
3126 "r_editlights : enable/disable editing mode\n"
3127 "r_editlights_cursordistance : maximum distance of cursor from eye\n"
3128 "r_editlights_cursorpushback : push back cursor this far from surface\n"
3129 "r_editlights_cursorpushoff : push cursor off surface this far\n"
3130 "r_editlights_cursorgrid : snap cursor to grid of this size\n"
3131 "r_editlights_quakelightsizescale : imported quake light entity size scaling\n"
3132 "r_editlights_rtlightssizescale : imported rtlight size scaling\n"
3133 "r_editlights_rtlightscolorscale : imported rtlight color scaling\n"
3134 "Commands:\n"
3135 "r_editlights_help : this help\n"
3136 "r_editlights_clear : remove all lights\n"
3137 "r_editlights_reload : reload .rtlights, .lights file, or entities\n"
3138 "r_editlights_save : save to .rtlights file\n"
3139 "r_editlights_spawn : create a light with default settings\n"
3140 "r_editlights_edit command : edit selected light - more documentation below\n"
3141 "r_editlights_remove : remove selected light\n"
3142 "r_editlights_toggleshadow : toggles on/off selected light's shadow property\n"
3143 "r_editlights_importlightentitiesfrommap : reload light entities\n"
3144 "r_editlights_importlightsfile : reload .light file (produced by hlight)\n"
3145 "Edit commands:\n"
3146 "origin x y z : set light location\n"
3147 "originx x: set x component of light location\n"
3148 "originy y: set y component of light location\n"
3149 "originz z: set z component of light location\n"
3150 "move x y z : adjust light location\n"
3151 "movex x: adjust x component of light location\n"
3152 "movey y: adjust y component of light location\n"
3153 "movez z: adjust z component of light location\n"
3154 "angles x y z : set light angles\n"
3155 "anglesx x: set x component of light angles\n"
3156 "anglesy y: set y component of light angles\n"
3157 "anglesz z: set z component of light angles\n"
3158 "color r g b : set color of light (can be brighter than 1 1 1)\n"
3159 "radius radius : set radius (size) of light\n"
3160 "style style : set lightstyle of light (flickering patterns, switches, etc)\n"
3161 "cubemap basename : set filter cubemap of light (not yet supported)\n"
3162 "shadows 1/0 : turn on/off shadows\n"
3163 "corona n : set corona intensity\n"
3164 "<nothing> : print light properties to console\n"
3165         );
3166 }
3167
3168 void R_Shadow_EditLights_Init(void)
3169 {
3170         Cvar_RegisterVariable(&r_editlights);
3171         Cvar_RegisterVariable(&r_editlights_cursordistance);
3172         Cvar_RegisterVariable(&r_editlights_cursorpushback);
3173         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
3174         Cvar_RegisterVariable(&r_editlights_cursorgrid);
3175         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
3176         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
3177         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
3178         Cmd_AddCommand("r_editlights_help", R_Shadow_EditLights_Help_f);
3179         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
3180         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
3181         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
3182         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
3183         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
3184         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
3185         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
3186         Cmd_AddCommand("r_editlights_togglecorona", R_Shadow_EditLights_ToggleCorona_f);
3187         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
3188         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
3189 }
3190