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