]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
4ae9e42001c88272a3db9ee79f02144631a78cd0
[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).
18
19
20
21 Terminology: Stencil Light Volume (sometimes called Light Volumes)
22 Similar to a Stencil Shadow Volume, but inverted; rather than containing the
23 areas in shadow it contanis the areas in light, this can only be built
24 quickly for certain limited cases (such as portal visibility from a point),
25 but is quite useful for some effects (sunlight coming from sky polygons is
26 one possible example, translucent occluders is another example).
27
28
29
30 Terminology: Optimized Stencil Shadow Volume
31 A Stencil Shadow Volume that has been processed sufficiently to ensure it has
32 no duplicate coverage of areas (no need to shadow an area twice), often this
33 greatly improves performance but is an operation too costly to use on moving
34 lights (however completely optimal Stencil Light Volumes can be constructed
35 in some ideal cases).
36
37
38
39 Terminology: Per Pixel Lighting (sometimes abbreviated PPL)
40 Per pixel evaluation of lighting equations, at a bare minimum this involves
41 DOT3 shading of diffuse lighting (per pixel dotproduct of negated incidence
42 vector and surface normal, using a texture of the surface bumps, called a
43 NormalMap) if supported by hardware; in our case there is support for cards
44 which are incapable of DOT3, the quality is quite poor however.  Additionally
45 it is desirable to have specular evaluation per pixel, per vertex
46 normalization of specular halfangle vectors causes noticable distortion but
47 is unavoidable on hardware without GL_ARB_fragment_program.
48
49
50
51 Terminology: Normalization CubeMap
52 A cubemap containing normalized dot3-encoded (vectors of length 1 or less
53 encoded as RGB colors) for any possible direction, this technique allows per
54 pixel calculation of incidence vector for per pixel lighting purposes, which
55 would not otherwise be possible per pixel without GL_ARB_fragment_program.
56
57
58
59 Terminology: 2D Attenuation Texturing
60 A very crude approximation of light attenuation with distance which results
61 in cylindrical light shapes which fade vertically as a streak (some games
62 such as Doom3 allow this to be rotated to be less noticable in specific
63 cases), the technique is simply modulating lighting by two 2D textures (which
64 can be the same) on different axes of projection (XY and Z, typically), this
65 is the best technique available without 3D Attenuation Texturing or
66 GL_ARB_fragment_program technology.
67
68
69
70 Terminology: 3D Attenuation Texturing
71 A slightly crude approximation of light attenuation with distance, its flaws
72 are limited radius and resolution (performance tradeoffs).
73
74
75
76 Terminology: 3D Attenuation-Normalization Texturing
77 A 3D Attenuation Texture merged with a Normalization CubeMap, by making the
78 vectors shorter the lighting becomes darker, a very effective optimization of
79 diffuse lighting if 3D Attenuation Textures are already used.
80
81
82
83 Terminology: Light Cubemap Filtering
84 A technique for modeling non-uniform light distribution according to
85 direction, for example projecting a stained glass window image onto a wall,
86 this is done by texturing the lighting with a cubemap.
87
88
89
90 Terminology: Light Projection Filtering
91 A technique for modeling shadowing of light passing through translucent
92 surfaces, allowing stained glass windows and other effects to be done more
93 elegantly than possible with Light Cubemap Filtering by applying an occluder
94 texture to the lighting combined with a stencil light volume to limit the lit
95 area (this allows evaluating multiple translucent occluders in a scene).
96
97
98
99 Terminology: Doom3 Lighting
100 A combination of Stencil Shadow Volume, Per Pixel Lighting, Normalization
101 CubeMap, 2D Attenuation Texturing, and Light Filtering, as demonstrated by
102 the (currently upcoming) game Doom3.
103 */
104
105 #include "quakedef.h"
106 #include "r_shadow.h"
107 #include "cl_collision.h"
108 #include "portals.h"
109
110 extern void R_Shadow_EditLights_Init(void);
111
112 #define SHADOWSTAGE_NONE 0
113 #define SHADOWSTAGE_STENCIL 1
114 #define SHADOWSTAGE_LIGHT 2
115 #define SHADOWSTAGE_ERASESTENCIL 3
116
117 int r_shadowstage = SHADOWSTAGE_NONE;
118 int r_shadow_reloadlights = false;
119
120 mempool_t *r_shadow_mempool;
121
122 int maxshadowelements;
123 int *shadowelements;
124 int maxtrianglefacinglight;
125 qbyte *trianglefacinglight;
126 int *trianglefacinglightlist;
127
128 int maxvertexupdate;
129 int *vertexupdate;
130 int *vertexremap;
131 int vertexupdatenum;
132
133 rtexturepool_t *r_shadow_texturepool;
134 rtexture_t *r_shadow_normalcubetexture;
135 rtexture_t *r_shadow_attenuation2dtexture;
136 rtexture_t *r_shadow_attenuation3dtexture;
137 rtexture_t *r_shadow_blankbumptexture;
138 rtexture_t *r_shadow_blankglosstexture;
139 rtexture_t *r_shadow_blankwhitetexture;
140
141 cvar_t r_shadow_lightattenuationpower = {0, "r_shadow_lightattenuationpower", "0.5"};
142 cvar_t r_shadow_lightattenuationscale = {0, "r_shadow_lightattenuationscale", "1"};
143 cvar_t r_shadow_lightintensityscale = {0, "r_shadow_lightintensityscale", "1"};
144 cvar_t r_shadow_realtime_world = {0, "r_shadow_realtime_world", "0"};
145 cvar_t r_shadow_realtime_dlight = {0, "r_shadow_realtime_dlight", "0"};
146 cvar_t r_shadow_visiblevolumes = {0, "r_shadow_visiblevolumes", "0"};
147 cvar_t r_shadow_gloss = {0, "r_shadow_gloss", "1"};
148 cvar_t r_shadow_debuglight = {0, "r_shadow_debuglight", "-1"};
149 cvar_t r_shadow_scissor = {0, "r_shadow_scissor", "1"};
150 cvar_t r_shadow_bumpscale_bumpmap = {0, "r_shadow_bumpscale_bumpmap", "4"};
151 cvar_t r_shadow_bumpscale_basetexture = {0, "r_shadow_bumpscale_basetexture", "0"};
152 cvar_t r_shadow_polygonoffset = {0, "r_shadow_polygonoffset", "0"};
153 cvar_t r_shadow_portallight = {0, "r_shadow_portallight", "1"};
154 cvar_t r_shadow_projectdistance = {0, "r_shadow_projectdistance", "10000"};
155 cvar_t r_shadow_texture3d = {0, "r_shadow_texture3d", "1"};
156 cvar_t r_shadow_singlepassvolumegeneration = {0, "r_shadow_singlepassvolumegeneration", "1"};
157
158 int c_rt_lights, c_rt_clears, c_rt_scissored;
159 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
160 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
161
162 void R_Shadow_ClearWorldLights(void);
163 void R_Shadow_SaveWorldLights(void);
164 void R_Shadow_LoadWorldLights(void);
165 void R_Shadow_LoadLightsFile(void);
166 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
167
168 void r_shadow_start(void)
169 {
170         // allocate vertex processing arrays
171         r_shadow_mempool = Mem_AllocPool("R_Shadow");
172         maxshadowelements = 0;
173         shadowelements = NULL;
174         maxvertexupdate = 0;
175         vertexupdate = NULL;
176         vertexremap = NULL;
177         vertexupdatenum = 0;
178         maxtrianglefacinglight = 0;
179         trianglefacinglight = NULL;
180         trianglefacinglightlist = NULL;
181         r_shadow_normalcubetexture = NULL;
182         r_shadow_attenuation2dtexture = NULL;
183         r_shadow_attenuation3dtexture = NULL;
184         r_shadow_blankbumptexture = NULL;
185         r_shadow_blankglosstexture = NULL;
186         r_shadow_blankwhitetexture = NULL;
187         r_shadow_texturepool = NULL;
188         R_Shadow_ClearWorldLights();
189         r_shadow_reloadlights = true;
190 }
191
192 void r_shadow_shutdown(void)
193 {
194         R_Shadow_ClearWorldLights();
195         r_shadow_reloadlights = true;
196         r_shadow_normalcubetexture = NULL;
197         r_shadow_attenuation2dtexture = NULL;
198         r_shadow_attenuation3dtexture = NULL;
199         r_shadow_blankbumptexture = NULL;
200         r_shadow_blankglosstexture = NULL;
201         r_shadow_blankwhitetexture = NULL;
202         R_FreeTexturePool(&r_shadow_texturepool);
203         maxshadowelements = 0;
204         shadowelements = NULL;
205         maxvertexupdate = 0;
206         vertexupdate = NULL;
207         vertexremap = NULL;
208         vertexupdatenum = 0;
209         maxtrianglefacinglight = 0;
210         trianglefacinglight = NULL;
211         trianglefacinglightlist = NULL;
212         Mem_FreePool(&r_shadow_mempool);
213 }
214
215 void r_shadow_newmap(void)
216 {
217         R_Shadow_ClearWorldLights();
218         r_shadow_reloadlights = true;
219 }
220
221 void R_Shadow_Init(void)
222 {
223         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
224         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
225         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
226         Cvar_RegisterVariable(&r_shadow_realtime_world);
227         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
228         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
229         Cvar_RegisterVariable(&r_shadow_gloss);
230         Cvar_RegisterVariable(&r_shadow_debuglight);
231         Cvar_RegisterVariable(&r_shadow_scissor);
232         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
233         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
234         Cvar_RegisterVariable(&r_shadow_polygonoffset);
235         Cvar_RegisterVariable(&r_shadow_portallight);
236         Cvar_RegisterVariable(&r_shadow_projectdistance);
237         Cvar_RegisterVariable(&r_shadow_texture3d);
238         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
239         R_Shadow_EditLights_Init();
240         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
241 }
242
243 void R_Shadow_ResizeTriangleFacingLight(int numtris)
244 {
245         // make sure trianglefacinglight is big enough for this volume
246         // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
247         // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
248         if (maxtrianglefacinglight < numtris)
249         {
250                 maxtrianglefacinglight = numtris;
251                 if (trianglefacinglight)
252                         Mem_Free(trianglefacinglight);
253                 if (trianglefacinglightlist)
254                         Mem_Free(trianglefacinglightlist);
255                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
256                 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
257         }
258 }
259
260 int *R_Shadow_ResizeShadowElements(int numtris)
261 {
262         // make sure shadowelements is big enough for this volume
263         if (maxshadowelements < numtris * 24)
264         {
265                 maxshadowelements = numtris * 24;
266                 if (shadowelements)
267                         Mem_Free(shadowelements);
268                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
269         }
270         return shadowelements;
271 }
272
273 int R_Shadow_ConstructShadowVolume(int innumvertices, int trianglerange_start, int trianglerange_end, const int *inelement3i, const int *inneighbor3i, const float *invertex3f, int *outnumvertices, int *outelement3i, float *outvertex3f, const float *relativelightorigin, float projectdistance)
274 {
275         int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
276         const float *v[3];
277         const int *e, *n, *te;
278         float f, temp[3];
279
280         // make sure trianglefacinglight is big enough for this volume
281         if (maxtrianglefacinglight < trianglerange_end)
282                 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
283
284         if (maxvertexupdate < innumvertices)
285         {
286                 maxvertexupdate = innumvertices;
287                 if (vertexupdate)
288                         Mem_Free(vertexupdate);
289                 if (vertexremap)
290                         Mem_Free(vertexremap);
291                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
292                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
293         }
294         vertexupdatenum++;
295
296         if (r_shadow_singlepassvolumegeneration.integer)
297         {
298                 // one pass approach (identify lit/dark faces and generate sides while doing so)
299                 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
300                 {
301                         // calculate triangle facing flag
302                         v[0] = invertex3f + e[0] * 3;
303                         v[1] = invertex3f + e[1] * 3;
304                         v[2] = invertex3f + e[2] * 3;
305                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
306                         {
307                                 // make sure the vertices are created
308                                 for (j = 0;j < 3;j++)
309                                 {
310                                         if (vertexupdate[e[j]] != vertexupdatenum)
311                                         {
312                                                 vertexupdate[e[j]] = vertexupdatenum;
313                                                 vertexremap[e[j]] = outvertices;
314                                                 VectorCopy(v[j], outvertex3f);
315                                                 VectorSubtract(v[j], relativelightorigin, temp);
316                                                 f = projectdistance / VectorLength(temp);
317                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
318                                                 outvertex3f += 6;
319                                                 outvertices += 2;
320                                         }
321                                 }
322                                 // output the front and back triangles
323                                 vr[0] = vertexremap[e[0]];
324                                 vr[1] = vertexremap[e[1]];
325                                 vr[2] = vertexremap[e[2]];
326                                 outelement3i[0] = vr[0];
327                                 outelement3i[1] = vr[1];
328                                 outelement3i[2] = vr[2];
329                                 outelement3i[3] = vr[2] + 1;
330                                 outelement3i[4] = vr[1] + 1;
331                                 outelement3i[5] = vr[0] + 1;
332                                 outelement3i += 6;
333                                 tris += 2;
334                                 // output the sides (facing outward from this triangle)
335                                 t = n[0];
336                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
337                                 {
338                                         outelement3i[0] = vr[1];
339                                         outelement3i[1] = vr[0];
340                                         outelement3i[2] = vr[0] + 1;
341                                         outelement3i[3] = vr[1];
342                                         outelement3i[4] = vr[0] + 1;
343                                         outelement3i[5] = vr[1] + 1;
344                                         outelement3i += 6;
345                                         tris += 2;
346                                 }
347                                 t = n[1];
348                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
349                                 {
350                                         outelement3i[0] = vr[2];
351                                         outelement3i[1] = vr[1];
352                                         outelement3i[2] = vr[1] + 1;
353                                         outelement3i[3] = vr[2];
354                                         outelement3i[4] = vr[1] + 1;
355                                         outelement3i[5] = vr[2] + 1;
356                                         outelement3i += 6;
357                                         tris += 2;
358                                 }
359                                 t = n[2];
360                                 if ((t >= trianglerange_start && t < trianglerange_end) ? (t < i && !trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
361                                 {
362                                         outelement3i[0] = vr[0];
363                                         outelement3i[1] = vr[2];
364                                         outelement3i[2] = vr[2] + 1;
365                                         outelement3i[3] = vr[0];
366                                         outelement3i[4] = vr[2] + 1;
367                                         outelement3i[5] = vr[0] + 1;
368                                         outelement3i += 6;
369                                         tris += 2;
370                                 }
371                         }
372                         else
373                         {
374                                 // this triangle is not facing the light
375                                 // output the sides (facing inward to this triangle)
376                                 t = n[0];
377                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
378                                 {
379                                         vr[0] = vertexremap[e[0]];
380                                         vr[1] = vertexremap[e[1]];
381                                         outelement3i[0] = vr[1];
382                                         outelement3i[1] = vr[0] + 1;
383                                         outelement3i[2] = vr[0];
384                                         outelement3i[3] = vr[1];
385                                         outelement3i[4] = vr[1] + 1;
386                                         outelement3i[5] = vr[0] + 1;
387                                         outelement3i += 6;
388                                         tris += 2;
389                                 }
390                                 t = n[1];
391                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
392                                 {
393                                         vr[1] = vertexremap[e[1]];
394                                         vr[2] = vertexremap[e[2]];
395                                         outelement3i[0] = vr[2];
396                                         outelement3i[1] = vr[1] + 1;
397                                         outelement3i[2] = vr[1];
398                                         outelement3i[3] = vr[2];
399                                         outelement3i[4] = vr[2] + 1;
400                                         outelement3i[5] = vr[1] + 1;
401                                         outelement3i += 6;
402                                         tris += 2;
403                                 }
404                                 t = n[2];
405                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
406                                 {
407                                         vr[0] = vertexremap[e[0]];
408                                         vr[2] = vertexremap[e[2]];
409                                         outelement3i[0] = vr[0];
410                                         outelement3i[1] = vr[2] + 1;
411                                         outelement3i[2] = vr[2];
412                                         outelement3i[3] = vr[0];
413                                         outelement3i[4] = vr[0] + 1;
414                                         outelement3i[5] = vr[2] + 1;
415                                         outelement3i += 6;
416                                         tris += 2;
417                                 }
418                         }
419                 }
420         }
421         else
422         {
423                 // two pass approach (identify lit/dark faces and then generate sides)
424                 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
425                 {
426                         // calculate triangle facing flag
427                         v[0] = invertex3f + e[0] * 3;
428                         v[1] = invertex3f + e[1] * 3;
429                         v[2] = invertex3f + e[2] * 3;
430                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
431                         {
432                                 trianglefacinglightlist[numfacing++] = i;
433                                 // make sure the vertices are created
434                                 for (j = 0;j < 3;j++)
435                                 {
436                                         if (vertexupdate[e[j]] != vertexupdatenum)
437                                         {
438                                                 vertexupdate[e[j]] = vertexupdatenum;
439                                                 vertexremap[e[j]] = outvertices;
440                                                 VectorSubtract(v[j], relativelightorigin, temp);
441                                                 f = projectdistance / VectorLength(temp);
442                                                 VectorCopy(v[j], outvertex3f);
443                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
444                                                 outvertex3f += 6;
445                                                 outvertices += 2;
446                                         }
447                                 }
448                                 // output the front and back triangles
449                                 outelement3i[0] = vertexremap[e[0]];
450                                 outelement3i[1] = vertexremap[e[1]];
451                                 outelement3i[2] = vertexremap[e[2]];
452                                 outelement3i[3] = vertexremap[e[2]] + 1;
453                                 outelement3i[4] = vertexremap[e[1]] + 1;
454                                 outelement3i[5] = vertexremap[e[0]] + 1;
455                                 outelement3i += 6;
456                                 tris += 2;
457                         }
458                 }
459                 for (i = 0;i < numfacing;i++)
460                 {
461                         t = trianglefacinglightlist[i];
462                         e = inelement3i + t * 3;
463                         n = inneighbor3i + t * 3;
464                         // output the sides (facing outward from this triangle)
465                         t = n[0];
466                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
467                         {
468                                 vr[0] = vertexremap[e[0]];
469                                 vr[1] = vertexremap[e[1]];
470                                 outelement3i[0] = vr[1];
471                                 outelement3i[1] = vr[0];
472                                 outelement3i[2] = vr[0] + 1;
473                                 outelement3i[3] = vr[1];
474                                 outelement3i[4] = vr[0] + 1;
475                                 outelement3i[5] = vr[1] + 1;
476                                 outelement3i += 6;
477                                 tris += 2;
478                         }
479                         t = n[1];
480                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
481                         {
482                                 vr[1] = vertexremap[e[1]];
483                                 vr[2] = vertexremap[e[2]];
484                                 outelement3i[0] = vr[2];
485                                 outelement3i[1] = vr[1];
486                                 outelement3i[2] = vr[1] + 1;
487                                 outelement3i[3] = vr[2];
488                                 outelement3i[4] = vr[1] + 1;
489                                 outelement3i[5] = vr[2] + 1;
490                                 outelement3i += 6;
491                                 tris += 2;
492                         }
493                         t = n[2];
494                         if ((t >= trianglerange_start && t < trianglerange_end) ? (!trianglefacinglight[t]) : (t < 0 || (te = inelement3i + t * 3, v[0] = invertex3f + te[0] * 3, v[1] = invertex3f + te[1] * 3, v[2] = invertex3f + te[2] * 3, !PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
495                         {
496                                 vr[0] = vertexremap[e[0]];
497                                 vr[2] = vertexremap[e[2]];
498                                 outelement3i[0] = vr[0];
499                                 outelement3i[1] = vr[2];
500                                 outelement3i[2] = vr[2] + 1;
501                                 outelement3i[3] = vr[0];
502                                 outelement3i[4] = vr[2] + 1;
503                                 outelement3i[5] = vr[0] + 1;
504                                 outelement3i += 6;
505                                 tris += 2;
506                         }
507                 }
508         }
509         if (outnumvertices)
510                 *outnumvertices = outvertices;
511         return tris;
512 }
513
514 float varray_vertex3f2[65536*3];
515
516 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
517 {
518         int tris, outverts;
519         if (projectdistance < 0.1)
520         {
521                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
522                 return;
523         }
524         if (!numverts)
525                 return;
526
527         // make sure shadowelements is big enough for this volume
528         if (maxshadowelements < numtris * 24)
529                 R_Shadow_ResizeShadowElements(numtris);
530
531         // check which triangles are facing the light, and then output
532         // triangle elements and vertices...  by clever use of elements we
533         // can construct the whole shadow from the unprojected vertices and
534         // the projected vertices
535         if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
536         {
537                 GL_VertexPointer(varray_vertex3f2);
538                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
539                 {
540                         // increment stencil if backface is behind depthbuffer
541                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
542                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
543                         R_Mesh_Draw(outverts, tris, shadowelements);
544                         c_rt_shadowmeshes++;
545                         c_rt_shadowtris += numtris;
546                         // decrement stencil if frontface is behind depthbuffer
547                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
548                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
549                 }
550                 R_Mesh_Draw(outverts, tris, shadowelements);
551                 c_rt_shadowmeshes++;
552                 c_rt_shadowtris += numtris;
553         }
554 }
555
556 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
557 {
558         shadowmesh_t *mesh;
559         if (r_shadowstage == SHADOWSTAGE_STENCIL)
560         {
561                 // increment stencil if backface is behind depthbuffer
562                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
563                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
564                 for (mesh = firstmesh;mesh;mesh = mesh->next)
565                 {
566                         GL_VertexPointer(mesh->vertex3f);
567                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
568                         c_rtcached_shadowmeshes++;
569                         c_rtcached_shadowtris += mesh->numtriangles;
570                 }
571                 // decrement stencil if frontface is behind depthbuffer
572                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
573                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
574         }
575         for (mesh = firstmesh;mesh;mesh = mesh->next)
576         {
577                 GL_VertexPointer(mesh->vertex3f);
578                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
579                 c_rtcached_shadowmeshes++;
580                 c_rtcached_shadowtris += mesh->numtriangles;
581         }
582 }
583
584 float r_shadow_attenpower, r_shadow_attenscale;
585 static void R_Shadow_MakeTextures(void)
586 {
587         int x, y, z, d, side;
588         float v[3], s, t, intensity;
589         qbyte *data;
590         R_FreeTexturePool(&r_shadow_texturepool);
591         r_shadow_texturepool = R_AllocTexturePool();
592         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
593         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
594 #define NORMSIZE 64
595 #define ATTEN2DSIZE 64
596 #define ATTEN3DSIZE 32
597         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
598         data[0] = 128;
599         data[1] = 128;
600         data[2] = 255;
601         data[3] = 255;
602         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
603         data[0] = 255;
604         data[1] = 255;
605         data[2] = 255;
606         data[3] = 255;
607         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 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_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
613         if (gl_texturecubemap)
614         {
615                 for (side = 0;side < 6;side++)
616                 {
617                         for (y = 0;y < NORMSIZE;y++)
618                         {
619                                 for (x = 0;x < NORMSIZE;x++)
620                                 {
621                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
622                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
623                                         switch(side)
624                                         {
625                                         case 0:
626                                                 v[0] = 1;
627                                                 v[1] = -t;
628                                                 v[2] = -s;
629                                                 break;
630                                         case 1:
631                                                 v[0] = -1;
632                                                 v[1] = -t;
633                                                 v[2] = s;
634                                                 break;
635                                         case 2:
636                                                 v[0] = s;
637                                                 v[1] = 1;
638                                                 v[2] = t;
639                                                 break;
640                                         case 3:
641                                                 v[0] = s;
642                                                 v[1] = -1;
643                                                 v[2] = -t;
644                                                 break;
645                                         case 4:
646                                                 v[0] = s;
647                                                 v[1] = -t;
648                                                 v[2] = 1;
649                                                 break;
650                                         case 5:
651                                                 v[0] = -s;
652                                                 v[1] = -t;
653                                                 v[2] = -1;
654                                                 break;
655                                         }
656                                         intensity = 127.0f / sqrt(DotProduct(v, v));
657                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
658                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
659                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
660                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
661                                 }
662                         }
663                 }
664                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
665         }
666         else
667                 r_shadow_normalcubetexture = NULL;
668         for (y = 0;y < ATTEN2DSIZE;y++)
669         {
670                 for (x = 0;x < ATTEN2DSIZE;x++)
671                 {
672                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
673                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
674                         v[2] = 0;
675                         intensity = 1.0f - sqrt(DotProduct(v, v));
676                         if (intensity > 0)
677                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
678                         d = bound(0, intensity, 255);
679                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
680                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
681                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
682                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
683                 }
684         }
685         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
686         if (r_shadow_texture3d.integer)
687         {
688                 for (z = 0;z < ATTEN3DSIZE;z++)
689                 {
690                         for (y = 0;y < ATTEN3DSIZE;y++)
691                         {
692                                 for (x = 0;x < ATTEN3DSIZE;x++)
693                                 {
694                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
695                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
696                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
697                                         intensity = 1.0f - sqrt(DotProduct(v, v));
698                                         if (intensity > 0)
699                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
700                                         d = bound(0, intensity, 255);
701                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
702                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
703                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
704                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
705                                 }
706                         }
707                 }
708                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
709         }
710         Mem_Free(data);
711 }
712
713 void R_Shadow_Stage_Begin(void)
714 {
715         rmeshstate_t m;
716
717         if (r_shadow_texture3d.integer && !gl_texture3d)
718                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
719
720         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
721         if (!r_shadow_attenuation2dtexture
722          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
723          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
724          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
725                 R_Shadow_MakeTextures();
726
727         memset(&m, 0, sizeof(m));
728         GL_BlendFunc(GL_ONE, GL_ZERO);
729         GL_DepthMask(false);
730         GL_DepthTest(true);
731         R_Mesh_State_Texture(&m);
732         GL_Color(0, 0, 0, 1);
733         qglDisable(GL_SCISSOR_TEST);
734         r_shadowstage = SHADOWSTAGE_NONE;
735
736         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
737         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
738         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
739 }
740
741 void R_Shadow_LoadWorldLightsIfNeeded(void)
742 {
743         if (r_shadow_reloadlights && cl.worldmodel)
744         {
745                 R_Shadow_ClearWorldLights();
746                 r_shadow_reloadlights = false;
747                 R_Shadow_LoadWorldLights();
748                 if (r_shadow_worldlightchain == NULL)
749                 {
750                         R_Shadow_LoadLightsFile();
751                         if (r_shadow_worldlightchain == NULL)
752                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
753                 }
754         }
755 }
756
757 void R_Shadow_Stage_ShadowVolumes(void)
758 {
759         rmeshstate_t m;
760         memset(&m, 0, sizeof(m));
761         R_Mesh_State_Texture(&m);
762         GL_Color(1, 1, 1, 1);
763         qglColorMask(0, 0, 0, 0);
764         GL_BlendFunc(GL_ONE, GL_ZERO);
765         GL_DepthMask(false);
766         GL_DepthTest(true);
767         if (r_shadow_polygonoffset.value != 0)
768         {
769                 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
770                 qglEnable(GL_POLYGON_OFFSET_FILL);
771         }
772         else
773                 qglDisable(GL_POLYGON_OFFSET_FILL);
774         qglDepthFunc(GL_LESS);
775         qglEnable(GL_STENCIL_TEST);
776         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
777         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
778         r_shadowstage = SHADOWSTAGE_STENCIL;
779         qglClear(GL_STENCIL_BUFFER_BIT);
780         c_rt_clears++;
781         // LordHavoc note: many shadow volumes reside entirely inside the world
782         // (that is to say they are entirely bounded by their lit surfaces),
783         // which can be optimized by handling things as an inverted light volume,
784         // with the shadow boundaries of the world being simulated by an altered
785         // (129) bias to stencil clearing on such lights
786         // FIXME: generate inverted light volumes for use as shadow volumes and
787         // optimize for them as noted above
788 }
789
790 void R_Shadow_Stage_LightWithoutShadows(void)
791 {
792         rmeshstate_t m;
793         memset(&m, 0, sizeof(m));
794         R_Mesh_State_Texture(&m);
795         GL_BlendFunc(GL_ONE, GL_ONE);
796         GL_DepthMask(false);
797         GL_DepthTest(true);
798         qglDisable(GL_POLYGON_OFFSET_FILL);
799         GL_Color(1, 1, 1, 1);
800         qglColorMask(1, 1, 1, 1);
801         qglDepthFunc(GL_EQUAL);
802         qglDisable(GL_STENCIL_TEST);
803         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
804         qglStencilFunc(GL_EQUAL, 128, 0xFF);
805         r_shadowstage = SHADOWSTAGE_LIGHT;
806         c_rt_lights++;
807 }
808
809 void R_Shadow_Stage_LightWithShadows(void)
810 {
811         rmeshstate_t m;
812         memset(&m, 0, sizeof(m));
813         R_Mesh_State_Texture(&m);
814         GL_BlendFunc(GL_ONE, GL_ONE);
815         GL_DepthMask(false);
816         GL_DepthTest(true);
817         qglDisable(GL_POLYGON_OFFSET_FILL);
818         GL_Color(1, 1, 1, 1);
819         qglColorMask(1, 1, 1, 1);
820         qglDepthFunc(GL_EQUAL);
821         qglEnable(GL_STENCIL_TEST);
822         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
823         // only draw light where this geometry was already rendered AND the
824         // stencil is 128 (values other than this mean shadow)
825         qglStencilFunc(GL_EQUAL, 128, 0xFF);
826         r_shadowstage = SHADOWSTAGE_LIGHT;
827         c_rt_lights++;
828 }
829
830 void R_Shadow_Stage_End(void)
831 {
832         rmeshstate_t m;
833         memset(&m, 0, sizeof(m));
834         R_Mesh_State_Texture(&m);
835         GL_BlendFunc(GL_ONE, GL_ZERO);
836         GL_DepthMask(true);
837         GL_DepthTest(true);
838         qglDisable(GL_POLYGON_OFFSET_FILL);
839         GL_Color(1, 1, 1, 1);
840         qglColorMask(1, 1, 1, 1);
841         qglDisable(GL_SCISSOR_TEST);
842         qglDepthFunc(GL_LEQUAL);
843         qglDisable(GL_STENCIL_TEST);
844         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
845         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
846         r_shadowstage = SHADOWSTAGE_NONE;
847 }
848
849 #if 0
850 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
851 {
852         int i, ix1, iy1, ix2, iy2;
853         float x1, y1, x2, y2, x, y;
854         vec3_t smins, smaxs;
855         vec4_t v, v2;
856         if (!r_shadow_scissor.integer)
857                 return false;
858         // if view is inside the box, just say yes it's visible
859         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
860          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
861          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
862         {
863                 qglDisable(GL_SCISSOR_TEST);
864                 return false;
865         }
866         VectorSubtract(r_origin, origin, v);
867         if (DotProduct(v, v) < radius * radius)
868         {
869                 qglDisable(GL_SCISSOR_TEST);
870                 return false;
871         }
872         // create viewspace bbox
873         for (i = 0;i < 8;i++)
874         {
875                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
876                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
877                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
878                 v2[0] = DotProduct(v, vright);
879                 v2[1] = DotProduct(v, vup);
880                 v2[2] = DotProduct(v, vpn);
881                 if (i)
882                 {
883                         if (smins[0] > v2[0]) smins[0] = v2[0];
884                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
885                         if (smins[1] > v2[1]) smins[1] = v2[1];
886                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
887                         if (smins[2] > v2[2]) smins[2] = v2[2];
888                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
889                 }
890                 else
891                 {
892                         smins[0] = smaxs[0] = v2[0];
893                         smins[1] = smaxs[1] = v2[1];
894                         smins[2] = smaxs[2] = v2[2];
895                 }
896         }
897         // now we have a bbox in viewspace
898         // clip it to the viewspace version of the sphere
899         v[0] = origin[0] - r_origin[0];
900         v[1] = origin[1] - r_origin[1];
901         v[2] = origin[2] - r_origin[2];
902         v2[0] = DotProduct(v, vright);
903         v2[1] = DotProduct(v, vup);
904         v2[2] = DotProduct(v, vpn);
905         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
906         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
907         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
908         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
909         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
910         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
911         // clip it to the view plane
912         if (smins[2] < 1)
913                 smins[2] = 1;
914         // return true if that culled the box
915         if (smins[2] >= smaxs[2])
916                 return true;
917         // ok some of it is infront of the view, transform each corner back to
918         // worldspace and then to screenspace and make screen rect
919         // initialize these variables just to avoid compiler warnings
920         x1 = y1 = x2 = y2 = 0;
921         for (i = 0;i < 8;i++)
922         {
923                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
924                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
925                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
926                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
927                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
928                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
929                 v[3] = 1.0f;
930                 GL_TransformToScreen(v, v2);
931                 //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]);
932                 x = v2[0];
933                 y = v2[1];
934                 if (i)
935                 {
936                         if (x1 > x) x1 = x;
937                         if (x2 < x) x2 = x;
938                         if (y1 > y) y1 = y;
939                         if (y2 < y) y2 = y;
940                 }
941                 else
942                 {
943                         x1 = x2 = x;
944                         y1 = y2 = y;
945                 }
946         }
947         /*
948         // this code doesn't handle boxes with any points behind view properly
949         x1 = 1000;x2 = -1000;
950         y1 = 1000;y2 = -1000;
951         for (i = 0;i < 8;i++)
952         {
953                 v[0] = (i & 1) ? mins[0] : maxs[0];
954                 v[1] = (i & 2) ? mins[1] : maxs[1];
955                 v[2] = (i & 4) ? mins[2] : maxs[2];
956                 v[3] = 1.0f;
957                 GL_TransformToScreen(v, v2);
958                 //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]);
959                 if (v2[2] > 0)
960                 {
961                         x = v2[0];
962                         y = v2[1];
963
964                         if (x1 > x) x1 = x;
965                         if (x2 < x) x2 = x;
966                         if (y1 > y) y1 = y;
967                         if (y2 < y) y2 = y;
968                 }
969         }
970         */
971         ix1 = x1 - 1.0f;
972         iy1 = y1 - 1.0f;
973         ix2 = x2 + 1.0f;
974         iy2 = y2 + 1.0f;
975         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
976         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
977         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
978         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
979         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
980         if (ix2 <= ix1 || iy2 <= iy1)
981                 return true;
982         // set up the scissor rectangle
983         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
984         qglEnable(GL_SCISSOR_TEST);
985         c_rt_scissored++;
986         return false;
987 }
988 #endif
989
990 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
991 {
992         int i, ix1, iy1, ix2, iy2;
993         float x1, y1, x2, y2, x, y, f;
994         vec3_t smins, smaxs;
995         vec4_t v, v2;
996         if (!r_shadow_scissor.integer)
997                 return false;
998         // if view is inside the box, just say yes it's visible
999         // LordHavoc: for some odd reason scissor seems broken without stencil
1000         // (?!?  seems like a driver bug) so abort if gl_stencil is false
1001         if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1002         {
1003                 qglDisable(GL_SCISSOR_TEST);
1004                 return false;
1005         }
1006         for (i = 0;i < 3;i++)
1007         {
1008                 if (vpn[i] >= 0)
1009                 {
1010                         v[i] = mins[i];
1011                         v2[i] = maxs[i];
1012                 }
1013                 else
1014                 {
1015                         v[i] = maxs[i];
1016                         v2[i] = mins[i];
1017                 }
1018         }
1019         f = DotProduct(vpn, r_origin) + 1;
1020         if (DotProduct(vpn, v2) <= f)
1021         {
1022                 // entirely behind nearclip plane
1023                 return true;
1024         }
1025         if (DotProduct(vpn, v) >= f)
1026         {
1027                 // entirely infront of nearclip plane
1028                 x1 = y1 = x2 = y2 = 0;
1029                 for (i = 0;i < 8;i++)
1030                 {
1031                         v[0] = (i & 1) ? mins[0] : maxs[0];
1032                         v[1] = (i & 2) ? mins[1] : maxs[1];
1033                         v[2] = (i & 4) ? mins[2] : maxs[2];
1034                         v[3] = 1.0f;
1035                         GL_TransformToScreen(v, v2);
1036                         //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]);
1037                         x = v2[0];
1038                         y = v2[1];
1039                         if (i)
1040                         {
1041                                 if (x1 > x) x1 = x;
1042                                 if (x2 < x) x2 = x;
1043                                 if (y1 > y) y1 = y;
1044                                 if (y2 < y) y2 = y;
1045                         }
1046                         else
1047                         {
1048                                 x1 = x2 = x;
1049                                 y1 = y2 = y;
1050                         }
1051                 }
1052         }
1053         else
1054         {
1055                 // clipped by nearclip plane
1056                 // this is nasty and crude...
1057                 // create viewspace bbox
1058                 for (i = 0;i < 8;i++)
1059                 {
1060                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1061                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1062                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1063                         v2[0] = DotProduct(v, vright);
1064                         v2[1] = DotProduct(v, vup);
1065                         v2[2] = DotProduct(v, vpn);
1066                         if (i)
1067                         {
1068                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1069                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1070                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1071                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1072                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1073                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1074                         }
1075                         else
1076                         {
1077                                 smins[0] = smaxs[0] = v2[0];
1078                                 smins[1] = smaxs[1] = v2[1];
1079                                 smins[2] = smaxs[2] = v2[2];
1080                         }
1081                 }
1082                 // now we have a bbox in viewspace
1083                 // clip it to the view plane
1084                 if (smins[2] < 1)
1085                         smins[2] = 1;
1086                 // return true if that culled the box
1087                 if (smins[2] >= smaxs[2])
1088                         return true;
1089                 // ok some of it is infront of the view, transform each corner back to
1090                 // worldspace and then to screenspace and make screen rect
1091                 // initialize these variables just to avoid compiler warnings
1092                 x1 = y1 = x2 = y2 = 0;
1093                 for (i = 0;i < 8;i++)
1094                 {
1095                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1096                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1097                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1098                         v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1099                         v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1100                         v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1101                         v[3] = 1.0f;
1102                         GL_TransformToScreen(v, v2);
1103                         //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]);
1104                         x = v2[0];
1105                         y = v2[1];
1106                         if (i)
1107                         {
1108                                 if (x1 > x) x1 = x;
1109                                 if (x2 < x) x2 = x;
1110                                 if (y1 > y) y1 = y;
1111                                 if (y2 < y) y2 = y;
1112                         }
1113                         else
1114                         {
1115                                 x1 = x2 = x;
1116                                 y1 = y2 = y;
1117                         }
1118                 }
1119                 /*
1120                 // this code doesn't handle boxes with any points behind view properly
1121                 x1 = 1000;x2 = -1000;
1122                 y1 = 1000;y2 = -1000;
1123                 for (i = 0;i < 8;i++)
1124                 {
1125                         v[0] = (i & 1) ? mins[0] : maxs[0];
1126                         v[1] = (i & 2) ? mins[1] : maxs[1];
1127                         v[2] = (i & 4) ? mins[2] : maxs[2];
1128                         v[3] = 1.0f;
1129                         GL_TransformToScreen(v, v2);
1130                         //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]);
1131                         if (v2[2] > 0)
1132                         {
1133                                 x = v2[0];
1134                                 y = v2[1];
1135
1136                                 if (x1 > x) x1 = x;
1137                                 if (x2 < x) x2 = x;
1138                                 if (y1 > y) y1 = y;
1139                                 if (y2 < y) y2 = y;
1140                         }
1141                 }
1142                 */
1143         }
1144         ix1 = x1 - 1.0f;
1145         iy1 = y1 - 1.0f;
1146         ix2 = x2 + 1.0f;
1147         iy2 = y2 + 1.0f;
1148         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1149         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1150         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1151         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1152         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1153         if (ix2 <= ix1 || iy2 <= iy1)
1154                 return true;
1155         // set up the scissor rectangle
1156         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1157         qglEnable(GL_SCISSOR_TEST);
1158         c_rt_scissored++;
1159         return false;
1160 }
1161
1162 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1163 {
1164         float *color4f = varray_color4f;
1165         float dist, dot, intensity, v[3], n[3];
1166         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1167         {
1168                 Matrix4x4_Transform(m, vertex3f, v);
1169                 if ((dist = DotProduct(v, v)) < 1)
1170                 {
1171                         Matrix4x4_Transform3x3(m, normal3f, n);
1172                         if ((dot = DotProduct(n, v)) > 0)
1173                         {
1174                                 dist = sqrt(dist);
1175                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1176                                 VectorScale(lightcolor, intensity, color4f);
1177                                 color4f[3] = 1;
1178                         }
1179                         else
1180                         {
1181                                 VectorClear(color4f);
1182                                 color4f[3] = 1;
1183                         }
1184                 }
1185                 else
1186                 {
1187                         VectorClear(color4f);
1188                         color4f[3] = 1;
1189                 }
1190         }
1191 }
1192
1193 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1194 {
1195         float *color4f = varray_color4f;
1196         float dist, dot, intensity, v[3], n[3];
1197         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1198         {
1199                 Matrix4x4_Transform(m, vertex3f, v);
1200                 if ((dist = fabs(v[2])) < 1)
1201                 {
1202                         Matrix4x4_Transform3x3(m, normal3f, n);
1203                         if ((dot = DotProduct(n, v)) > 0)
1204                         {
1205                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1206                                 VectorScale(lightcolor, intensity, color4f);
1207                                 color4f[3] = 1;
1208                         }
1209                         else
1210                         {
1211                                 VectorClear(color4f);
1212                                 color4f[3] = 1;
1213                         }
1214                 }
1215                 else
1216                 {
1217                         VectorClear(color4f);
1218                         color4f[3] = 1;
1219                 }
1220         }
1221 }
1222
1223 // FIXME: this should be done in a vertex program when possible
1224 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1225 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1226 {
1227         do
1228         {
1229                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1230                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1231                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1232                 vertex3f += 3;
1233                 tc3f += 3;
1234         }
1235         while (--numverts);
1236 }
1237
1238 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1239 {
1240         do
1241         {
1242                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1243                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1244                 vertex3f += 3;
1245                 tc2f += 2;
1246         }
1247         while (--numverts);
1248 }
1249
1250 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)
1251 {
1252         int i;
1253         float lightdir[3];
1254         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1255         {
1256                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1257                 // the cubemap normalizes this for us
1258                 out3f[0] = DotProduct(svector3f, lightdir);
1259                 out3f[1] = DotProduct(tvector3f, lightdir);
1260                 out3f[2] = DotProduct(normal3f, lightdir);
1261         }
1262 }
1263
1264 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)
1265 {
1266         int i;
1267         float lightdir[3], eyedir[3], halfdir[3];
1268         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1269         {
1270                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1271                 VectorNormalizeFast(lightdir);
1272                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1273                 VectorNormalizeFast(eyedir);
1274                 VectorAdd(lightdir, eyedir, halfdir);
1275                 // the cubemap normalizes this for us
1276                 out3f[0] = DotProduct(svector3f, halfdir);
1277                 out3f[1] = DotProduct(tvector3f, halfdir);
1278                 out3f[2] = DotProduct(normal3f, halfdir);
1279         }
1280 }
1281
1282 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, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *basetexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1283 {
1284         int renders;
1285         float color[3], color2[3];
1286         rmeshstate_t m;
1287         GL_VertexPointer(vertex3f);
1288         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1289         {
1290                 if (!bumptexture)
1291                         bumptexture = r_shadow_blankbumptexture;
1292                 GL_Color(1,1,1,1);
1293                 // colorscale accounts for how much we multiply the brightness during combine
1294                 // mult is how many times the final pass of the lighting will be
1295                 // performed to get more brightness than otherwise possible
1296                 // limit mult to 64 for sanity sake
1297                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1298                 {
1299                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1300                         memset(&m, 0, sizeof(m));
1301                         m.tex[0] = R_GetTexture(bumptexture);
1302                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1303                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
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                         m.pointer_texcoord[2] = varray_texcoord3f[2];
1309                         R_Mesh_State_Texture(&m);
1310                         qglColorMask(0,0,0,1);
1311                         GL_BlendFunc(GL_ONE, GL_ZERO);
1312                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1313                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1314                         R_Mesh_Draw(numverts, numtriangles, elements);
1315                         c_rt_lightmeshes++;
1316                         c_rt_lighttris += numtriangles;
1317
1318                         memset(&m, 0, sizeof(m));
1319                         m.tex[0] = R_GetTexture(basetexture);
1320                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1321                         m.pointer_texcoord[0] = texcoord2f;
1322                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1323                         R_Mesh_State_Texture(&m);
1324                         qglColorMask(1,1,1,0);
1325                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1326                         if (lightcubemap)
1327                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1328                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1329                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1330                         {
1331                                 color[0] = bound(0, color2[0], 1);
1332                                 color[1] = bound(0, color2[1], 1);
1333                                 color[2] = bound(0, color2[2], 1);
1334                                 GL_Color(color[0], color[1], color[2], 1);
1335                                 R_Mesh_Draw(numverts, numtriangles, elements);
1336                                 c_rt_lightmeshes++;
1337                                 c_rt_lighttris += numtriangles;
1338                         }
1339                 }
1340                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1341                 {
1342                         // 1/2/2 3D combine path (original Radeon)
1343                         memset(&m, 0, sizeof(m));
1344                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1345                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1346                         R_Mesh_State_Texture(&m);
1347                         qglColorMask(0,0,0,1);
1348                         GL_BlendFunc(GL_ONE, GL_ZERO);
1349                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1350                         R_Mesh_Draw(numverts, numtriangles, elements);
1351                         c_rt_lightmeshes++;
1352                         c_rt_lighttris += numtriangles;
1353
1354                         memset(&m, 0, sizeof(m));
1355                         m.tex[0] = R_GetTexture(bumptexture);
1356                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1357                         m.texcombinergb[0] = GL_REPLACE;
1358                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1359                         m.pointer_texcoord[0] = texcoord2f;
1360                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1361                         R_Mesh_State_Texture(&m);
1362                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1363                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1364                         R_Mesh_Draw(numverts, numtriangles, elements);
1365                         c_rt_lightmeshes++;
1366                         c_rt_lighttris += numtriangles;
1367
1368                         memset(&m, 0, sizeof(m));
1369                         m.tex[0] = R_GetTexture(basetexture);
1370                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1371                         m.pointer_texcoord[0] = texcoord2f;
1372                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1373                         R_Mesh_State_Texture(&m);
1374                         qglColorMask(1,1,1,0);
1375                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1376                         if (lightcubemap)
1377                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1378                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1379                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1380                         {
1381                                 color[0] = bound(0, color2[0], 1);
1382                                 color[1] = bound(0, color2[1], 1);
1383                                 color[2] = bound(0, color2[2], 1);
1384                                 GL_Color(color[0], color[1], color[2], 1);
1385                                 R_Mesh_Draw(numverts, numtriangles, elements);
1386                                 c_rt_lightmeshes++;
1387                                 c_rt_lighttris += numtriangles;
1388                         }
1389                 }
1390                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1391                 {
1392                         // 2/2 3D combine path (original Radeon)
1393                         memset(&m, 0, sizeof(m));
1394                         m.tex[0] = R_GetTexture(bumptexture);
1395                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1396                         m.texcombinergb[0] = GL_REPLACE;
1397                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1398                         m.pointer_texcoord[0] = texcoord2f;
1399                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1400                         R_Mesh_State_Texture(&m);
1401                         qglColorMask(0,0,0,1);
1402                         GL_BlendFunc(GL_ONE, GL_ZERO);
1403                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1404                         R_Mesh_Draw(numverts, numtriangles, elements);
1405                         c_rt_lightmeshes++;
1406                         c_rt_lighttris += numtriangles;
1407
1408                         memset(&m, 0, sizeof(m));
1409                         m.tex[0] = R_GetTexture(basetexture);
1410                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1411                         m.pointer_texcoord[0] = texcoord2f;
1412                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1413                         R_Mesh_State_Texture(&m);
1414                         qglColorMask(1,1,1,0);
1415                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1416                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1417                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1418                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1419                         {
1420                                 color[0] = bound(0, color2[0], 1);
1421                                 color[1] = bound(0, color2[1], 1);
1422                                 color[2] = bound(0, color2[2], 1);
1423                                 GL_Color(color[0], color[1], color[2], 1);
1424                                 R_Mesh_Draw(numverts, numtriangles, elements);
1425                                 c_rt_lightmeshes++;
1426                                 c_rt_lighttris += numtriangles;
1427                         }
1428                 }
1429                 else if (r_textureunits.integer >= 4)
1430                 {
1431                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1432                         memset(&m, 0, sizeof(m));
1433                         m.tex[0] = R_GetTexture(bumptexture);
1434                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1435                         m.texcombinergb[0] = GL_REPLACE;
1436                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1437                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1438                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1439                         m.pointer_texcoord[0] = texcoord2f;
1440                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1441                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1442                         m.pointer_texcoord[3] = varray_texcoord2f[3];
1443                         R_Mesh_State_Texture(&m);
1444                         qglColorMask(0,0,0,1);
1445                         GL_BlendFunc(GL_ONE, GL_ZERO);
1446                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1447                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1448                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1449                         R_Mesh_Draw(numverts, numtriangles, elements);
1450                         c_rt_lightmeshes++;
1451                         c_rt_lighttris += numtriangles;
1452
1453                         memset(&m, 0, sizeof(m));
1454                         m.tex[0] = R_GetTexture(basetexture);
1455                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1456                         m.pointer_texcoord[0] = texcoord2f;
1457                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1458                         R_Mesh_State_Texture(&m);
1459                         qglColorMask(1,1,1,0);
1460                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1461                         if (lightcubemap)
1462                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1463                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1464                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1465                         {
1466                                 color[0] = bound(0, color2[0], 1);
1467                                 color[1] = bound(0, color2[1], 1);
1468                                 color[2] = bound(0, color2[2], 1);
1469                                 GL_Color(color[0], color[1], color[2], 1);
1470                                 R_Mesh_Draw(numverts, numtriangles, elements);
1471                                 c_rt_lightmeshes++;
1472                                 c_rt_lighttris += numtriangles;
1473                         }
1474                 }
1475                 else
1476                 {
1477                         // 2/2/2 2D combine path (any dot3 card)
1478                         memset(&m, 0, sizeof(m));
1479                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1480                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1481                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1482                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1483                         R_Mesh_State_Texture(&m);
1484                         qglColorMask(0,0,0,1);
1485                         GL_BlendFunc(GL_ONE, GL_ZERO);
1486                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1487                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1488                         R_Mesh_Draw(numverts, numtriangles, elements);
1489                         c_rt_lightmeshes++;
1490                         c_rt_lighttris += numtriangles;
1491
1492                         memset(&m, 0, sizeof(m));
1493                         m.tex[0] = R_GetTexture(bumptexture);
1494                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1495                         m.texcombinergb[0] = GL_REPLACE;
1496                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1497                         m.pointer_texcoord[0] = texcoord2f;
1498                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1499                         R_Mesh_State_Texture(&m);
1500                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1501                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1502                         R_Mesh_Draw(numverts, numtriangles, elements);
1503                         c_rt_lightmeshes++;
1504                         c_rt_lighttris += numtriangles;
1505
1506                         memset(&m, 0, sizeof(m));
1507                         m.tex[0] = R_GetTexture(basetexture);
1508                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1509                         m.pointer_texcoord[0] = texcoord2f;
1510                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1511                         R_Mesh_State_Texture(&m);
1512                         qglColorMask(1,1,1,0);
1513                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1514                         if (lightcubemap)
1515                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1516                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1517                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1518                         {
1519                                 color[0] = bound(0, color2[0], 1);
1520                                 color[1] = bound(0, color2[1], 1);
1521                                 color[2] = bound(0, color2[2], 1);
1522                                 GL_Color(color[0], color[1], color[2], 1);
1523                                 R_Mesh_Draw(numverts, numtriangles, elements);
1524                                 c_rt_lightmeshes++;
1525                                 c_rt_lighttris += numtriangles;
1526                         }
1527                 }
1528         }
1529         else
1530         {
1531                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1532                 GL_DepthMask(false);
1533                 GL_DepthTest(true);
1534                 GL_ColorPointer(varray_color4f);
1535                 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1536                 memset(&m, 0, sizeof(m));
1537                 m.tex[0] = R_GetTexture(basetexture);
1538                 m.pointer_texcoord[0] = texcoord2f;
1539                 if (r_textureunits.integer >= 2)
1540                 {
1541                         // voodoo2
1542                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1543                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1544                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1545                 }
1546                 R_Mesh_State_Texture(&m);
1547                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1548                 {
1549                         color[0] = bound(0, color2[0], 1);
1550                         color[1] = bound(0, color2[1], 1);
1551                         color[2] = bound(0, color2[2], 1);
1552                         if (r_textureunits.integer >= 2)
1553                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1554                         else
1555                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1556                         R_Mesh_Draw(numverts, numtriangles, elements);
1557                         c_rt_lightmeshes++;
1558                         c_rt_lighttris += numtriangles;
1559                 }
1560         }
1561 }
1562
1563 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, float lightradius, const float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz, rtexture_t *glosstexture, rtexture_t *bumptexture, rtexture_t *lightcubemap)
1564 {
1565         int renders;
1566         float color[3], color2[3];
1567         rmeshstate_t m;
1568         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1569                 return;
1570         if (!bumptexture)
1571                 bumptexture = r_shadow_blankbumptexture;
1572         if (!glosstexture)
1573                 glosstexture = r_shadow_blankglosstexture;
1574         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1575         {
1576                 GL_VertexPointer(vertex3f);
1577                 GL_Color(1,1,1,1);
1578                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1579                 {
1580                         // 2/0/0/1/2 3D combine blendsquare path
1581                         memset(&m, 0, sizeof(m));
1582                         m.tex[0] = R_GetTexture(bumptexture);
1583                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1584                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1585                         m.pointer_texcoord[0] = texcoord2f;
1586                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1587                         R_Mesh_State_Texture(&m);
1588                         qglColorMask(0,0,0,1);
1589                         // this squares the result
1590                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1591                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1592                         R_Mesh_Draw(numverts, numtriangles, elements);
1593                         c_rt_lightmeshes++;
1594                         c_rt_lighttris += numtriangles;
1595
1596                         memset(&m, 0, sizeof(m));
1597                         R_Mesh_State_Texture(&m);
1598                         // square alpha in framebuffer a few times to make it shiny
1599                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1600                         // these comments are a test run through this math for intensity 0.5
1601                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1602                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1603                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1604                         R_Mesh_Draw(numverts, numtriangles, elements);
1605                         c_rt_lightmeshes++;
1606                         c_rt_lighttris += numtriangles;
1607                         R_Mesh_Draw(numverts, numtriangles, elements);
1608                         c_rt_lightmeshes++;
1609                         c_rt_lighttris += numtriangles;
1610
1611                         memset(&m, 0, sizeof(m));
1612                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1613                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1614                         R_Mesh_State_Texture(&m);
1615                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1616                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1617                         R_Mesh_Draw(numverts, numtriangles, elements);
1618                         c_rt_lightmeshes++;
1619                         c_rt_lighttris += numtriangles;
1620
1621                         memset(&m, 0, sizeof(m));
1622                         m.tex[0] = R_GetTexture(glosstexture);
1623                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1624                         m.pointer_texcoord[0] = texcoord2f;
1625                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1626                         R_Mesh_State_Texture(&m);
1627                         qglColorMask(1,1,1,0);
1628                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1629                         if (lightcubemap)
1630                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1631                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1632                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1633                         {
1634                                 color[0] = bound(0, color2[0], 1);
1635                                 color[1] = bound(0, color2[1], 1);
1636                                 color[2] = bound(0, color2[2], 1);
1637                                 GL_Color(color[0], color[1], color[2], 1);
1638                                 R_Mesh_Draw(numverts, numtriangles, elements);
1639                                 c_rt_lightmeshes++;
1640                                 c_rt_lighttris += numtriangles;
1641                         }
1642                 }
1643                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1644                 {
1645                         // 2/0/0/2 3D combine blendsquare path
1646                         memset(&m, 0, sizeof(m));
1647                         m.tex[0] = R_GetTexture(bumptexture);
1648                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1649                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1650                         m.pointer_texcoord[0] = texcoord2f;
1651                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1652                         R_Mesh_State_Texture(&m);
1653                         qglColorMask(0,0,0,1);
1654                         // this squares the result
1655                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1656                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1657                         R_Mesh_Draw(numverts, numtriangles, elements);
1658                         c_rt_lightmeshes++;
1659                         c_rt_lighttris += numtriangles;
1660
1661                         memset(&m, 0, sizeof(m));
1662                         R_Mesh_State_Texture(&m);
1663                         // square alpha in framebuffer a few times to make it shiny
1664                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1665                         // these comments are a test run through this math for intensity 0.5
1666                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1667                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1668                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1669                         R_Mesh_Draw(numverts, numtriangles, elements);
1670                         c_rt_lightmeshes++;
1671                         c_rt_lighttris += numtriangles;
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.tex[0] = R_GetTexture(glosstexture);
1678                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1679                         m.pointer_texcoord[0] = texcoord2f;
1680                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1681                         R_Mesh_State_Texture(&m);
1682                         qglColorMask(1,1,1,0);
1683                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1684                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1685                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1686                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1687                         {
1688                                 color[0] = bound(0, color2[0], 1);
1689                                 color[1] = bound(0, color2[1], 1);
1690                                 color[2] = bound(0, color2[2], 1);
1691                                 GL_Color(color[0], color[1], color[2], 1);
1692                                 R_Mesh_Draw(numverts, numtriangles, elements);
1693                                 c_rt_lightmeshes++;
1694                                 c_rt_lighttris += numtriangles;
1695                         }
1696                 }
1697                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1698                 {
1699                         // 2/0/0/2/2 2D combine blendsquare path
1700                         memset(&m, 0, sizeof(m));
1701                         m.tex[0] = R_GetTexture(bumptexture);
1702                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1703                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1704                         m.pointer_texcoord[0] = texcoord2f;
1705                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1706                         R_Mesh_State_Texture(&m);
1707                         qglColorMask(0,0,0,1);
1708                         // this squares the result
1709                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1710                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1711                         R_Mesh_Draw(numverts, numtriangles, elements);
1712                         c_rt_lightmeshes++;
1713                         c_rt_lighttris += numtriangles;
1714
1715                         memset(&m, 0, sizeof(m));
1716                         R_Mesh_State_Texture(&m);
1717                         // square alpha in framebuffer a few times to make it shiny
1718                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1719                         // these comments are a test run through this math for intensity 0.5
1720                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1721                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1722                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1723                         R_Mesh_Draw(numverts, numtriangles, elements);
1724                         c_rt_lightmeshes++;
1725                         c_rt_lighttris += numtriangles;
1726                         R_Mesh_Draw(numverts, numtriangles, elements);
1727                         c_rt_lightmeshes++;
1728                         c_rt_lighttris += numtriangles;
1729
1730                         memset(&m, 0, sizeof(m));
1731                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1732                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1733                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1734                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1735                         R_Mesh_State_Texture(&m);
1736                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1737                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1738                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1739                         R_Mesh_Draw(numverts, numtriangles, elements);
1740                         c_rt_lightmeshes++;
1741                         c_rt_lighttris += numtriangles;
1742
1743                         memset(&m, 0, sizeof(m));
1744                         m.tex[0] = R_GetTexture(glosstexture);
1745                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1746                         m.pointer_texcoord[0] = texcoord2f;
1747                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1748                         R_Mesh_State_Texture(&m);
1749                         qglColorMask(1,1,1,0);
1750                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1751                         if (lightcubemap)
1752                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1753                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1754                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1755                         {
1756                                 color[0] = bound(0, color2[0], 1);
1757                                 color[1] = bound(0, color2[1], 1);
1758                                 color[2] = bound(0, color2[2], 1);
1759                                 GL_Color(color[0], color[1], color[2], 1);
1760                                 R_Mesh_Draw(numverts, numtriangles, elements);
1761                                 c_rt_lightmeshes++;
1762                                 c_rt_lighttris += numtriangles;
1763                         }
1764                 }
1765         }
1766 }
1767
1768 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1769 {
1770         R_Mesh_Matrix(matrix);
1771         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1772 }
1773
1774 cvar_t r_editlights = {0, "r_editlights", "0"};
1775 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1776 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1777 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1778 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1779 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1780 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1781 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1782 worldlight_t *r_shadow_worldlightchain;
1783 worldlight_t *r_shadow_selectedlight;
1784 vec3_t r_editlights_cursorlocation;
1785
1786 static int castshadowcount = 1;
1787 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1788 {
1789         int i, j, k, l, maxverts = 256, *mark, tris;
1790         float *vertex3f = NULL;
1791         worldlight_t *e;
1792         shadowmesh_t *mesh, *castmesh;
1793         mleaf_t *leaf;
1794         msurface_t *surf;
1795         qbyte *pvs;
1796         surfmesh_t *surfmesh;
1797
1798         if (radius < 15 || DotProduct(color, color) < 0.03)
1799         {
1800                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1801                 return;
1802         }
1803
1804         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1805         VectorCopy(origin, e->origin);
1806         VectorCopy(color, e->light);
1807         e->lightradius = radius;
1808         e->style = style;
1809         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1810         {
1811                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1812                 e->style = 0;
1813         }
1814         e->castshadows = castshadow;
1815
1816         e->cullradius = e->lightradius;
1817         for (k = 0;k < 3;k++)
1818         {
1819                 e->mins[k] = e->origin[k] - e->lightradius;
1820                 e->maxs[k] = e->origin[k] + e->lightradius;
1821         }
1822
1823         e->next = r_shadow_worldlightchain;
1824         r_shadow_worldlightchain = e;
1825         if (cubemapname && cubemapname[0])
1826         {
1827                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1828                 strcpy(e->cubemapname, cubemapname);
1829                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1830         }
1831         if (cl.worldmodel)
1832         {
1833                 castshadowcount++;
1834                 i = cl.worldmodel->PointContents(cl.worldmodel, e->origin);
1835                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1836                 {
1837                         qbyte *byteleafpvs;
1838                         qbyte *bytesurfacepvs;
1839
1840                         byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1841                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1842
1843                         Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1844
1845                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1846                                 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1847                                         leaf->worldnodeframe = castshadowcount;
1848
1849                         for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1850                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1851                                         surf->castshadow = castshadowcount;
1852
1853                         Mem_Free(byteleafpvs);
1854                         Mem_Free(bytesurfacepvs);
1855                 }
1856                 else
1857                 {
1858                         leaf = cl.worldmodel->PointInLeaf(cl.worldmodel, origin);
1859                         pvs = cl.worldmodel->LeafPVS(cl.worldmodel, leaf);
1860                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1861                         {
1862                                 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1863                                 {
1864                                         leaf->worldnodeframe = castshadowcount;
1865                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1866                                         {
1867                                                 surf = cl.worldmodel->surfaces + *mark;
1868                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1869                                                         surf->castshadow = castshadowcount;
1870                                         }
1871                                 }
1872                         }
1873                 }
1874
1875                 e->numleafs = 0;
1876                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1877                         if (leaf->worldnodeframe == castshadowcount)
1878                                 e->numleafs++;
1879                 e->numsurfaces = 0;
1880                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1881                         if (surf->castshadow == castshadowcount)
1882                                 e->numsurfaces++;
1883
1884                 if (e->numleafs)
1885                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1886                 if (e->numsurfaces)
1887                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1888                 e->numleafs = 0;
1889                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1890                         if (leaf->worldnodeframe == castshadowcount)
1891                                 e->leafs[e->numleafs++] = leaf;
1892                 e->numsurfaces = 0;
1893                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1894                         if (surf->castshadow == castshadowcount)
1895                                 e->surfaces[e->numsurfaces++] = surf;
1896
1897                 // find bounding box of lit leafs
1898                 VectorCopy(e->origin, e->mins);
1899                 VectorCopy(e->origin, e->maxs);
1900                 for (j = 0;j < e->numleafs;j++)
1901                 {
1902                         leaf = e->leafs[j];
1903                         for (k = 0;k < 3;k++)
1904                         {
1905                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1906                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1907                         }
1908                 }
1909
1910                 for (k = 0;k < 3;k++)
1911                 {
1912                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1913                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1914                 }
1915                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1916
1917                 if (e->castshadows)
1918                 {
1919                         castshadowcount++;
1920                         for (j = 0;j < e->numsurfaces;j++)
1921                         {
1922                                 surf = e->surfaces[j];
1923                                 if (surf->flags & SURF_SHADOWCAST)
1924                                 {
1925                                         surf->castshadow = castshadowcount;
1926                                         if (maxverts < surf->poly_numverts)
1927                                                 maxverts = surf->poly_numverts;
1928                                 }
1929                         }
1930                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1931                         // make a mesh to cast a shadow volume from
1932                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1933                         for (j = 0;j < e->numsurfaces;j++)
1934                                 if (e->surfaces[j]->castshadow == castshadowcount)
1935                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1936                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1937                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1938
1939                         // cast shadow volume from castmesh
1940                         for (mesh = castmesh;mesh;mesh = mesh->next)
1941                         {
1942                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1943
1944                                 if (maxverts < castmesh->numverts * 2)
1945                                 {
1946                                         maxverts = castmesh->numverts * 2;
1947                                         if (vertex3f)
1948                                                 Mem_Free(vertex3f);
1949                                         vertex3f = NULL;
1950                                 }
1951                                 if (vertex3f == NULL && maxverts > 0)
1952                                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1953
1954                                 // now that we have the buffers big enough, construct and add
1955                                 // the shadow volume mesh
1956                                 if ((tris = R_Shadow_ConstructShadowVolume(castmesh->numverts, 0, castmesh->numtriangles, castmesh->element3i, castmesh->neighbor3i, castmesh->vertex3f, NULL, shadowelements, vertex3f, e->origin, r_shadow_projectdistance.value)))
1957                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1958                         }
1959                         if (vertex3f)
1960                                 Mem_Free(vertex3f);
1961                         vertex3f = NULL;
1962                         // we're done with castmesh now
1963                         Mod_ShadowMesh_Free(castmesh);
1964                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1965                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1966                                 l += mesh->numtriangles;
1967                         Con_Printf("static shadow volume built containing %i triangles\n", l);
1968                 }
1969         }
1970         Con_Printf("%f %f %f, %f %f %f, %f, %f, %d, %d\n", e->mins[0], e->mins[1], e->mins[2], e->maxs[0], e->maxs[1], e->maxs[2], e->cullradius, e->lightradius, e->numleafs, e->numsurfaces);
1971 }
1972
1973 void R_Shadow_FreeWorldLight(worldlight_t *light)
1974 {
1975         worldlight_t **lightpointer;
1976         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1977         if (*lightpointer != light)
1978                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1979         *lightpointer = light->next;
1980         if (light->cubemapname)
1981                 Mem_Free(light->cubemapname);
1982         if (light->shadowvolume)
1983                 Mod_ShadowMesh_Free(light->shadowvolume);
1984         if (light->surfaces)
1985                 Mem_Free(light->surfaces);
1986         if (light->leafs)
1987                 Mem_Free(light->leafs);
1988         Mem_Free(light);
1989 }
1990
1991 void R_Shadow_ClearWorldLights(void)
1992 {
1993         while (r_shadow_worldlightchain)
1994                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1995         r_shadow_selectedlight = NULL;
1996 }
1997
1998 void R_Shadow_SelectLight(worldlight_t *light)
1999 {
2000         if (r_shadow_selectedlight)
2001                 r_shadow_selectedlight->selected = false;
2002         r_shadow_selectedlight = light;
2003         if (r_shadow_selectedlight)
2004                 r_shadow_selectedlight->selected = true;
2005 }
2006
2007 rtexture_t *lighttextures[5];
2008
2009 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2010 {
2011         float scale = r_editlights_cursorgrid.value * 0.5f;
2012         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2013 }
2014
2015 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2016 {
2017         float intensity;
2018         const worldlight_t *light;
2019         light = calldata1;
2020         intensity = 0.5;
2021         if (light->selected)
2022                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2023         if (!light->shadowvolume)
2024                 intensity *= 0.5f;
2025         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2026 }
2027
2028 void R_Shadow_DrawLightSprites(void)
2029 {
2030         int i;
2031         cachepic_t *pic;
2032         worldlight_t *light;
2033
2034         for (i = 0;i < 5;i++)
2035         {
2036                 lighttextures[i] = NULL;
2037                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2038                         lighttextures[i] = pic->tex;
2039         }
2040
2041         for (light = r_shadow_worldlightchain;light;light = light->next)
2042                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2043         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2044 }
2045
2046 void R_Shadow_SelectLightInView(void)
2047 {
2048         float bestrating, rating, temp[3];
2049         worldlight_t *best, *light;
2050         best = NULL;
2051         bestrating = 0;
2052         for (light = r_shadow_worldlightchain;light;light = light->next)
2053         {
2054                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2055                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2056                 if (rating >= 0.95)
2057                 {
2058                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2059                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2060                         {
2061                                 bestrating = rating;
2062                                 best = light;
2063                         }
2064                 }
2065         }
2066         R_Shadow_SelectLight(best);
2067 }
2068
2069 void R_Shadow_LoadWorldLights(void)
2070 {
2071         int n, a, style, shadow;
2072         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2073         float origin[3], radius, color[3];
2074         if (cl.worldmodel == NULL)
2075         {
2076                 Con_Printf("No map loaded.\n");
2077                 return;
2078         }
2079         FS_StripExtension(cl.worldmodel->name, name);
2080         strcat(name, ".rtlights");
2081         lightsstring = FS_LoadFile(name, false);
2082         if (lightsstring)
2083         {
2084                 s = lightsstring;
2085                 n = 0;
2086                 while (*s)
2087                 {
2088                         t = s;
2089                         while (*s && *s != '\n')
2090                                 s++;
2091                         if (!*s)
2092                                 break;
2093                         *s = 0;
2094                         shadow = true;
2095                         // check for modifier flags
2096                         if (*t == '!')
2097                         {
2098                                 shadow = false;
2099                                 t++;
2100                         }
2101                         a = sscanf(t, "%f %f %f %f %f %f %f %d %s", &origin[0], &origin[1], &origin[2], &radius, &color[0], &color[1], &color[2], &style, cubemapname);
2102                         if (a < 9)
2103                                 cubemapname[0] = 0;
2104                         *s = '\n';
2105                         if (a < 8)
2106                         {
2107                                 Con_Printf("found %d parameters on line %i, should be 8 or 9 parameters (origin[0] origin[1] origin[2] radius color[0] color[1] color[2] style cubemapname)\n", a, n + 1);
2108                                 break;
2109                         }
2110                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2111                         radius *= r_editlights_rtlightssizescale.value;
2112                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2113                         s++;
2114                         n++;
2115                 }
2116                 if (*s)
2117                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2118                 Mem_Free(lightsstring);
2119         }
2120 }
2121
2122 void R_Shadow_SaveWorldLights(void)
2123 {
2124         worldlight_t *light;
2125         int bufchars, bufmaxchars;
2126         char *buf, *oldbuf;
2127         char name[MAX_QPATH];
2128         char line[1024];
2129         if (!r_shadow_worldlightchain)
2130                 return;
2131         if (cl.worldmodel == NULL)
2132         {
2133                 Con_Printf("No map loaded.\n");
2134                 return;
2135         }
2136         FS_StripExtension(cl.worldmodel->name, name);
2137         strcat(name, ".rtlights");
2138         bufchars = bufmaxchars = 0;
2139         buf = NULL;
2140         for (light = r_shadow_worldlightchain;light;light = light->next)
2141         {
2142                 sprintf(line, "%s%g %g %g %g %g %g %g %d %s\n", light->castshadows ? "" : "!", light->origin[0], light->origin[1], light->origin[2], light->lightradius / r_editlights_rtlightssizescale.value, light->light[0] / r_editlights_rtlightscolorscale.value, light->light[1] / r_editlights_rtlightscolorscale.value, light->light[2] / r_editlights_rtlightscolorscale.value, light->style, light->cubemapname ? light->cubemapname : "");
2143                 if (bufchars + (int) strlen(line) > bufmaxchars)
2144                 {
2145                         bufmaxchars = bufchars + strlen(line) + 2048;
2146                         oldbuf = buf;
2147                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2148                         if (oldbuf)
2149                         {
2150                                 if (bufchars)
2151                                         memcpy(buf, oldbuf, bufchars);
2152                                 Mem_Free(oldbuf);
2153                         }
2154                 }
2155                 if (strlen(line))
2156                 {
2157                         memcpy(buf + bufchars, line, strlen(line));
2158                         bufchars += strlen(line);
2159                 }
2160         }
2161         if (bufchars)
2162                 FS_WriteFile(name, buf, bufchars);
2163         if (buf)
2164                 Mem_Free(buf);
2165 }
2166
2167 void R_Shadow_LoadLightsFile(void)
2168 {
2169         int n, a, style;
2170         char name[MAX_QPATH], *lightsstring, *s, *t;
2171         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2172         if (cl.worldmodel == NULL)
2173         {
2174                 Con_Printf("No map loaded.\n");
2175                 return;
2176         }
2177         FS_StripExtension(cl.worldmodel->name, name);
2178         strcat(name, ".lights");
2179         lightsstring = FS_LoadFile(name, false);
2180         if (lightsstring)
2181         {
2182                 s = lightsstring;
2183                 n = 0;
2184                 while (*s)
2185                 {
2186                         t = s;
2187                         while (*s && *s != '\n')
2188                                 s++;
2189                         if (!*s)
2190                                 break;
2191                         *s = 0;
2192                         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);
2193                         *s = '\n';
2194                         if (a < 14)
2195                         {
2196                                 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);
2197                                 break;
2198                         }
2199                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2200                         radius = bound(15, radius, 4096);
2201                         VectorScale(color, (2.0f / (8388608.0f)), color);
2202                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2203                         s++;
2204                         n++;
2205                 }
2206                 if (*s)
2207                         Con_Printf("invalid lights file \"%s\"\n", name);
2208                 Mem_Free(lightsstring);
2209         }
2210 }
2211
2212 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2213 {
2214         int entnum, style, islight;
2215         char key[256], value[1024];
2216         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2217         const char *data;
2218
2219         if (cl.worldmodel == NULL)
2220         {
2221                 Con_Printf("No map loaded.\n");
2222                 return;
2223         }
2224         data = cl.worldmodel->entities;
2225         if (!data)
2226                 return;
2227         for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2228         {
2229                 light = 0;
2230                 origin[0] = origin[1] = origin[2] = 0;
2231                 originhack[0] = originhack[1] = originhack[2] = 0;
2232                 color[0] = color[1] = color[2] = 1;
2233                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2234                 scale = 1;
2235                 style = 0;
2236                 islight = false;
2237                 while (1)
2238                 {
2239                         if (!COM_ParseToken(&data))
2240                                 break; // error
2241                         if (com_token[0] == '}')
2242                                 break; // end of entity
2243                         if (com_token[0] == '_')
2244                                 strcpy(key, com_token + 1);
2245                         else
2246                                 strcpy(key, com_token);
2247                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2248                                 key[strlen(key)-1] = 0;
2249                         if (!COM_ParseToken(&data))
2250                                 break; // error
2251                         strcpy(value, com_token);
2252
2253                         // now that we have the key pair worked out...
2254                         if (!strcmp("light", key))
2255                                 light = atof(value);
2256                         else if (!strcmp("origin", key))
2257                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2258                         else if (!strcmp("color", key))
2259                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2260                         else if (!strcmp("wait", key))
2261                                 scale = atof(value);
2262                         else if (!strcmp("classname", key))
2263                         {
2264                                 if (!strncmp(value, "light", 5))
2265                                 {
2266                                         islight = true;
2267                                         if (!strcmp(value, "light_fluoro"))
2268                                         {
2269                                                 originhack[0] = 0;
2270                                                 originhack[1] = 0;
2271                                                 originhack[2] = 0;
2272                                                 overridecolor[0] = 1;
2273                                                 overridecolor[1] = 1;
2274                                                 overridecolor[2] = 1;
2275                                         }
2276                                         if (!strcmp(value, "light_fluorospark"))
2277                                         {
2278                                                 originhack[0] = 0;
2279                                                 originhack[1] = 0;
2280                                                 originhack[2] = 0;
2281                                                 overridecolor[0] = 1;
2282                                                 overridecolor[1] = 1;
2283                                                 overridecolor[2] = 1;
2284                                         }
2285                                         if (!strcmp(value, "light_globe"))
2286                                         {
2287                                                 originhack[0] = 0;
2288                                                 originhack[1] = 0;
2289                                                 originhack[2] = 0;
2290                                                 overridecolor[0] = 1;
2291                                                 overridecolor[1] = 0.8;
2292                                                 overridecolor[2] = 0.4;
2293                                         }
2294                                         if (!strcmp(value, "light_flame_large_yellow"))
2295                                         {
2296                                                 originhack[0] = 0;
2297                                                 originhack[1] = 0;
2298                                                 originhack[2] = 48;
2299                                                 overridecolor[0] = 1;
2300                                                 overridecolor[1] = 0.5;
2301                                                 overridecolor[2] = 0.1;
2302                                         }
2303                                         if (!strcmp(value, "light_flame_small_yellow"))
2304                                         {
2305                                                 originhack[0] = 0;
2306                                                 originhack[1] = 0;
2307                                                 originhack[2] = 40;
2308                                                 overridecolor[0] = 1;
2309                                                 overridecolor[1] = 0.5;
2310                                                 overridecolor[2] = 0.1;
2311                                         }
2312                                         if (!strcmp(value, "light_torch_small_white"))
2313                                         {
2314                                                 originhack[0] = 0;
2315                                                 originhack[1] = 0;
2316                                                 originhack[2] = 40;
2317                                                 overridecolor[0] = 1;
2318                                                 overridecolor[1] = 0.5;
2319                                                 overridecolor[2] = 0.1;
2320                                         }
2321                                         if (!strcmp(value, "light_torch_small_walltorch"))
2322                                         {
2323                                                 originhack[0] = 0;
2324                                                 originhack[1] = 0;
2325                                                 originhack[2] = 40;
2326                                                 overridecolor[0] = 1;
2327                                                 overridecolor[1] = 0.5;
2328                                                 overridecolor[2] = 0.1;
2329                                         }
2330                                 }
2331                         }
2332                         else if (!strcmp("style", key))
2333                                 style = atoi(value);
2334                 }
2335                 if (light <= 0 && islight)
2336                         light = 300;
2337                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2338                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2339                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2340                         VectorCopy(overridecolor, color);
2341                 VectorScale(color, light, color);
2342                 VectorAdd(origin, originhack, origin);
2343                 if (radius >= 15)
2344                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2345         }
2346 }
2347
2348
2349 void R_Shadow_SetCursorLocationForView(void)
2350 {
2351         vec_t dist, push, frac;
2352         vec3_t dest, endpos, normal;
2353         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2354         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2355         if (frac < 1)
2356         {
2357                 dist = frac * r_editlights_cursordistance.value;
2358                 push = r_editlights_cursorpushback.value;
2359                 if (push > dist)
2360                         push = dist;
2361                 push = -push;
2362                 VectorMA(endpos, push, vpn, endpos);
2363                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2364         }
2365         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2366         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2367         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2368 }
2369
2370 void R_Shadow_UpdateWorldLightSelection(void)
2371 {
2372         R_Shadow_SetCursorLocationForView();
2373         if (r_editlights.integer)
2374         {
2375                 R_Shadow_SelectLightInView();
2376                 R_Shadow_DrawLightSprites();
2377         }
2378         else
2379                 R_Shadow_SelectLight(NULL);
2380 }
2381
2382 void R_Shadow_EditLights_Clear_f(void)
2383 {
2384         R_Shadow_ClearWorldLights();
2385 }
2386
2387 void R_Shadow_EditLights_Reload_f(void)
2388 {
2389         r_shadow_reloadlights = true;
2390 }
2391
2392 void R_Shadow_EditLights_Save_f(void)
2393 {
2394         if (cl.worldmodel)
2395                 R_Shadow_SaveWorldLights();
2396 }
2397
2398 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2399 {
2400         R_Shadow_ClearWorldLights();
2401         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2402 }
2403
2404 void R_Shadow_EditLights_ImportLightsFile_f(void)
2405 {
2406         R_Shadow_ClearWorldLights();
2407         R_Shadow_LoadLightsFile();
2408 }
2409
2410 void R_Shadow_EditLights_Spawn_f(void)
2411 {
2412         vec3_t color;
2413         if (!r_editlights.integer)
2414         {
2415                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2416                 return;
2417         }
2418         if (Cmd_Argc() != 1)
2419         {
2420                 Con_Printf("r_editlights_spawn does not take parameters\n");
2421                 return;
2422         }
2423         color[0] = color[1] = color[2] = 1;
2424         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2425 }
2426
2427 void R_Shadow_EditLights_Edit_f(void)
2428 {
2429         vec3_t origin, color;
2430         vec_t radius;
2431         int style, shadows;
2432         char cubemapname[1024];
2433         if (!r_editlights.integer)
2434         {
2435                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2436                 return;
2437         }
2438         if (!r_shadow_selectedlight)
2439         {
2440                 Con_Printf("No selected light.\n");
2441                 return;
2442         }
2443         VectorCopy(r_shadow_selectedlight->origin, origin);
2444         radius = r_shadow_selectedlight->lightradius;
2445         VectorCopy(r_shadow_selectedlight->light, color);
2446         style = r_shadow_selectedlight->style;
2447         if (r_shadow_selectedlight->cubemapname)
2448                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2449         else
2450                 cubemapname[0] = 0;
2451         shadows = r_shadow_selectedlight->castshadows;
2452         if (!strcmp(Cmd_Argv(1), "origin"))
2453         {
2454                 if (Cmd_Argc() != 5)
2455                 {
2456                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2457                         return;
2458                 }
2459                 origin[0] = atof(Cmd_Argv(2));
2460                 origin[1] = atof(Cmd_Argv(3));
2461                 origin[2] = atof(Cmd_Argv(4));
2462         }
2463         else if (!strcmp(Cmd_Argv(1), "originx"))
2464         {
2465                 if (Cmd_Argc() != 3)
2466                 {
2467                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2468                         return;
2469                 }
2470                 origin[0] = atof(Cmd_Argv(2));
2471         }
2472         else if (!strcmp(Cmd_Argv(1), "originy"))
2473         {
2474                 if (Cmd_Argc() != 3)
2475                 {
2476                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2477                         return;
2478                 }
2479                 origin[1] = atof(Cmd_Argv(2));
2480         }
2481         else if (!strcmp(Cmd_Argv(1), "originz"))
2482         {
2483                 if (Cmd_Argc() != 3)
2484                 {
2485                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2486                         return;
2487                 }
2488                 origin[2] = atof(Cmd_Argv(2));
2489         }
2490         else if (!strcmp(Cmd_Argv(1), "move"))
2491         {
2492                 if (Cmd_Argc() != 5)
2493                 {
2494                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2495                         return;
2496                 }
2497                 origin[0] += atof(Cmd_Argv(2));
2498                 origin[1] += atof(Cmd_Argv(3));
2499                 origin[2] += atof(Cmd_Argv(4));
2500         }
2501         else if (!strcmp(Cmd_Argv(1), "movex"))
2502         {
2503                 if (Cmd_Argc() != 3)
2504                 {
2505                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2506                         return;
2507                 }
2508                 origin[0] += atof(Cmd_Argv(2));
2509         }
2510         else if (!strcmp(Cmd_Argv(1), "movey"))
2511         {
2512                 if (Cmd_Argc() != 3)
2513                 {
2514                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2515                         return;
2516                 }
2517                 origin[1] += atof(Cmd_Argv(2));
2518         }
2519         else if (!strcmp(Cmd_Argv(1), "movez"))
2520         {
2521                 if (Cmd_Argc() != 3)
2522                 {
2523                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2524                         return;
2525                 }
2526                 origin[2] += atof(Cmd_Argv(2));
2527         }
2528         else if (!strcmp(Cmd_Argv(1), "color"))
2529         {
2530                 if (Cmd_Argc() != 5)
2531                 {
2532                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2533                         return;
2534                 }
2535                 color[0] = atof(Cmd_Argv(2));
2536                 color[1] = atof(Cmd_Argv(3));
2537                 color[2] = atof(Cmd_Argv(4));
2538         }
2539         else if (!strcmp(Cmd_Argv(1), "radius"))
2540         {
2541                 if (Cmd_Argc() != 3)
2542                 {
2543                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2544                         return;
2545                 }
2546                 radius = atof(Cmd_Argv(2));
2547         }
2548         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2549         {
2550                 if (Cmd_Argc() != 3)
2551                 {
2552                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2553                         return;
2554                 }
2555                 style = atoi(Cmd_Argv(2));
2556         }
2557         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2558         {
2559                 if (Cmd_Argc() > 3)
2560                 {
2561                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2562                         return;
2563                 }
2564                 if (Cmd_Argc() == 3)
2565                         strcpy(cubemapname, Cmd_Argv(2));
2566                 else
2567                         cubemapname[0] = 0;
2568         }
2569         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2570         {
2571                 if (Cmd_Argc() != 3)
2572                 {
2573                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2574                         return;
2575                 }
2576                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2577         }
2578         else
2579         {
2580                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2581                 Con_Printf("Selected light's properties:\n");
2582                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2583                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2584                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2585                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2586                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2587                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2588                 return;
2589         }
2590         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2591         r_shadow_selectedlight = NULL;
2592         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2593 }
2594
2595 extern int con_vislines;
2596 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2597 {
2598         float x, y;
2599         char temp[256];
2600         if (r_shadow_selectedlight == NULL)
2601                 return;
2602         x = 0;
2603         y = con_vislines;
2604         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2605         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;
2606         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2607         sprintf(temp, "Color %f %f %f", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2608         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2609         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2610         sprintf(temp, "Shadows %s", r_shadow_selectedlight->castshadows ? "yes" : "no");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2611 }
2612
2613 void R_Shadow_EditLights_ToggleShadow_f(void)
2614 {
2615         if (!r_editlights.integer)
2616         {
2617                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2618                 return;
2619         }
2620         if (!r_shadow_selectedlight)
2621         {
2622                 Con_Printf("No selected light.\n");
2623                 return;
2624         }
2625         R_Shadow_NewWorldLight(r_shadow_selectedlight->origin, r_shadow_selectedlight->lightradius, r_shadow_selectedlight->light, r_shadow_selectedlight->style, r_shadow_selectedlight->cubemapname, !r_shadow_selectedlight->castshadows);
2626         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2627         r_shadow_selectedlight = NULL;
2628 }
2629
2630 void R_Shadow_EditLights_Remove_f(void)
2631 {
2632         if (!r_editlights.integer)
2633         {
2634                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2635                 return;
2636         }
2637         if (!r_shadow_selectedlight)
2638         {
2639                 Con_Printf("No selected light.\n");
2640                 return;
2641         }
2642         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2643         r_shadow_selectedlight = NULL;
2644 }
2645
2646 void R_Shadow_EditLights_Init(void)
2647 {
2648         Cvar_RegisterVariable(&r_editlights);
2649         Cvar_RegisterVariable(&r_editlights_cursordistance);
2650         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2651         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2652         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2653         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2654         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2655         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2656         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2657         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2658         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2659         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2660         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2661         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2662         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2663         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2664         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2665 }