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