]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
redesigned how the renderer handles much of it's state (R_Mesh_State turned into...
[divverent/darkplaces.git] / r_shadow.c
1
2 /*
3 Terminology: Stencil Shadow Volume (sometimes called Stencil Shadows)
4 An extrusion of the lit faces, beginning at the original geometry and ending
5 further from the light source than the original geometry (presumably at least
6 as far as the light's radius, if the light has a radius at all), capped at
7 both front and back to avoid any problems (extrusion from dark faces also
8 works but has a different set of problems)
9
10 This is rendered using Carmack's Reverse technique, in which backfaces behind
11 zbuffer (zfail) increment the stencil, and frontfaces behind zbuffer (zfail)
12 decrement the stencil, the result is a stencil value of zero where shadows
13 did not intersect the visible geometry, suitable as a stencil mask for
14 rendering lighting everywhere but shadow.
15
16 In our case we use a biased stencil clear of 128 to avoid requiring the
17 stencil wrap extension (but probably should support it).
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[3] = vertexremap[e[0]];
450                                 outelement3i[4] = vertexremap[e[1]];
451                                 outelement3i[5] = vertexremap[e[2]];
452                                 outelement3i[0] = vertexremap[e[2]] + 1;
453                                 outelement3i[1] = vertexremap[e[1]] + 1;
454                                 outelement3i[2] = 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, 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         rmeshstate_t m;
560         memset(&m, 0, sizeof(m));
561         if (r_shadowstage == SHADOWSTAGE_STENCIL)
562         {
563                 // increment stencil if backface is behind depthbuffer
564                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
565                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
566                 for (mesh = firstmesh;mesh;mesh = mesh->next)
567                 {
568                         GL_VertexPointer(mesh->vertex3f);
569                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
570                         c_rtcached_shadowmeshes++;
571                         c_rtcached_shadowtris += mesh->numtriangles;
572                 }
573                 // decrement stencil if frontface is behind depthbuffer
574                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
575                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
576         }
577         for (mesh = firstmesh;mesh;mesh = mesh->next)
578         {
579                 GL_VertexPointer(mesh->vertex3f);
580                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
581                 c_rtcached_shadowmeshes++;
582                 c_rtcached_shadowtris += mesh->numtriangles;
583         }
584 }
585
586 float r_shadow_attenpower, r_shadow_attenscale;
587 static void R_Shadow_MakeTextures(void)
588 {
589         int x, y, z, d, side;
590         float v[3], s, t, intensity;
591         qbyte *data;
592         R_FreeTexturePool(&r_shadow_texturepool);
593         r_shadow_texturepool = R_AllocTexturePool();
594         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
595         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
596 #define NORMSIZE 64
597 #define ATTEN2DSIZE 64
598 #define ATTEN3DSIZE 32
599         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
600         data[0] = 128;
601         data[1] = 128;
602         data[2] = 255;
603         data[3] = 255;
604         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
605         data[0] = 255;
606         data[1] = 255;
607         data[2] = 255;
608         data[3] = 255;
609         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
610         data[0] = 255;
611         data[1] = 255;
612         data[2] = 255;
613         data[3] = 255;
614         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
615         if (gl_texturecubemap)
616         {
617                 for (side = 0;side < 6;side++)
618                 {
619                         for (y = 0;y < NORMSIZE;y++)
620                         {
621                                 for (x = 0;x < NORMSIZE;x++)
622                                 {
623                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
624                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
625                                         switch(side)
626                                         {
627                                         case 0:
628                                                 v[0] = 1;
629                                                 v[1] = -t;
630                                                 v[2] = -s;
631                                                 break;
632                                         case 1:
633                                                 v[0] = -1;
634                                                 v[1] = -t;
635                                                 v[2] = s;
636                                                 break;
637                                         case 2:
638                                                 v[0] = s;
639                                                 v[1] = 1;
640                                                 v[2] = t;
641                                                 break;
642                                         case 3:
643                                                 v[0] = s;
644                                                 v[1] = -1;
645                                                 v[2] = -t;
646                                                 break;
647                                         case 4:
648                                                 v[0] = s;
649                                                 v[1] = -t;
650                                                 v[2] = 1;
651                                                 break;
652                                         case 5:
653                                                 v[0] = -s;
654                                                 v[1] = -t;
655                                                 v[2] = -1;
656                                                 break;
657                                         }
658                                         intensity = 127.0f / sqrt(DotProduct(v, v));
659                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
660                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
661                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
662                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
663                                 }
664                         }
665                 }
666                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
667         }
668         else
669                 r_shadow_normalcubetexture = NULL;
670         for (y = 0;y < ATTEN2DSIZE;y++)
671         {
672                 for (x = 0;x < ATTEN2DSIZE;x++)
673                 {
674                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
675                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
676                         v[2] = 0;
677                         intensity = 1.0f - sqrt(DotProduct(v, v));
678                         if (intensity > 0)
679                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
680                         d = bound(0, intensity, 255);
681                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
682                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
683                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
684                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
685                 }
686         }
687         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
688         if (r_shadow_texture3d.integer)
689         {
690                 for (z = 0;z < ATTEN3DSIZE;z++)
691                 {
692                         for (y = 0;y < ATTEN3DSIZE;y++)
693                         {
694                                 for (x = 0;x < ATTEN3DSIZE;x++)
695                                 {
696                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
697                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
698                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
699                                         intensity = 1.0f - sqrt(DotProduct(v, v));
700                                         if (intensity > 0)
701                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
702                                         d = bound(0, intensity, 255);
703                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
704                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
705                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
706                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
707                                 }
708                         }
709                 }
710                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
711         }
712         Mem_Free(data);
713 }
714
715 void R_Shadow_Stage_Begin(void)
716 {
717         rmeshstate_t m;
718
719         if (r_shadow_texture3d.integer && !gl_texture3d)
720                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
721
722         //cl.worldmodel->numlights = min(cl.worldmodel->numlights, 1);
723         if (!r_shadow_attenuation2dtexture
724          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
725          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
726          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
727                 R_Shadow_MakeTextures();
728
729         GL_BlendFunc(GL_ONE, GL_ZERO);
730         GL_DepthMask(false);
731         GL_DepthTest(true);
732         GL_Color(0, 0, 0, 1);
733
734         memset(&m, 0, sizeof(m));
735         R_Mesh_State_Texture(&m);
736
737         qglDisable(GL_SCISSOR_TEST);
738         r_shadowstage = SHADOWSTAGE_NONE;
739
740         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
741         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
742         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
743 }
744
745 void R_Shadow_LoadWorldLightsIfNeeded(void)
746 {
747         if (r_shadow_reloadlights && cl.worldmodel)
748         {
749                 R_Shadow_ClearWorldLights();
750                 r_shadow_reloadlights = false;
751                 R_Shadow_LoadWorldLights();
752                 if (r_shadow_worldlightchain == NULL)
753                 {
754                         R_Shadow_LoadLightsFile();
755                         if (r_shadow_worldlightchain == NULL)
756                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
757                 }
758         }
759 }
760
761 void R_Shadow_Stage_ShadowVolumes(void)
762 {
763         rmeshstate_t m;
764
765         memset(&m, 0, sizeof(m));
766         R_Mesh_State_Texture(&m);
767
768         GL_Color(1, 1, 1, 1);
769         GL_BlendFunc(GL_ONE, GL_ZERO);
770         GL_DepthMask(false);
771         GL_DepthTest(true);
772
773         if (r_shadow_polygonoffset.value != 0)
774         {
775                 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
776                 qglEnable(GL_POLYGON_OFFSET_FILL);
777         }
778         else
779                 qglDisable(GL_POLYGON_OFFSET_FILL);
780         qglColorMask(0, 0, 0, 0);
781         qglDepthFunc(GL_LESS);
782         qglEnable(GL_STENCIL_TEST);
783         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
784         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
785         r_shadowstage = SHADOWSTAGE_STENCIL;
786
787         qglClear(GL_STENCIL_BUFFER_BIT);
788         c_rt_clears++;
789         // LordHavoc note: many shadow volumes reside entirely inside the world
790         // (that is to say they are entirely bounded by their lit surfaces),
791         // which can be optimized by handling things as an inverted light volume,
792         // with the shadow boundaries of the world being simulated by an altered
793         // (129) bias to stencil clearing on such lights
794         // FIXME: generate inverted light volumes for use as shadow volumes and
795         // optimize for them as noted above
796 }
797
798 void R_Shadow_Stage_LightWithoutShadows(void)
799 {
800         rmeshstate_t m;
801
802         memset(&m, 0, sizeof(m));
803         R_Mesh_State_Texture(&m);
804
805         GL_Color(1, 1, 1, 1);
806         GL_BlendFunc(GL_ONE, GL_ONE);
807         GL_DepthMask(false);
808         GL_DepthTest(true);
809         qglDisable(GL_POLYGON_OFFSET_FILL);
810
811         //GL_DepthTest(false);
812
813         qglColorMask(1, 1, 1, 1);
814         qglDepthFunc(GL_LEQUAL);
815         qglDisable(GL_STENCIL_TEST);
816         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
817         qglStencilFunc(GL_EQUAL, 128, 0xFF);
818
819         r_shadowstage = SHADOWSTAGE_LIGHT;
820         c_rt_lights++;
821 }
822
823 void R_Shadow_Stage_LightWithShadows(void)
824 {
825         rmeshstate_t m;
826
827         memset(&m, 0, sizeof(m));
828         R_Mesh_State_Texture(&m);
829
830         GL_Color(1, 1, 1, 1);
831         GL_BlendFunc(GL_ONE, GL_ONE);
832         GL_DepthMask(false);
833         GL_DepthTest(true);
834         qglDisable(GL_POLYGON_OFFSET_FILL);
835
836         //GL_DepthTest(false);
837
838         qglColorMask(1, 1, 1, 1);
839         qglDepthFunc(GL_LEQUAL);
840         qglEnable(GL_STENCIL_TEST);
841         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
842         // only draw light where this geometry was already rendered AND the
843         // stencil is 128 (values other than this mean shadow)
844         qglStencilFunc(GL_EQUAL, 128, 0xFF);
845
846         r_shadowstage = SHADOWSTAGE_LIGHT;
847         c_rt_lights++;
848 }
849
850 void R_Shadow_Stage_End(void)
851 {
852         rmeshstate_t m;
853
854         memset(&m, 0, sizeof(m));
855         R_Mesh_State_Texture(&m);
856
857         GL_Color(1, 1, 1, 1);
858         GL_BlendFunc(GL_ONE, GL_ZERO);
859         GL_DepthMask(true);
860         GL_DepthTest(true);
861         qglDisable(GL_POLYGON_OFFSET_FILL);
862
863         qglColorMask(1, 1, 1, 1);
864         qglDisable(GL_SCISSOR_TEST);
865         qglDepthFunc(GL_LEQUAL);
866         qglDisable(GL_STENCIL_TEST);
867         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
868         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
869
870         r_shadowstage = SHADOWSTAGE_NONE;
871 }
872
873 #if 0
874 int R_Shadow_ScissorForBBoxAndSphere(const float *mins, const float *maxs, const float *origin, float radius)
875 {
876         int i, ix1, iy1, ix2, iy2;
877         float x1, y1, x2, y2, x, y;
878         vec3_t smins, smaxs;
879         vec4_t v, v2;
880         if (!r_shadow_scissor.integer)
881                 return false;
882         // if view is inside the box, just say yes it's visible
883         if (r_origin[0] >= mins[0] && r_origin[0] <= maxs[0]
884          && r_origin[1] >= mins[1] && r_origin[1] <= maxs[1]
885          && r_origin[2] >= mins[2] && r_origin[2] <= maxs[2])
886         {
887                 qglDisable(GL_SCISSOR_TEST);
888                 return false;
889         }
890         VectorSubtract(r_origin, origin, v);
891         if (DotProduct(v, v) < radius * radius)
892         {
893                 qglDisable(GL_SCISSOR_TEST);
894                 return false;
895         }
896         // create viewspace bbox
897         for (i = 0;i < 8;i++)
898         {
899                 v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
900                 v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
901                 v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
902                 v2[0] = DotProduct(v, vright);
903                 v2[1] = DotProduct(v, vup);
904                 v2[2] = DotProduct(v, vpn);
905                 if (i)
906                 {
907                         if (smins[0] > v2[0]) smins[0] = v2[0];
908                         if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
909                         if (smins[1] > v2[1]) smins[1] = v2[1];
910                         if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
911                         if (smins[2] > v2[2]) smins[2] = v2[2];
912                         if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
913                 }
914                 else
915                 {
916                         smins[0] = smaxs[0] = v2[0];
917                         smins[1] = smaxs[1] = v2[1];
918                         smins[2] = smaxs[2] = v2[2];
919                 }
920         }
921         // now we have a bbox in viewspace
922         // clip it to the viewspace version of the sphere
923         v[0] = origin[0] - r_origin[0];
924         v[1] = origin[1] - r_origin[1];
925         v[2] = origin[2] - r_origin[2];
926         v2[0] = DotProduct(v, vright);
927         v2[1] = DotProduct(v, vup);
928         v2[2] = DotProduct(v, vpn);
929         if (smins[0] < v2[0] - radius) smins[0] = v2[0] - radius;
930         if (smaxs[0] < v2[0] - radius) smaxs[0] = v2[0] + radius;
931         if (smins[1] < v2[1] - radius) smins[1] = v2[1] - radius;
932         if (smaxs[1] < v2[1] - radius) smaxs[1] = v2[1] + radius;
933         if (smins[2] < v2[2] - radius) smins[2] = v2[2] - radius;
934         if (smaxs[2] < v2[2] - radius) smaxs[2] = v2[2] + radius;
935         // clip it to the view plane
936         if (smins[2] < 1)
937                 smins[2] = 1;
938         // return true if that culled the box
939         if (smins[2] >= smaxs[2])
940                 return true;
941         // ok some of it is infront of the view, transform each corner back to
942         // worldspace and then to screenspace and make screen rect
943         // initialize these variables just to avoid compiler warnings
944         x1 = y1 = x2 = y2 = 0;
945         for (i = 0;i < 8;i++)
946         {
947                 v2[0] = (i & 1) ? smins[0] : smaxs[0];
948                 v2[1] = (i & 2) ? smins[1] : smaxs[1];
949                 v2[2] = (i & 4) ? smins[2] : smaxs[2];
950                 v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
951                 v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
952                 v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
953                 v[3] = 1.0f;
954                 GL_TransformToScreen(v, v2);
955                 //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]);
956                 x = v2[0];
957                 y = v2[1];
958                 if (i)
959                 {
960                         if (x1 > x) x1 = x;
961                         if (x2 < x) x2 = x;
962                         if (y1 > y) y1 = y;
963                         if (y2 < y) y2 = y;
964                 }
965                 else
966                 {
967                         x1 = x2 = x;
968                         y1 = y2 = y;
969                 }
970         }
971         /*
972         // this code doesn't handle boxes with any points behind view properly
973         x1 = 1000;x2 = -1000;
974         y1 = 1000;y2 = -1000;
975         for (i = 0;i < 8;i++)
976         {
977                 v[0] = (i & 1) ? mins[0] : maxs[0];
978                 v[1] = (i & 2) ? mins[1] : maxs[1];
979                 v[2] = (i & 4) ? mins[2] : maxs[2];
980                 v[3] = 1.0f;
981                 GL_TransformToScreen(v, v2);
982                 //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]);
983                 if (v2[2] > 0)
984                 {
985                         x = v2[0];
986                         y = v2[1];
987
988                         if (x1 > x) x1 = x;
989                         if (x2 < x) x2 = x;
990                         if (y1 > y) y1 = y;
991                         if (y2 < y) y2 = y;
992                 }
993         }
994         */
995         ix1 = x1 - 1.0f;
996         iy1 = y1 - 1.0f;
997         ix2 = x2 + 1.0f;
998         iy2 = y2 + 1.0f;
999         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1000         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1001         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1002         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1003         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1004         if (ix2 <= ix1 || iy2 <= iy1)
1005                 return true;
1006         // set up the scissor rectangle
1007         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1008         qglEnable(GL_SCISSOR_TEST);
1009         c_rt_scissored++;
1010         return false;
1011 }
1012 #endif
1013
1014 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
1015 {
1016         int i, ix1, iy1, ix2, iy2;
1017         float x1, y1, x2, y2, x, y, f;
1018         vec3_t smins, smaxs;
1019         vec4_t v, v2;
1020         if (!r_shadow_scissor.integer)
1021                 return false;
1022         // if view is inside the box, just say yes it's visible
1023         // LordHavoc: for some odd reason scissor seems broken without stencil
1024         // (?!?  seems like a driver bug) so abort if gl_stencil is false
1025         if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
1026         {
1027                 qglDisable(GL_SCISSOR_TEST);
1028                 return false;
1029         }
1030         for (i = 0;i < 3;i++)
1031         {
1032                 if (vpn[i] >= 0)
1033                 {
1034                         v[i] = mins[i];
1035                         v2[i] = maxs[i];
1036                 }
1037                 else
1038                 {
1039                         v[i] = maxs[i];
1040                         v2[i] = mins[i];
1041                 }
1042         }
1043         f = DotProduct(vpn, r_origin) + 1;
1044         if (DotProduct(vpn, v2) <= f)
1045         {
1046                 // entirely behind nearclip plane
1047                 return true;
1048         }
1049         if (DotProduct(vpn, v) >= f)
1050         {
1051                 // entirely infront of nearclip plane
1052                 x1 = y1 = x2 = y2 = 0;
1053                 for (i = 0;i < 8;i++)
1054                 {
1055                         v[0] = (i & 1) ? mins[0] : maxs[0];
1056                         v[1] = (i & 2) ? mins[1] : maxs[1];
1057                         v[2] = (i & 4) ? mins[2] : maxs[2];
1058                         v[3] = 1.0f;
1059                         GL_TransformToScreen(v, v2);
1060                         //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]);
1061                         x = v2[0];
1062                         y = v2[1];
1063                         if (i)
1064                         {
1065                                 if (x1 > x) x1 = x;
1066                                 if (x2 < x) x2 = x;
1067                                 if (y1 > y) y1 = y;
1068                                 if (y2 < y) y2 = y;
1069                         }
1070                         else
1071                         {
1072                                 x1 = x2 = x;
1073                                 y1 = y2 = y;
1074                         }
1075                 }
1076         }
1077         else
1078         {
1079                 // clipped by nearclip plane
1080                 // this is nasty and crude...
1081                 // create viewspace bbox
1082                 for (i = 0;i < 8;i++)
1083                 {
1084                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
1085                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
1086                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
1087                         v2[0] = DotProduct(v, vright);
1088                         v2[1] = DotProduct(v, vup);
1089                         v2[2] = DotProduct(v, vpn);
1090                         if (i)
1091                         {
1092                                 if (smins[0] > v2[0]) smins[0] = v2[0];
1093                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
1094                                 if (smins[1] > v2[1]) smins[1] = v2[1];
1095                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
1096                                 if (smins[2] > v2[2]) smins[2] = v2[2];
1097                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
1098                         }
1099                         else
1100                         {
1101                                 smins[0] = smaxs[0] = v2[0];
1102                                 smins[1] = smaxs[1] = v2[1];
1103                                 smins[2] = smaxs[2] = v2[2];
1104                         }
1105                 }
1106                 // now we have a bbox in viewspace
1107                 // clip it to the view plane
1108                 if (smins[2] < 1)
1109                         smins[2] = 1;
1110                 // return true if that culled the box
1111                 if (smins[2] >= smaxs[2])
1112                         return true;
1113                 // ok some of it is infront of the view, transform each corner back to
1114                 // worldspace and then to screenspace and make screen rect
1115                 // initialize these variables just to avoid compiler warnings
1116                 x1 = y1 = x2 = y2 = 0;
1117                 for (i = 0;i < 8;i++)
1118                 {
1119                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1120                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1121                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1122                         v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1123                         v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1124                         v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1125                         v[3] = 1.0f;
1126                         GL_TransformToScreen(v, v2);
1127                         //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]);
1128                         x = v2[0];
1129                         y = v2[1];
1130                         if (i)
1131                         {
1132                                 if (x1 > x) x1 = x;
1133                                 if (x2 < x) x2 = x;
1134                                 if (y1 > y) y1 = y;
1135                                 if (y2 < y) y2 = y;
1136                         }
1137                         else
1138                         {
1139                                 x1 = x2 = x;
1140                                 y1 = y2 = y;
1141                         }
1142                 }
1143                 /*
1144                 // this code doesn't handle boxes with any points behind view properly
1145                 x1 = 1000;x2 = -1000;
1146                 y1 = 1000;y2 = -1000;
1147                 for (i = 0;i < 8;i++)
1148                 {
1149                         v[0] = (i & 1) ? mins[0] : maxs[0];
1150                         v[1] = (i & 2) ? mins[1] : maxs[1];
1151                         v[2] = (i & 4) ? mins[2] : maxs[2];
1152                         v[3] = 1.0f;
1153                         GL_TransformToScreen(v, v2);
1154                         //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]);
1155                         if (v2[2] > 0)
1156                         {
1157                                 x = v2[0];
1158                                 y = v2[1];
1159
1160                                 if (x1 > x) x1 = x;
1161                                 if (x2 < x) x2 = x;
1162                                 if (y1 > y) y1 = y;
1163                                 if (y2 < y) y2 = y;
1164                         }
1165                 }
1166                 */
1167         }
1168         ix1 = x1 - 1.0f;
1169         iy1 = y1 - 1.0f;
1170         ix2 = x2 + 1.0f;
1171         iy2 = y2 + 1.0f;
1172         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1173         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1174         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1175         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1176         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1177         if (ix2 <= ix1 || iy2 <= iy1)
1178                 return true;
1179         // set up the scissor rectangle
1180         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1181         qglEnable(GL_SCISSOR_TEST);
1182         c_rt_scissored++;
1183         return false;
1184 }
1185
1186 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1187 {
1188         float *color4f = varray_color4f;
1189         float dist, dot, intensity, v[3], n[3];
1190         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1191         {
1192                 Matrix4x4_Transform(m, vertex3f, v);
1193                 if ((dist = DotProduct(v, v)) < 1)
1194                 {
1195                         Matrix4x4_Transform3x3(m, normal3f, n);
1196                         if ((dot = DotProduct(n, v)) > 0)
1197                         {
1198                                 dist = sqrt(dist);
1199                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1200                                 VectorScale(lightcolor, intensity, color4f);
1201                                 color4f[3] = 1;
1202                         }
1203                         else
1204                         {
1205                                 VectorClear(color4f);
1206                                 color4f[3] = 1;
1207                         }
1208                 }
1209                 else
1210                 {
1211                         VectorClear(color4f);
1212                         color4f[3] = 1;
1213                 }
1214         }
1215 }
1216
1217 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1218 {
1219         float *color4f = varray_color4f;
1220         float dist, dot, intensity, v[3], n[3];
1221         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1222         {
1223                 Matrix4x4_Transform(m, vertex3f, v);
1224                 if ((dist = fabs(v[2])) < 1)
1225                 {
1226                         Matrix4x4_Transform3x3(m, normal3f, n);
1227                         if ((dot = DotProduct(n, v)) > 0)
1228                         {
1229                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1230                                 VectorScale(lightcolor, intensity, color4f);
1231                                 color4f[3] = 1;
1232                         }
1233                         else
1234                         {
1235                                 VectorClear(color4f);
1236                                 color4f[3] = 1;
1237                         }
1238                 }
1239                 else
1240                 {
1241                         VectorClear(color4f);
1242                         color4f[3] = 1;
1243                 }
1244         }
1245 }
1246
1247 // FIXME: this should be done in a vertex program when possible
1248 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1249 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1250 {
1251         do
1252         {
1253                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1254                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1255                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1256                 vertex3f += 3;
1257                 tc3f += 3;
1258         }
1259         while (--numverts);
1260 }
1261
1262 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1263 {
1264         do
1265         {
1266                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1267                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1268                 vertex3f += 3;
1269                 tc2f += 2;
1270         }
1271         while (--numverts);
1272 }
1273
1274 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)
1275 {
1276         int i;
1277         float lightdir[3];
1278         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1279         {
1280                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1281                 // the cubemap normalizes this for us
1282                 out3f[0] = DotProduct(svector3f, lightdir);
1283                 out3f[1] = DotProduct(tvector3f, lightdir);
1284                 out3f[2] = DotProduct(normal3f, lightdir);
1285         }
1286 }
1287
1288 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)
1289 {
1290         int i;
1291         float lightdir[3], eyedir[3], halfdir[3];
1292         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1293         {
1294                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1295                 VectorNormalizeFast(lightdir);
1296                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1297                 VectorNormalizeFast(eyedir);
1298                 VectorAdd(lightdir, eyedir, halfdir);
1299                 // the cubemap normalizes this for us
1300                 out3f[0] = DotProduct(svector3f, halfdir);
1301                 out3f[1] = DotProduct(tvector3f, halfdir);
1302                 out3f[2] = DotProduct(normal3f, halfdir);
1303         }
1304 }
1305
1306 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)
1307 {
1308         int renders;
1309         float color[3], color2[3];
1310         rmeshstate_t m;
1311         GL_VertexPointer(vertex3f);
1312         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1313         {
1314                 if (!bumptexture)
1315                         bumptexture = r_shadow_blankbumptexture;
1316                 GL_Color(1,1,1,1);
1317                 // colorscale accounts for how much we multiply the brightness during combine
1318                 // mult is how many times the final pass of the lighting will be
1319                 // performed to get more brightness than otherwise possible
1320                 // limit mult to 64 for sanity sake
1321                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1322                 {
1323                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1324                         memset(&m, 0, sizeof(m));
1325                         m.tex[0] = R_GetTexture(bumptexture);
1326                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1327                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1328                         m.texcombinergb[0] = GL_REPLACE;
1329                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1330                         m.pointer_texcoord[0] = texcoord2f;
1331                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1332                         m.pointer_texcoord[2] = varray_texcoord3f[2];
1333                         R_Mesh_State_Texture(&m);
1334                         qglColorMask(0,0,0,1);
1335                         GL_BlendFunc(GL_ONE, GL_ZERO);
1336                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1337                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1338                         R_Mesh_Draw(numverts, numtriangles, elements);
1339                         c_rt_lightmeshes++;
1340                         c_rt_lighttris += numtriangles;
1341
1342                         memset(&m, 0, sizeof(m));
1343                         m.tex[0] = R_GetTexture(basetexture);
1344                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1345                         m.pointer_texcoord[0] = texcoord2f;
1346                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1347                         R_Mesh_State_Texture(&m);
1348                         qglColorMask(1,1,1,0);
1349                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1350                         if (lightcubemap)
1351                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1352                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1353                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1354                         {
1355                                 color[0] = bound(0, color2[0], 1);
1356                                 color[1] = bound(0, color2[1], 1);
1357                                 color[2] = bound(0, color2[2], 1);
1358                                 GL_Color(color[0], color[1], color[2], 1);
1359                                 R_Mesh_Draw(numverts, numtriangles, elements);
1360                                 c_rt_lightmeshes++;
1361                                 c_rt_lighttris += numtriangles;
1362                         }
1363                 }
1364                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1365                 {
1366                         // 1/2/2 3D combine path (original Radeon)
1367                         memset(&m, 0, sizeof(m));
1368                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1369                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1370                         R_Mesh_State_Texture(&m);
1371                         qglColorMask(0,0,0,1);
1372                         GL_BlendFunc(GL_ONE, GL_ZERO);
1373                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1374                         R_Mesh_Draw(numverts, numtriangles, elements);
1375                         c_rt_lightmeshes++;
1376                         c_rt_lighttris += numtriangles;
1377
1378                         memset(&m, 0, sizeof(m));
1379                         m.tex[0] = R_GetTexture(bumptexture);
1380                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1381                         m.texcombinergb[0] = GL_REPLACE;
1382                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1383                         m.pointer_texcoord[0] = texcoord2f;
1384                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1385                         R_Mesh_State_Texture(&m);
1386                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1387                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1388                         R_Mesh_Draw(numverts, numtriangles, elements);
1389                         c_rt_lightmeshes++;
1390                         c_rt_lighttris += numtriangles;
1391
1392                         memset(&m, 0, sizeof(m));
1393                         m.tex[0] = R_GetTexture(basetexture);
1394                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1395                         m.pointer_texcoord[0] = texcoord2f;
1396                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1397                         R_Mesh_State_Texture(&m);
1398                         qglColorMask(1,1,1,0);
1399                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1400                         if (lightcubemap)
1401                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1402                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1403                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1404                         {
1405                                 color[0] = bound(0, color2[0], 1);
1406                                 color[1] = bound(0, color2[1], 1);
1407                                 color[2] = bound(0, color2[2], 1);
1408                                 GL_Color(color[0], color[1], color[2], 1);
1409                                 R_Mesh_Draw(numverts, numtriangles, elements);
1410                                 c_rt_lightmeshes++;
1411                                 c_rt_lighttris += numtriangles;
1412                         }
1413                 }
1414                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1415                 {
1416                         // 2/2 3D combine path (original Radeon)
1417                         memset(&m, 0, sizeof(m));
1418                         m.tex[0] = R_GetTexture(bumptexture);
1419                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1420                         m.texcombinergb[0] = GL_REPLACE;
1421                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1422                         m.pointer_texcoord[0] = texcoord2f;
1423                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1424                         R_Mesh_State_Texture(&m);
1425                         qglColorMask(0,0,0,1);
1426                         GL_BlendFunc(GL_ONE, GL_ZERO);
1427                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1428                         R_Mesh_Draw(numverts, numtriangles, elements);
1429                         c_rt_lightmeshes++;
1430                         c_rt_lighttris += numtriangles;
1431
1432                         memset(&m, 0, sizeof(m));
1433                         m.tex[0] = R_GetTexture(basetexture);
1434                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1435                         m.pointer_texcoord[0] = texcoord2f;
1436                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1437                         R_Mesh_State_Texture(&m);
1438                         qglColorMask(1,1,1,0);
1439                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1440                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1441                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1442                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1443                         {
1444                                 color[0] = bound(0, color2[0], 1);
1445                                 color[1] = bound(0, color2[1], 1);
1446                                 color[2] = bound(0, color2[2], 1);
1447                                 GL_Color(color[0], color[1], color[2], 1);
1448                                 R_Mesh_Draw(numverts, numtriangles, elements);
1449                                 c_rt_lightmeshes++;
1450                                 c_rt_lighttris += numtriangles;
1451                         }
1452                 }
1453                 else if (r_textureunits.integer >= 4)
1454                 {
1455                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1456                         memset(&m, 0, sizeof(m));
1457                         m.tex[0] = R_GetTexture(bumptexture);
1458                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1459                         m.texcombinergb[0] = GL_REPLACE;
1460                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1461                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1462                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1463                         m.pointer_texcoord[0] = texcoord2f;
1464                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1465                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1466                         m.pointer_texcoord[3] = varray_texcoord2f[3];
1467                         R_Mesh_State_Texture(&m);
1468                         qglColorMask(0,0,0,1);
1469                         GL_BlendFunc(GL_ONE, GL_ZERO);
1470                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1471                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1472                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1473                         R_Mesh_Draw(numverts, numtriangles, elements);
1474                         c_rt_lightmeshes++;
1475                         c_rt_lighttris += numtriangles;
1476
1477                         memset(&m, 0, sizeof(m));
1478                         m.tex[0] = R_GetTexture(basetexture);
1479                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1480                         m.pointer_texcoord[0] = texcoord2f;
1481                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1482                         R_Mesh_State_Texture(&m);
1483                         qglColorMask(1,1,1,0);
1484                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1485                         if (lightcubemap)
1486                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1487                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1488                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1489                         {
1490                                 color[0] = bound(0, color2[0], 1);
1491                                 color[1] = bound(0, color2[1], 1);
1492                                 color[2] = bound(0, color2[2], 1);
1493                                 GL_Color(color[0], color[1], color[2], 1);
1494                                 R_Mesh_Draw(numverts, numtriangles, elements);
1495                                 c_rt_lightmeshes++;
1496                                 c_rt_lighttris += numtriangles;
1497                         }
1498                 }
1499                 else
1500                 {
1501                         // 2/2/2 2D combine path (any dot3 card)
1502                         memset(&m, 0, sizeof(m));
1503                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1504                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1505                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1506                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1507                         R_Mesh_State_Texture(&m);
1508                         qglColorMask(0,0,0,1);
1509                         GL_BlendFunc(GL_ONE, GL_ZERO);
1510                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1511                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1512                         R_Mesh_Draw(numverts, numtriangles, elements);
1513                         c_rt_lightmeshes++;
1514                         c_rt_lighttris += numtriangles;
1515
1516                         memset(&m, 0, sizeof(m));
1517                         m.tex[0] = R_GetTexture(bumptexture);
1518                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1519                         m.texcombinergb[0] = GL_REPLACE;
1520                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1521                         m.pointer_texcoord[0] = texcoord2f;
1522                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1523                         R_Mesh_State_Texture(&m);
1524                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1525                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1526                         R_Mesh_Draw(numverts, numtriangles, elements);
1527                         c_rt_lightmeshes++;
1528                         c_rt_lighttris += numtriangles;
1529
1530                         memset(&m, 0, sizeof(m));
1531                         m.tex[0] = R_GetTexture(basetexture);
1532                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1533                         m.pointer_texcoord[0] = texcoord2f;
1534                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1535                         R_Mesh_State_Texture(&m);
1536                         qglColorMask(1,1,1,0);
1537                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1538                         if (lightcubemap)
1539                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1540                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1541                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1542                         {
1543                                 color[0] = bound(0, color2[0], 1);
1544                                 color[1] = bound(0, color2[1], 1);
1545                                 color[2] = bound(0, color2[2], 1);
1546                                 GL_Color(color[0], color[1], color[2], 1);
1547                                 R_Mesh_Draw(numverts, numtriangles, elements);
1548                                 c_rt_lightmeshes++;
1549                                 c_rt_lighttris += numtriangles;
1550                         }
1551                 }
1552         }
1553         else
1554         {
1555                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1556                 GL_DepthMask(false);
1557                 GL_DepthTest(true);
1558                 GL_ColorPointer(varray_color4f);
1559                 VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value, color2);
1560                 memset(&m, 0, sizeof(m));
1561                 m.tex[0] = R_GetTexture(basetexture);
1562                 m.pointer_texcoord[0] = texcoord2f;
1563                 if (r_textureunits.integer >= 2)
1564                 {
1565                         // voodoo2
1566                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1567                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1568                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1569                 }
1570                 R_Mesh_State_Texture(&m);
1571                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1572                 {
1573                         color[0] = bound(0, color2[0], 1);
1574                         color[1] = bound(0, color2[1], 1);
1575                         color[2] = bound(0, color2[2], 1);
1576                         if (r_textureunits.integer >= 2)
1577                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1578                         else
1579                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1580                         R_Mesh_Draw(numverts, numtriangles, elements);
1581                         c_rt_lightmeshes++;
1582                         c_rt_lighttris += numtriangles;
1583                 }
1584         }
1585 }
1586
1587 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)
1588 {
1589         int renders;
1590         float color[3], color2[3];
1591         rmeshstate_t m;
1592         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1593                 return;
1594         if (!bumptexture)
1595                 bumptexture = r_shadow_blankbumptexture;
1596         if (!glosstexture)
1597                 glosstexture = r_shadow_blankglosstexture;
1598         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1599         {
1600                 GL_VertexPointer(vertex3f);
1601                 GL_Color(1,1,1,1);
1602                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1603                 {
1604                         // 2/0/0/1/2 3D combine blendsquare path
1605                         memset(&m, 0, sizeof(m));
1606                         m.tex[0] = R_GetTexture(bumptexture);
1607                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1608                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1609                         m.pointer_texcoord[0] = texcoord2f;
1610                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1611                         R_Mesh_State_Texture(&m);
1612                         qglColorMask(0,0,0,1);
1613                         // this squares the result
1614                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1615                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1616                         R_Mesh_Draw(numverts, numtriangles, elements);
1617                         c_rt_lightmeshes++;
1618                         c_rt_lighttris += numtriangles;
1619
1620                         memset(&m, 0, sizeof(m));
1621                         R_Mesh_State_Texture(&m);
1622                         // square alpha in framebuffer a few times to make it shiny
1623                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1624                         // these comments are a test run through this math for intensity 0.5
1625                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1626                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1627                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1628                         R_Mesh_Draw(numverts, numtriangles, elements);
1629                         c_rt_lightmeshes++;
1630                         c_rt_lighttris += numtriangles;
1631                         R_Mesh_Draw(numverts, numtriangles, elements);
1632                         c_rt_lightmeshes++;
1633                         c_rt_lighttris += numtriangles;
1634
1635                         memset(&m, 0, sizeof(m));
1636                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1637                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1638                         R_Mesh_State_Texture(&m);
1639                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1640                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1641                         R_Mesh_Draw(numverts, numtriangles, elements);
1642                         c_rt_lightmeshes++;
1643                         c_rt_lighttris += numtriangles;
1644
1645                         memset(&m, 0, sizeof(m));
1646                         m.tex[0] = R_GetTexture(glosstexture);
1647                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1648                         m.pointer_texcoord[0] = texcoord2f;
1649                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1650                         R_Mesh_State_Texture(&m);
1651                         qglColorMask(1,1,1,0);
1652                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1653                         if (lightcubemap)
1654                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1655                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1656                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1657                         {
1658                                 color[0] = bound(0, color2[0], 1);
1659                                 color[1] = bound(0, color2[1], 1);
1660                                 color[2] = bound(0, color2[2], 1);
1661                                 GL_Color(color[0], color[1], color[2], 1);
1662                                 R_Mesh_Draw(numverts, numtriangles, elements);
1663                                 c_rt_lightmeshes++;
1664                                 c_rt_lighttris += numtriangles;
1665                         }
1666                 }
1667                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1668                 {
1669                         // 2/0/0/2 3D combine blendsquare path
1670                         memset(&m, 0, sizeof(m));
1671                         m.tex[0] = R_GetTexture(bumptexture);
1672                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1673                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1674                         m.pointer_texcoord[0] = texcoord2f;
1675                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1676                         R_Mesh_State_Texture(&m);
1677                         qglColorMask(0,0,0,1);
1678                         // this squares the result
1679                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1680                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1681                         R_Mesh_Draw(numverts, numtriangles, elements);
1682                         c_rt_lightmeshes++;
1683                         c_rt_lighttris += numtriangles;
1684
1685                         memset(&m, 0, sizeof(m));
1686                         R_Mesh_State_Texture(&m);
1687                         // square alpha in framebuffer a few times to make it shiny
1688                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1689                         // these comments are a test run through this math for intensity 0.5
1690                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1691                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1692                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1693                         R_Mesh_Draw(numverts, numtriangles, elements);
1694                         c_rt_lightmeshes++;
1695                         c_rt_lighttris += numtriangles;
1696                         R_Mesh_Draw(numverts, numtriangles, elements);
1697                         c_rt_lightmeshes++;
1698                         c_rt_lighttris += numtriangles;
1699
1700                         memset(&m, 0, sizeof(m));
1701                         m.tex[0] = R_GetTexture(glosstexture);
1702                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1703                         m.pointer_texcoord[0] = texcoord2f;
1704                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1705                         R_Mesh_State_Texture(&m);
1706                         qglColorMask(1,1,1,0);
1707                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1708                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1709                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1710                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1711                         {
1712                                 color[0] = bound(0, color2[0], 1);
1713                                 color[1] = bound(0, color2[1], 1);
1714                                 color[2] = bound(0, color2[2], 1);
1715                                 GL_Color(color[0], color[1], color[2], 1);
1716                                 R_Mesh_Draw(numverts, numtriangles, elements);
1717                                 c_rt_lightmeshes++;
1718                                 c_rt_lighttris += numtriangles;
1719                         }
1720                 }
1721                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1722                 {
1723                         // 2/0/0/2/2 2D combine blendsquare path
1724                         memset(&m, 0, sizeof(m));
1725                         m.tex[0] = R_GetTexture(bumptexture);
1726                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1727                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1728                         m.pointer_texcoord[0] = texcoord2f;
1729                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1730                         R_Mesh_State_Texture(&m);
1731                         qglColorMask(0,0,0,1);
1732                         // this squares the result
1733                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1734                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1735                         R_Mesh_Draw(numverts, numtriangles, elements);
1736                         c_rt_lightmeshes++;
1737                         c_rt_lighttris += numtriangles;
1738
1739                         memset(&m, 0, sizeof(m));
1740                         R_Mesh_State_Texture(&m);
1741                         // square alpha in framebuffer a few times to make it shiny
1742                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1743                         // these comments are a test run through this math for intensity 0.5
1744                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1745                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1746                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1747                         R_Mesh_Draw(numverts, numtriangles, elements);
1748                         c_rt_lightmeshes++;
1749                         c_rt_lighttris += numtriangles;
1750                         R_Mesh_Draw(numverts, numtriangles, elements);
1751                         c_rt_lightmeshes++;
1752                         c_rt_lighttris += numtriangles;
1753
1754                         memset(&m, 0, sizeof(m));
1755                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1756                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1757                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1758                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1759                         R_Mesh_State_Texture(&m);
1760                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1761                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1762                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1763                         R_Mesh_Draw(numverts, numtriangles, elements);
1764                         c_rt_lightmeshes++;
1765                         c_rt_lighttris += numtriangles;
1766
1767                         memset(&m, 0, sizeof(m));
1768                         m.tex[0] = R_GetTexture(glosstexture);
1769                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1770                         m.pointer_texcoord[0] = texcoord2f;
1771                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1772                         R_Mesh_State_Texture(&m);
1773                         qglColorMask(1,1,1,0);
1774                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1775                         if (lightcubemap)
1776                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1777                         VectorScale(lightcolor, r_colorscale * r_shadow_lightintensityscale.value * 0.25f, color2);
1778                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1779                         {
1780                                 color[0] = bound(0, color2[0], 1);
1781                                 color[1] = bound(0, color2[1], 1);
1782                                 color[2] = bound(0, color2[2], 1);
1783                                 GL_Color(color[0], color[1], color[2], 1);
1784                                 R_Mesh_Draw(numverts, numtriangles, elements);
1785                                 c_rt_lightmeshes++;
1786                                 c_rt_lighttris += numtriangles;
1787                         }
1788                 }
1789         }
1790 }
1791
1792 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1793 {
1794         R_Mesh_Matrix(matrix);
1795         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1796 }
1797
1798 cvar_t r_editlights = {0, "r_editlights", "0"};
1799 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1800 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1801 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1802 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1803 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1804 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1805 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1806 worldlight_t *r_shadow_worldlightchain;
1807 worldlight_t *r_shadow_selectedlight;
1808 vec3_t r_editlights_cursorlocation;
1809
1810 static int castshadowcount = 1;
1811 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1812 {
1813         int i, j, k, l, maxverts = 256, *mark, tris;
1814         float *vertex3f = NULL;
1815         worldlight_t *e;
1816         shadowmesh_t *mesh, *castmesh;
1817         mleaf_t *leaf;
1818         msurface_t *surf;
1819         qbyte *pvs;
1820         surfmesh_t *surfmesh;
1821
1822         if (radius < 15 || DotProduct(color, color) < 0.03)
1823         {
1824                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1825                 return;
1826         }
1827
1828         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1829         VectorCopy(origin, e->origin);
1830         VectorCopy(color, e->light);
1831         e->lightradius = radius;
1832         e->style = style;
1833         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1834         {
1835                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1836                 e->style = 0;
1837         }
1838         e->castshadows = castshadow;
1839
1840         e->cullradius = e->lightradius;
1841         for (k = 0;k < 3;k++)
1842         {
1843                 e->mins[k] = e->origin[k] - e->lightradius;
1844                 e->maxs[k] = e->origin[k] + e->lightradius;
1845         }
1846
1847         e->next = r_shadow_worldlightchain;
1848         r_shadow_worldlightchain = e;
1849         if (cubemapname && cubemapname[0])
1850         {
1851                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1852                 strcpy(e->cubemapname, cubemapname);
1853                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1854         }
1855         if (cl.worldmodel)
1856         {
1857                 castshadowcount++;
1858                 i = Mod_PointContents(e->origin, cl.worldmodel);
1859                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1860                 {
1861                         qbyte *byteleafpvs;
1862                         qbyte *bytesurfacepvs;
1863
1864                         byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->numleafs + 1);
1865                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->numsurfaces);
1866
1867                         Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1868
1869                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1870                                 if (byteleafpvs[i+1] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1871                                         leaf->worldnodeframe = castshadowcount;
1872
1873                         for (i = 0, surf = cl.worldmodel->surfaces;i < cl.worldmodel->numsurfaces;i++, surf++)
1874                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1875                                         surf->castshadow = castshadowcount;
1876
1877                         Mem_Free(byteleafpvs);
1878                         Mem_Free(bytesurfacepvs);
1879                 }
1880                 else
1881                 {
1882                         leaf = Mod_PointInLeaf(origin, cl.worldmodel);
1883                         pvs = Mod_LeafPVS(leaf, cl.worldmodel);
1884                         for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1885                         {
1886                                 if (pvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1887                                 {
1888                                         leaf->worldnodeframe = castshadowcount;
1889                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1890                                         {
1891                                                 surf = cl.worldmodel->surfaces + *mark;
1892                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1893                                                         surf->castshadow = castshadowcount;
1894                                         }
1895                                 }
1896                         }
1897                 }
1898
1899                 e->numleafs = 0;
1900                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1901                         if (leaf->worldnodeframe == castshadowcount)
1902                                 e->numleafs++;
1903                 e->numsurfaces = 0;
1904                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1905                         if (surf->castshadow == castshadowcount)
1906                                 e->numsurfaces++;
1907
1908                 if (e->numleafs)
1909                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1910                 if (e->numsurfaces)
1911                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1912                 e->numleafs = 0;
1913                 for (i = 0, leaf = cl.worldmodel->leafs + 1;i < cl.worldmodel->numleafs;i++, leaf++)
1914                         if (leaf->worldnodeframe == castshadowcount)
1915                                 e->leafs[e->numleafs++] = leaf;
1916                 e->numsurfaces = 0;
1917                 for (i = 0, surf = cl.worldmodel->surfaces + cl.worldmodel->firstmodelsurface;i < cl.worldmodel->nummodelsurfaces;i++, surf++)
1918                         if (surf->castshadow == castshadowcount)
1919                                 e->surfaces[e->numsurfaces++] = surf;
1920
1921                 // find bounding box of lit leafs
1922                 VectorCopy(e->origin, e->mins);
1923                 VectorCopy(e->origin, e->maxs);
1924                 for (j = 0;j < e->numleafs;j++)
1925                 {
1926                         leaf = e->leafs[j];
1927                         for (k = 0;k < 3;k++)
1928                         {
1929                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1930                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1931                         }
1932                 }
1933
1934                 for (k = 0;k < 3;k++)
1935                 {
1936                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1937                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1938                 }
1939                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1940
1941                 if (e->castshadows)
1942                 {
1943                         castshadowcount++;
1944                         for (j = 0;j < e->numsurfaces;j++)
1945                         {
1946                                 surf = e->surfaces[j];
1947                                 if (surf->flags & SURF_SHADOWCAST)
1948                                 {
1949                                         surf->castshadow = castshadowcount;
1950                                         if (maxverts < surf->poly_numverts)
1951                                                 maxverts = surf->poly_numverts;
1952                                 }
1953                         }
1954                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1955                         // make a mesh to cast a shadow volume from
1956                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1957                         for (j = 0;j < e->numsurfaces;j++)
1958                                 if (e->surfaces[j]->castshadow == castshadowcount)
1959                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1960                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1961                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1962
1963                         // cast shadow volume from castmesh
1964                         for (mesh = castmesh;mesh;mesh = mesh->next)
1965                         {
1966                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1967
1968                                 if (maxverts < castmesh->numverts * 2)
1969                                 {
1970                                         maxverts = castmesh->numverts * 2;
1971                                         if (vertex3f)
1972                                                 Mem_Free(vertex3f);
1973                                         vertex3f = NULL;
1974                                 }
1975                                 if (vertex3f == NULL && maxverts > 0)
1976                                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1977
1978                                 // now that we have the buffers big enough, construct and add
1979                                 // the shadow volume mesh
1980                                 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)))
1981                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1982                         }
1983                         if (vertex3f)
1984                                 Mem_Free(vertex3f);
1985                         vertex3f = NULL;
1986                         // we're done with castmesh now
1987                         Mod_ShadowMesh_Free(castmesh);
1988                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1989                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1990                                 l += mesh->numtriangles;
1991                         Con_Printf("static shadow volume built containing %i triangles\n", l);
1992                 }
1993         }
1994         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);
1995 }
1996
1997 void R_Shadow_FreeWorldLight(worldlight_t *light)
1998 {
1999         worldlight_t **lightpointer;
2000         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2001         if (*lightpointer != light)
2002                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2003         *lightpointer = light->next;
2004         if (light->cubemapname)
2005                 Mem_Free(light->cubemapname);
2006         if (light->shadowvolume)
2007                 Mod_ShadowMesh_Free(light->shadowvolume);
2008         if (light->surfaces)
2009                 Mem_Free(light->surfaces);
2010         if (light->leafs)
2011                 Mem_Free(light->leafs);
2012         Mem_Free(light);
2013 }
2014
2015 void R_Shadow_ClearWorldLights(void)
2016 {
2017         while (r_shadow_worldlightchain)
2018                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2019         r_shadow_selectedlight = NULL;
2020 }
2021
2022 void R_Shadow_SelectLight(worldlight_t *light)
2023 {
2024         if (r_shadow_selectedlight)
2025                 r_shadow_selectedlight->selected = false;
2026         r_shadow_selectedlight = light;
2027         if (r_shadow_selectedlight)
2028                 r_shadow_selectedlight->selected = true;
2029 }
2030
2031 rtexture_t *lighttextures[5];
2032
2033 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2034 {
2035         float scale = r_editlights_cursorgrid.value * 0.5f;
2036         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2037 }
2038
2039 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2040 {
2041         float intensity;
2042         const worldlight_t *light;
2043         light = calldata1;
2044         intensity = 0.5;
2045         if (light->selected)
2046                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2047         if (!light->shadowvolume)
2048                 intensity *= 0.5f;
2049         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2050 }
2051
2052 void R_Shadow_DrawLightSprites(void)
2053 {
2054         int i;
2055         cachepic_t *pic;
2056         worldlight_t *light;
2057
2058         for (i = 0;i < 5;i++)
2059         {
2060                 lighttextures[i] = NULL;
2061                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2062                         lighttextures[i] = pic->tex;
2063         }
2064
2065         for (light = r_shadow_worldlightchain;light;light = light->next)
2066                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2067         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2068 }
2069
2070 void R_Shadow_SelectLightInView(void)
2071 {
2072         float bestrating, rating, temp[3];
2073         worldlight_t *best, *light;
2074         best = NULL;
2075         bestrating = 0;
2076         for (light = r_shadow_worldlightchain;light;light = light->next)
2077         {
2078                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2079                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2080                 if (rating >= 0.95)
2081                 {
2082                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2083                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2084                         {
2085                                 bestrating = rating;
2086                                 best = light;
2087                         }
2088                 }
2089         }
2090         R_Shadow_SelectLight(best);
2091 }
2092
2093 void R_Shadow_LoadWorldLights(void)
2094 {
2095         int n, a, style, shadow;
2096         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2097         float origin[3], radius, color[3];
2098         if (cl.worldmodel == NULL)
2099         {
2100                 Con_Printf("No map loaded.\n");
2101                 return;
2102         }
2103         FS_StripExtension(cl.worldmodel->name, name);
2104         strcat(name, ".rtlights");
2105         lightsstring = FS_LoadFile(name, false);
2106         if (lightsstring)
2107         {
2108                 s = lightsstring;
2109                 n = 0;
2110                 while (*s)
2111                 {
2112                         t = s;
2113                         while (*s && *s != '\n')
2114                                 s++;
2115                         if (!*s)
2116                                 break;
2117                         *s = 0;
2118                         shadow = true;
2119                         // check for modifier flags
2120                         if (*t == '!')
2121                         {
2122                                 shadow = false;
2123                                 t++;
2124                         }
2125                         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);
2126                         if (a < 9)
2127                                 cubemapname[0] = 0;
2128                         *s = '\n';
2129                         if (a < 8)
2130                         {
2131                                 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);
2132                                 break;
2133                         }
2134                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2135                         radius *= r_editlights_rtlightssizescale.value;
2136                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2137                         s++;
2138                         n++;
2139                 }
2140                 if (*s)
2141                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2142                 Mem_Free(lightsstring);
2143         }
2144 }
2145
2146 void R_Shadow_SaveWorldLights(void)
2147 {
2148         worldlight_t *light;
2149         int bufchars, bufmaxchars;
2150         char *buf, *oldbuf;
2151         char name[MAX_QPATH];
2152         char line[1024];
2153         if (!r_shadow_worldlightchain)
2154                 return;
2155         if (cl.worldmodel == NULL)
2156         {
2157                 Con_Printf("No map loaded.\n");
2158                 return;
2159         }
2160         FS_StripExtension(cl.worldmodel->name, name);
2161         strcat(name, ".rtlights");
2162         bufchars = bufmaxchars = 0;
2163         buf = NULL;
2164         for (light = r_shadow_worldlightchain;light;light = light->next)
2165         {
2166                 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 : "");
2167                 if (bufchars + (int) strlen(line) > bufmaxchars)
2168                 {
2169                         bufmaxchars = bufchars + strlen(line) + 2048;
2170                         oldbuf = buf;
2171                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2172                         if (oldbuf)
2173                         {
2174                                 if (bufchars)
2175                                         memcpy(buf, oldbuf, bufchars);
2176                                 Mem_Free(oldbuf);
2177                         }
2178                 }
2179                 if (strlen(line))
2180                 {
2181                         memcpy(buf + bufchars, line, strlen(line));
2182                         bufchars += strlen(line);
2183                 }
2184         }
2185         if (bufchars)
2186                 FS_WriteFile(name, buf, bufchars);
2187         if (buf)
2188                 Mem_Free(buf);
2189 }
2190
2191 void R_Shadow_LoadLightsFile(void)
2192 {
2193         int n, a, style;
2194         char name[MAX_QPATH], *lightsstring, *s, *t;
2195         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2196         if (cl.worldmodel == NULL)
2197         {
2198                 Con_Printf("No map loaded.\n");
2199                 return;
2200         }
2201         FS_StripExtension(cl.worldmodel->name, name);
2202         strcat(name, ".lights");
2203         lightsstring = FS_LoadFile(name, false);
2204         if (lightsstring)
2205         {
2206                 s = lightsstring;
2207                 n = 0;
2208                 while (*s)
2209                 {
2210                         t = s;
2211                         while (*s && *s != '\n')
2212                                 s++;
2213                         if (!*s)
2214                                 break;
2215                         *s = 0;
2216                         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);
2217                         *s = '\n';
2218                         if (a < 14)
2219                         {
2220                                 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);
2221                                 break;
2222                         }
2223                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2224                         radius = bound(15, radius, 4096);
2225                         VectorScale(color, (2.0f / (8388608.0f)), color);
2226                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2227                         s++;
2228                         n++;
2229                 }
2230                 if (*s)
2231                         Con_Printf("invalid lights file \"%s\"\n", name);
2232                 Mem_Free(lightsstring);
2233         }
2234 }
2235
2236 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2237 {
2238         int entnum, style, islight;
2239         char key[256], value[1024];
2240         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2241         const char *data;
2242
2243         if (cl.worldmodel == NULL)
2244         {
2245                 Con_Printf("No map loaded.\n");
2246                 return;
2247         }
2248         data = cl.worldmodel->entities;
2249         if (!data)
2250                 return;
2251         for (entnum = 0;COM_ParseToken(&data) && com_token[0] == '{';entnum++)
2252         {
2253                 light = 0;
2254                 origin[0] = origin[1] = origin[2] = 0;
2255                 originhack[0] = originhack[1] = originhack[2] = 0;
2256                 color[0] = color[1] = color[2] = 1;
2257                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2258                 scale = 1;
2259                 style = 0;
2260                 islight = false;
2261                 while (1)
2262                 {
2263                         if (!COM_ParseToken(&data))
2264                                 break; // error
2265                         if (com_token[0] == '}')
2266                                 break; // end of entity
2267                         if (com_token[0] == '_')
2268                                 strcpy(key, com_token + 1);
2269                         else
2270                                 strcpy(key, com_token);
2271                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2272                                 key[strlen(key)-1] = 0;
2273                         if (!COM_ParseToken(&data))
2274                                 break; // error
2275                         strcpy(value, com_token);
2276
2277                         // now that we have the key pair worked out...
2278                         if (!strcmp("light", key))
2279                                 light = atof(value);
2280                         else if (!strcmp("origin", key))
2281                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2282                         else if (!strcmp("color", key))
2283                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2284                         else if (!strcmp("wait", key))
2285                                 scale = atof(value);
2286                         else if (!strcmp("classname", key))
2287                         {
2288                                 if (!strncmp(value, "light", 5))
2289                                 {
2290                                         islight = true;
2291                                         if (!strcmp(value, "light_fluoro"))
2292                                         {
2293                                                 originhack[0] = 0;
2294                                                 originhack[1] = 0;
2295                                                 originhack[2] = 0;
2296                                                 overridecolor[0] = 1;
2297                                                 overridecolor[1] = 1;
2298                                                 overridecolor[2] = 1;
2299                                         }
2300                                         if (!strcmp(value, "light_fluorospark"))
2301                                         {
2302                                                 originhack[0] = 0;
2303                                                 originhack[1] = 0;
2304                                                 originhack[2] = 0;
2305                                                 overridecolor[0] = 1;
2306                                                 overridecolor[1] = 1;
2307                                                 overridecolor[2] = 1;
2308                                         }
2309                                         if (!strcmp(value, "light_globe"))
2310                                         {
2311                                                 originhack[0] = 0;
2312                                                 originhack[1] = 0;
2313                                                 originhack[2] = 0;
2314                                                 overridecolor[0] = 1;
2315                                                 overridecolor[1] = 0.8;
2316                                                 overridecolor[2] = 0.4;
2317                                         }
2318                                         if (!strcmp(value, "light_flame_large_yellow"))
2319                                         {
2320                                                 originhack[0] = 0;
2321                                                 originhack[1] = 0;
2322                                                 originhack[2] = 48;
2323                                                 overridecolor[0] = 1;
2324                                                 overridecolor[1] = 0.5;
2325                                                 overridecolor[2] = 0.1;
2326                                         }
2327                                         if (!strcmp(value, "light_flame_small_yellow"))
2328                                         {
2329                                                 originhack[0] = 0;
2330                                                 originhack[1] = 0;
2331                                                 originhack[2] = 40;
2332                                                 overridecolor[0] = 1;
2333                                                 overridecolor[1] = 0.5;
2334                                                 overridecolor[2] = 0.1;
2335                                         }
2336                                         if (!strcmp(value, "light_torch_small_white"))
2337                                         {
2338                                                 originhack[0] = 0;
2339                                                 originhack[1] = 0;
2340                                                 originhack[2] = 40;
2341                                                 overridecolor[0] = 1;
2342                                                 overridecolor[1] = 0.5;
2343                                                 overridecolor[2] = 0.1;
2344                                         }
2345                                         if (!strcmp(value, "light_torch_small_walltorch"))
2346                                         {
2347                                                 originhack[0] = 0;
2348                                                 originhack[1] = 0;
2349                                                 originhack[2] = 40;
2350                                                 overridecolor[0] = 1;
2351                                                 overridecolor[1] = 0.5;
2352                                                 overridecolor[2] = 0.1;
2353                                         }
2354                                 }
2355                         }
2356                         else if (!strcmp("style", key))
2357                                 style = atoi(value);
2358                 }
2359                 if (light <= 0 && islight)
2360                         light = 300;
2361                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2362                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2363                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2364                         VectorCopy(overridecolor, color);
2365                 VectorScale(color, light, color);
2366                 VectorAdd(origin, originhack, origin);
2367                 if (radius >= 15)
2368                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2369         }
2370 }
2371
2372
2373 void R_Shadow_SetCursorLocationForView(void)
2374 {
2375         vec_t dist, push, frac;
2376         vec3_t dest, endpos, normal;
2377         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2378         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2379         if (frac < 1)
2380         {
2381                 dist = frac * r_editlights_cursordistance.value;
2382                 push = r_editlights_cursorpushback.value;
2383                 if (push > dist)
2384                         push = dist;
2385                 push = -push;
2386                 VectorMA(endpos, push, vpn, endpos);
2387                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2388         }
2389         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2390         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2391         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2392 }
2393
2394 void R_Shadow_UpdateWorldLightSelection(void)
2395 {
2396         R_Shadow_SetCursorLocationForView();
2397         if (r_editlights.integer)
2398         {
2399                 R_Shadow_SelectLightInView();
2400                 R_Shadow_DrawLightSprites();
2401         }
2402         else
2403                 R_Shadow_SelectLight(NULL);
2404 }
2405
2406 void R_Shadow_EditLights_Clear_f(void)
2407 {
2408         R_Shadow_ClearWorldLights();
2409 }
2410
2411 void R_Shadow_EditLights_Reload_f(void)
2412 {
2413         r_shadow_reloadlights = true;
2414 }
2415
2416 void R_Shadow_EditLights_Save_f(void)
2417 {
2418         if (cl.worldmodel)
2419                 R_Shadow_SaveWorldLights();
2420 }
2421
2422 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2423 {
2424         R_Shadow_ClearWorldLights();
2425         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2426 }
2427
2428 void R_Shadow_EditLights_ImportLightsFile_f(void)
2429 {
2430         R_Shadow_ClearWorldLights();
2431         R_Shadow_LoadLightsFile();
2432 }
2433
2434 void R_Shadow_EditLights_Spawn_f(void)
2435 {
2436         vec3_t color;
2437         if (!r_editlights.integer)
2438         {
2439                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2440                 return;
2441         }
2442         if (Cmd_Argc() != 1)
2443         {
2444                 Con_Printf("r_editlights_spawn does not take parameters\n");
2445                 return;
2446         }
2447         color[0] = color[1] = color[2] = 1;
2448         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2449 }
2450
2451 void R_Shadow_EditLights_Edit_f(void)
2452 {
2453         vec3_t origin, color;
2454         vec_t radius;
2455         int style, shadows;
2456         char cubemapname[1024];
2457         if (!r_editlights.integer)
2458         {
2459                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2460                 return;
2461         }
2462         if (!r_shadow_selectedlight)
2463         {
2464                 Con_Printf("No selected light.\n");
2465                 return;
2466         }
2467         VectorCopy(r_shadow_selectedlight->origin, origin);
2468         radius = r_shadow_selectedlight->lightradius;
2469         VectorCopy(r_shadow_selectedlight->light, color);
2470         style = r_shadow_selectedlight->style;
2471         if (r_shadow_selectedlight->cubemapname)
2472                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2473         else
2474                 cubemapname[0] = 0;
2475         shadows = r_shadow_selectedlight->castshadows;
2476         if (!strcmp(Cmd_Argv(1), "origin"))
2477         {
2478                 if (Cmd_Argc() != 5)
2479                 {
2480                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2481                         return;
2482                 }
2483                 origin[0] = atof(Cmd_Argv(2));
2484                 origin[1] = atof(Cmd_Argv(3));
2485                 origin[2] = atof(Cmd_Argv(4));
2486         }
2487         else if (!strcmp(Cmd_Argv(1), "originx"))
2488         {
2489                 if (Cmd_Argc() != 3)
2490                 {
2491                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2492                         return;
2493                 }
2494                 origin[0] = atof(Cmd_Argv(2));
2495         }
2496         else if (!strcmp(Cmd_Argv(1), "originy"))
2497         {
2498                 if (Cmd_Argc() != 3)
2499                 {
2500                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2501                         return;
2502                 }
2503                 origin[1] = atof(Cmd_Argv(2));
2504         }
2505         else if (!strcmp(Cmd_Argv(1), "originz"))
2506         {
2507                 if (Cmd_Argc() != 3)
2508                 {
2509                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2510                         return;
2511                 }
2512                 origin[2] = atof(Cmd_Argv(2));
2513         }
2514         else if (!strcmp(Cmd_Argv(1), "move"))
2515         {
2516                 if (Cmd_Argc() != 5)
2517                 {
2518                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2519                         return;
2520                 }
2521                 origin[0] += atof(Cmd_Argv(2));
2522                 origin[1] += atof(Cmd_Argv(3));
2523                 origin[2] += atof(Cmd_Argv(4));
2524         }
2525         else if (!strcmp(Cmd_Argv(1), "movex"))
2526         {
2527                 if (Cmd_Argc() != 3)
2528                 {
2529                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2530                         return;
2531                 }
2532                 origin[0] += atof(Cmd_Argv(2));
2533         }
2534         else if (!strcmp(Cmd_Argv(1), "movey"))
2535         {
2536                 if (Cmd_Argc() != 3)
2537                 {
2538                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2539                         return;
2540                 }
2541                 origin[1] += atof(Cmd_Argv(2));
2542         }
2543         else if (!strcmp(Cmd_Argv(1), "movez"))
2544         {
2545                 if (Cmd_Argc() != 3)
2546                 {
2547                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2548                         return;
2549                 }
2550                 origin[2] += atof(Cmd_Argv(2));
2551         }
2552         else if (!strcmp(Cmd_Argv(1), "color"))
2553         {
2554                 if (Cmd_Argc() != 5)
2555                 {
2556                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2557                         return;
2558                 }
2559                 color[0] = atof(Cmd_Argv(2));
2560                 color[1] = atof(Cmd_Argv(3));
2561                 color[2] = atof(Cmd_Argv(4));
2562         }
2563         else if (!strcmp(Cmd_Argv(1), "radius"))
2564         {
2565                 if (Cmd_Argc() != 3)
2566                 {
2567                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2568                         return;
2569                 }
2570                 radius = atof(Cmd_Argv(2));
2571         }
2572         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2573         {
2574                 if (Cmd_Argc() != 3)
2575                 {
2576                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2577                         return;
2578                 }
2579                 style = atoi(Cmd_Argv(2));
2580         }
2581         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2582         {
2583                 if (Cmd_Argc() > 3)
2584                 {
2585                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2586                         return;
2587                 }
2588                 if (Cmd_Argc() == 3)
2589                         strcpy(cubemapname, Cmd_Argv(2));
2590                 else
2591                         cubemapname[0] = 0;
2592         }
2593         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2594         {
2595                 if (Cmd_Argc() != 3)
2596                 {
2597                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2598                         return;
2599                 }
2600                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2601         }
2602         else
2603         {
2604                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2605                 Con_Printf("Selected light's properties:\n");
2606                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2607                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2608                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2609                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2610                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2611                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2612                 return;
2613         }
2614         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2615         r_shadow_selectedlight = NULL;
2616         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2617 }
2618
2619 extern int con_vislines;
2620 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2621 {
2622         float x, y;
2623         char temp[256];
2624         if (r_shadow_selectedlight == NULL)
2625                 return;
2626         x = 0;
2627         y = con_vislines;
2628         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2629         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;
2630         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2631         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;
2632         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2633         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2634         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;
2635 }
2636
2637 void R_Shadow_EditLights_ToggleShadow_f(void)
2638 {
2639         if (!r_editlights.integer)
2640         {
2641                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2642                 return;
2643         }
2644         if (!r_shadow_selectedlight)
2645         {
2646                 Con_Printf("No selected light.\n");
2647                 return;
2648         }
2649         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);
2650         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2651         r_shadow_selectedlight = NULL;
2652 }
2653
2654 void R_Shadow_EditLights_Remove_f(void)
2655 {
2656         if (!r_editlights.integer)
2657         {
2658                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2659                 return;
2660         }
2661         if (!r_shadow_selectedlight)
2662         {
2663                 Con_Printf("No selected light.\n");
2664                 return;
2665         }
2666         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2667         r_shadow_selectedlight = NULL;
2668 }
2669
2670 void R_Shadow_EditLights_Init(void)
2671 {
2672         Cvar_RegisterVariable(&r_editlights);
2673         Cvar_RegisterVariable(&r_editlights_cursordistance);
2674         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2675         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2676         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2677         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2678         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2679         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2680         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2681         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2682         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2683         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2684         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2685         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2686         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2687         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2688         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2689 }