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