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