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