]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
removed RSurf_LightCheck because nothing used it anymore
[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_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_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_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_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_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_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_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, NULL/*byteleafpvs*/, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1764
1765                         /*
1766                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1767                         {
1768                                 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1769                                 {
1770                                         for (k = 0;k < 3;k++)
1771                                         {
1772                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1773                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1774                                         }
1775                                 }
1776                         }
1777                         */
1778
1779                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1780                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1781                                         surf->castshadow = castshadowcount;
1782
1783                         //Mem_Free(byteleafpvs);
1784                         Mem_Free(bytesurfacepvs);
1785                 }
1786                 else
1787                 {
1788                         lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1789                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1790                         {
1791                                 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1792                                 {
1793                                         for (k = 0;k < 3;k++)
1794                                         {
1795                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1796                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1797                                         }
1798                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1799                                         {
1800                                                 surf = cl.worldmodel->brushq1.surfaces + *mark;
1801                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1802                                                         surf->castshadow = castshadowcount;
1803                                         }
1804                                 }
1805                         }
1806                 }
1807
1808                 for (k = 0;k < 3;k++)
1809                 {
1810                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1811                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1812                 }
1813                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1814
1815                 numsurfaces = 0;
1816                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1817                         if (surf->castshadow == castshadowcount)
1818                                 numsurfaces++;
1819                 if (numsurfaces)
1820                         e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
1821                 e->numsurfaces = 0;
1822                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1823                         if (surf->castshadow == castshadowcount)
1824                                 e->surfaces[e->numsurfaces++] = surf;
1825
1826                 if (e->castshadows)
1827                 {
1828                         castshadowcount++;
1829                         for (j = 0;j < e->numsurfaces;j++)
1830                         {
1831                                 surf = e->surfaces[j];
1832                                 if (surf->flags & SURF_SHADOWCAST)
1833                                 {
1834                                         surf->castshadow = castshadowcount;
1835                                         if (maxverts < surf->poly_numverts)
1836                                                 maxverts = surf->poly_numverts;
1837                                 }
1838                         }
1839                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1840                         // make a mesh to cast a shadow volume from
1841                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1842                         for (j = 0;j < e->numsurfaces;j++)
1843                                 if (e->surfaces[j]->castshadow == castshadowcount)
1844                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1845                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1846                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1847
1848                         // cast shadow volume from castmesh
1849                         for (mesh = castmesh;mesh;mesh = mesh->next)
1850                         {
1851                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
1852
1853                                 if (maxverts < castmesh->numverts * 2)
1854                                 {
1855                                         maxverts = castmesh->numverts * 2;
1856                                         if (vertex3f)
1857                                                 Mem_Free(vertex3f);
1858                                         vertex3f = NULL;
1859                                 }
1860                                 if (vertex3f == NULL && maxverts > 0)
1861                                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
1862
1863                                 // now that we have the buffers big enough, construct and add
1864                                 // the shadow volume mesh
1865                                 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)))
1866                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
1867                         }
1868                         if (vertex3f)
1869                                 Mem_Free(vertex3f);
1870                         vertex3f = NULL;
1871                         // we're done with castmesh now
1872                         Mod_ShadowMesh_Free(castmesh);
1873                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
1874                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
1875                                 l += mesh->numtriangles;
1876                         Con_Printf("static shadow volume built containing %i triangles\n", l);
1877                 }
1878         }
1879         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);
1880 }
1881
1882 void R_Shadow_FreeWorldLight(worldlight_t *light)
1883 {
1884         worldlight_t **lightpointer;
1885         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
1886         if (*lightpointer != light)
1887                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
1888         *lightpointer = light->next;
1889         if (light->cubemapname)
1890                 Mem_Free(light->cubemapname);
1891         if (light->shadowvolume)
1892                 Mod_ShadowMesh_Free(light->shadowvolume);
1893         if (light->surfaces)
1894                 Mem_Free(light->surfaces);
1895         Mem_Free(light);
1896 }
1897
1898 void R_Shadow_ClearWorldLights(void)
1899 {
1900         while (r_shadow_worldlightchain)
1901                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
1902         r_shadow_selectedlight = NULL;
1903 }
1904
1905 void R_Shadow_SelectLight(worldlight_t *light)
1906 {
1907         if (r_shadow_selectedlight)
1908                 r_shadow_selectedlight->selected = false;
1909         r_shadow_selectedlight = light;
1910         if (r_shadow_selectedlight)
1911                 r_shadow_selectedlight->selected = true;
1912 }
1913
1914 rtexture_t *lighttextures[5];
1915
1916 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
1917 {
1918         float scale = r_editlights_cursorgrid.value * 0.5f;
1919         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
1920 }
1921
1922 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
1923 {
1924         float intensity;
1925         const worldlight_t *light;
1926         light = calldata1;
1927         intensity = 0.5;
1928         if (light->selected)
1929                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
1930         if (!light->shadowvolume)
1931                 intensity *= 0.5f;
1932         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
1933 }
1934
1935 void R_Shadow_DrawLightSprites(void)
1936 {
1937         int i;
1938         cachepic_t *pic;
1939         worldlight_t *light;
1940
1941         for (i = 0;i < 5;i++)
1942         {
1943                 lighttextures[i] = NULL;
1944                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
1945                         lighttextures[i] = pic->tex;
1946         }
1947
1948         for (light = r_shadow_worldlightchain;light;light = light->next)
1949                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
1950         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
1951 }
1952
1953 void R_Shadow_SelectLightInView(void)
1954 {
1955         float bestrating, rating, temp[3];
1956         worldlight_t *best, *light;
1957         best = NULL;
1958         bestrating = 0;
1959         for (light = r_shadow_worldlightchain;light;light = light->next)
1960         {
1961                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
1962                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
1963                 if (rating >= 0.95)
1964                 {
1965                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
1966                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, true, NULL, SUPERCONTENTS_SOLID) == 1.0f)
1967                         {
1968                                 bestrating = rating;
1969                                 best = light;
1970                         }
1971                 }
1972         }
1973         R_Shadow_SelectLight(best);
1974 }
1975
1976 void R_Shadow_LoadWorldLights(void)
1977 {
1978         int n, a, style, shadow;
1979         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
1980         float origin[3], radius, color[3];
1981         if (cl.worldmodel == NULL)
1982         {
1983                 Con_Printf("No map loaded.\n");
1984                 return;
1985         }
1986         FS_StripExtension(cl.worldmodel->name, name);
1987         strcat(name, ".rtlights");
1988         lightsstring = FS_LoadFile(name, false);
1989         if (lightsstring)
1990         {
1991                 s = lightsstring;
1992                 n = 0;
1993                 while (*s)
1994                 {
1995                         t = s;
1996                         while (*s && *s != '\n')
1997                                 s++;
1998                         if (!*s)
1999                                 break;
2000                         *s = 0;
2001                         shadow = true;
2002                         // check for modifier flags
2003                         if (*t == '!')
2004                         {
2005                                 shadow = false;
2006                                 t++;
2007                         }
2008                         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);
2009                         if (a < 9)
2010                                 cubemapname[0] = 0;
2011                         *s = '\n';
2012                         if (a < 8)
2013                         {
2014                                 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);
2015                                 break;
2016                         }
2017                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2018                         radius *= r_editlights_rtlightssizescale.value;
2019                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2020                         s++;
2021                         n++;
2022                 }
2023                 if (*s)
2024                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2025                 Mem_Free(lightsstring);
2026         }
2027 }
2028
2029 void R_Shadow_SaveWorldLights(void)
2030 {
2031         worldlight_t *light;
2032         int bufchars, bufmaxchars;
2033         char *buf, *oldbuf;
2034         char name[MAX_QPATH];
2035         char line[1024];
2036         if (!r_shadow_worldlightchain)
2037                 return;
2038         if (cl.worldmodel == NULL)
2039         {
2040                 Con_Printf("No map loaded.\n");
2041                 return;
2042         }
2043         FS_StripExtension(cl.worldmodel->name, name);
2044         strcat(name, ".rtlights");
2045         bufchars = bufmaxchars = 0;
2046         buf = NULL;
2047         for (light = r_shadow_worldlightchain;light;light = light->next)
2048         {
2049                 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 : "");
2050                 if (bufchars + (int) strlen(line) > bufmaxchars)
2051                 {
2052                         bufmaxchars = bufchars + strlen(line) + 2048;
2053                         oldbuf = buf;
2054                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2055                         if (oldbuf)
2056                         {
2057                                 if (bufchars)
2058                                         memcpy(buf, oldbuf, bufchars);
2059                                 Mem_Free(oldbuf);
2060                         }
2061                 }
2062                 if (strlen(line))
2063                 {
2064                         memcpy(buf + bufchars, line, strlen(line));
2065                         bufchars += strlen(line);
2066                 }
2067         }
2068         if (bufchars)
2069                 FS_WriteFile(name, buf, bufchars);
2070         if (buf)
2071                 Mem_Free(buf);
2072 }
2073
2074 void R_Shadow_LoadLightsFile(void)
2075 {
2076         int n, a, style;
2077         char name[MAX_QPATH], *lightsstring, *s, *t;
2078         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2079         if (cl.worldmodel == NULL)
2080         {
2081                 Con_Printf("No map loaded.\n");
2082                 return;
2083         }
2084         FS_StripExtension(cl.worldmodel->name, name);
2085         strcat(name, ".lights");
2086         lightsstring = FS_LoadFile(name, false);
2087         if (lightsstring)
2088         {
2089                 s = lightsstring;
2090                 n = 0;
2091                 while (*s)
2092                 {
2093                         t = s;
2094                         while (*s && *s != '\n')
2095                                 s++;
2096                         if (!*s)
2097                                 break;
2098                         *s = 0;
2099                         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);
2100                         *s = '\n';
2101                         if (a < 14)
2102                         {
2103                                 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);
2104                                 break;
2105                         }
2106                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2107                         radius = bound(15, radius, 4096);
2108                         VectorScale(color, (2.0f / (8388608.0f)), color);
2109                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2110                         s++;
2111                         n++;
2112                 }
2113                 if (*s)
2114                         Con_Printf("invalid lights file \"%s\"\n", name);
2115                 Mem_Free(lightsstring);
2116         }
2117 }
2118
2119 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2120 {
2121         int entnum, style, islight;
2122         char key[256], value[1024];
2123         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2124         const char *data;
2125
2126         if (cl.worldmodel == NULL)
2127         {
2128                 Con_Printf("No map loaded.\n");
2129                 return;
2130         }
2131         data = cl.worldmodel->brush.entities;
2132         if (!data)
2133                 return;
2134         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2135         {
2136                 light = 0;
2137                 origin[0] = origin[1] = origin[2] = 0;
2138                 originhack[0] = originhack[1] = originhack[2] = 0;
2139                 color[0] = color[1] = color[2] = 1;
2140                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2141                 scale = 1;
2142                 style = 0;
2143                 islight = false;
2144                 while (1)
2145                 {
2146                         if (!COM_ParseToken(&data, false))
2147                                 break; // error
2148                         if (com_token[0] == '}')
2149                                 break; // end of entity
2150                         if (com_token[0] == '_')
2151                                 strcpy(key, com_token + 1);
2152                         else
2153                                 strcpy(key, com_token);
2154                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2155                                 key[strlen(key)-1] = 0;
2156                         if (!COM_ParseToken(&data, false))
2157                                 break; // error
2158                         strcpy(value, com_token);
2159
2160                         // now that we have the key pair worked out...
2161                         if (!strcmp("light", key))
2162                                 light = atof(value);
2163                         else if (!strcmp("origin", key))
2164                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2165                         else if (!strcmp("color", key))
2166                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2167                         else if (!strcmp("wait", key))
2168                                 scale = atof(value);
2169                         else if (!strcmp("classname", key))
2170                         {
2171                                 if (!strncmp(value, "light", 5))
2172                                 {
2173                                         islight = true;
2174                                         if (!strcmp(value, "light_fluoro"))
2175                                         {
2176                                                 originhack[0] = 0;
2177                                                 originhack[1] = 0;
2178                                                 originhack[2] = 0;
2179                                                 overridecolor[0] = 1;
2180                                                 overridecolor[1] = 1;
2181                                                 overridecolor[2] = 1;
2182                                         }
2183                                         if (!strcmp(value, "light_fluorospark"))
2184                                         {
2185                                                 originhack[0] = 0;
2186                                                 originhack[1] = 0;
2187                                                 originhack[2] = 0;
2188                                                 overridecolor[0] = 1;
2189                                                 overridecolor[1] = 1;
2190                                                 overridecolor[2] = 1;
2191                                         }
2192                                         if (!strcmp(value, "light_globe"))
2193                                         {
2194                                                 originhack[0] = 0;
2195                                                 originhack[1] = 0;
2196                                                 originhack[2] = 0;
2197                                                 overridecolor[0] = 1;
2198                                                 overridecolor[1] = 0.8;
2199                                                 overridecolor[2] = 0.4;
2200                                         }
2201                                         if (!strcmp(value, "light_flame_large_yellow"))
2202                                         {
2203                                                 originhack[0] = 0;
2204                                                 originhack[1] = 0;
2205                                                 originhack[2] = 48;
2206                                                 overridecolor[0] = 1;
2207                                                 overridecolor[1] = 0.5;
2208                                                 overridecolor[2] = 0.1;
2209                                         }
2210                                         if (!strcmp(value, "light_flame_small_yellow"))
2211                                         {
2212                                                 originhack[0] = 0;
2213                                                 originhack[1] = 0;
2214                                                 originhack[2] = 40;
2215                                                 overridecolor[0] = 1;
2216                                                 overridecolor[1] = 0.5;
2217                                                 overridecolor[2] = 0.1;
2218                                         }
2219                                         if (!strcmp(value, "light_torch_small_white"))
2220                                         {
2221                                                 originhack[0] = 0;
2222                                                 originhack[1] = 0;
2223                                                 originhack[2] = 40;
2224                                                 overridecolor[0] = 1;
2225                                                 overridecolor[1] = 0.5;
2226                                                 overridecolor[2] = 0.1;
2227                                         }
2228                                         if (!strcmp(value, "light_torch_small_walltorch"))
2229                                         {
2230                                                 originhack[0] = 0;
2231                                                 originhack[1] = 0;
2232                                                 originhack[2] = 40;
2233                                                 overridecolor[0] = 1;
2234                                                 overridecolor[1] = 0.5;
2235                                                 overridecolor[2] = 0.1;
2236                                         }
2237                                 }
2238                         }
2239                         else if (!strcmp("style", key))
2240                                 style = atoi(value);
2241                 }
2242                 if (light <= 0 && islight)
2243                         light = 300;
2244                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2245                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2246                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2247                         VectorCopy(overridecolor, color);
2248                 VectorScale(color, light, color);
2249                 VectorAdd(origin, originhack, origin);
2250                 if (radius >= 15)
2251                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2252         }
2253 }
2254
2255
2256 void R_Shadow_SetCursorLocationForView(void)
2257 {
2258         vec_t dist, push, frac;
2259         vec3_t dest, endpos, normal;
2260         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2261         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, true, NULL, SUPERCONTENTS_SOLID);
2262         if (frac < 1)
2263         {
2264                 dist = frac * r_editlights_cursordistance.value;
2265                 push = r_editlights_cursorpushback.value;
2266                 if (push > dist)
2267                         push = dist;
2268                 push = -push;
2269                 VectorMA(endpos, push, vpn, endpos);
2270                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2271         }
2272         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2273         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2274         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2275 }
2276
2277 void R_Shadow_UpdateWorldLightSelection(void)
2278 {
2279         if (r_editlights.integer)
2280         {
2281                 R_Shadow_SetCursorLocationForView();
2282                 R_Shadow_SelectLightInView();
2283                 R_Shadow_DrawLightSprites();
2284         }
2285         else
2286                 R_Shadow_SelectLight(NULL);
2287 }
2288
2289 void R_Shadow_EditLights_Clear_f(void)
2290 {
2291         R_Shadow_ClearWorldLights();
2292 }
2293
2294 void R_Shadow_EditLights_Reload_f(void)
2295 {
2296         r_shadow_reloadlights = true;
2297 }
2298
2299 void R_Shadow_EditLights_Save_f(void)
2300 {
2301         if (cl.worldmodel)
2302                 R_Shadow_SaveWorldLights();
2303 }
2304
2305 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2306 {
2307         R_Shadow_ClearWorldLights();
2308         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2309 }
2310
2311 void R_Shadow_EditLights_ImportLightsFile_f(void)
2312 {
2313         R_Shadow_ClearWorldLights();
2314         R_Shadow_LoadLightsFile();
2315 }
2316
2317 void R_Shadow_EditLights_Spawn_f(void)
2318 {
2319         vec3_t color;
2320         if (!r_editlights.integer)
2321         {
2322                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2323                 return;
2324         }
2325         if (Cmd_Argc() != 1)
2326         {
2327                 Con_Printf("r_editlights_spawn does not take parameters\n");
2328                 return;
2329         }
2330         color[0] = color[1] = color[2] = 1;
2331         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2332 }
2333
2334 void R_Shadow_EditLights_Edit_f(void)
2335 {
2336         vec3_t origin, color;
2337         vec_t radius;
2338         int style, shadows;
2339         char cubemapname[1024];
2340         if (!r_editlights.integer)
2341         {
2342                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2343                 return;
2344         }
2345         if (!r_shadow_selectedlight)
2346         {
2347                 Con_Printf("No selected light.\n");
2348                 return;
2349         }
2350         VectorCopy(r_shadow_selectedlight->origin, origin);
2351         radius = r_shadow_selectedlight->lightradius;
2352         VectorCopy(r_shadow_selectedlight->light, color);
2353         style = r_shadow_selectedlight->style;
2354         if (r_shadow_selectedlight->cubemapname)
2355                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2356         else
2357                 cubemapname[0] = 0;
2358         shadows = r_shadow_selectedlight->castshadows;
2359         if (!strcmp(Cmd_Argv(1), "origin"))
2360         {
2361                 if (Cmd_Argc() != 5)
2362                 {
2363                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2364                         return;
2365                 }
2366                 origin[0] = atof(Cmd_Argv(2));
2367                 origin[1] = atof(Cmd_Argv(3));
2368                 origin[2] = atof(Cmd_Argv(4));
2369         }
2370         else if (!strcmp(Cmd_Argv(1), "originx"))
2371         {
2372                 if (Cmd_Argc() != 3)
2373                 {
2374                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2375                         return;
2376                 }
2377                 origin[0] = atof(Cmd_Argv(2));
2378         }
2379         else if (!strcmp(Cmd_Argv(1), "originy"))
2380         {
2381                 if (Cmd_Argc() != 3)
2382                 {
2383                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2384                         return;
2385                 }
2386                 origin[1] = atof(Cmd_Argv(2));
2387         }
2388         else if (!strcmp(Cmd_Argv(1), "originz"))
2389         {
2390                 if (Cmd_Argc() != 3)
2391                 {
2392                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2393                         return;
2394                 }
2395                 origin[2] = atof(Cmd_Argv(2));
2396         }
2397         else if (!strcmp(Cmd_Argv(1), "move"))
2398         {
2399                 if (Cmd_Argc() != 5)
2400                 {
2401                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2402                         return;
2403                 }
2404                 origin[0] += atof(Cmd_Argv(2));
2405                 origin[1] += atof(Cmd_Argv(3));
2406                 origin[2] += atof(Cmd_Argv(4));
2407         }
2408         else if (!strcmp(Cmd_Argv(1), "movex"))
2409         {
2410                 if (Cmd_Argc() != 3)
2411                 {
2412                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2413                         return;
2414                 }
2415                 origin[0] += atof(Cmd_Argv(2));
2416         }
2417         else if (!strcmp(Cmd_Argv(1), "movey"))
2418         {
2419                 if (Cmd_Argc() != 3)
2420                 {
2421                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2422                         return;
2423                 }
2424                 origin[1] += atof(Cmd_Argv(2));
2425         }
2426         else if (!strcmp(Cmd_Argv(1), "movez"))
2427         {
2428                 if (Cmd_Argc() != 3)
2429                 {
2430                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2431                         return;
2432                 }
2433                 origin[2] += atof(Cmd_Argv(2));
2434         }
2435         else if (!strcmp(Cmd_Argv(1), "color"))
2436         {
2437                 if (Cmd_Argc() != 5)
2438                 {
2439                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2440                         return;
2441                 }
2442                 color[0] = atof(Cmd_Argv(2));
2443                 color[1] = atof(Cmd_Argv(3));
2444                 color[2] = atof(Cmd_Argv(4));
2445         }
2446         else if (!strcmp(Cmd_Argv(1), "radius"))
2447         {
2448                 if (Cmd_Argc() != 3)
2449                 {
2450                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2451                         return;
2452                 }
2453                 radius = atof(Cmd_Argv(2));
2454         }
2455         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2456         {
2457                 if (Cmd_Argc() != 3)
2458                 {
2459                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2460                         return;
2461                 }
2462                 style = atoi(Cmd_Argv(2));
2463         }
2464         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2465         {
2466                 if (Cmd_Argc() > 3)
2467                 {
2468                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2469                         return;
2470                 }
2471                 if (Cmd_Argc() == 3)
2472                         strcpy(cubemapname, Cmd_Argv(2));
2473                 else
2474                         cubemapname[0] = 0;
2475         }
2476         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2477         {
2478                 if (Cmd_Argc() != 3)
2479                 {
2480                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2481                         return;
2482                 }
2483                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2484         }
2485         else
2486         {
2487                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2488                 Con_Printf("Selected light's properties:\n");
2489                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2490                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2491                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2492                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2493                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2494                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2495                 return;
2496         }
2497         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2498         r_shadow_selectedlight = NULL;
2499         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2500 }
2501
2502 extern int con_vislines;
2503 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2504 {
2505         float x, y;
2506         char temp[256];
2507         if (r_shadow_selectedlight == NULL)
2508                 return;
2509         x = 0;
2510         y = con_vislines;
2511         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2512         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;
2513         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2514         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;
2515         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2516         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2517         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;
2518 }
2519
2520 void R_Shadow_EditLights_ToggleShadow_f(void)
2521 {
2522         if (!r_editlights.integer)
2523         {
2524                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2525                 return;
2526         }
2527         if (!r_shadow_selectedlight)
2528         {
2529                 Con_Printf("No selected light.\n");
2530                 return;
2531         }
2532         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);
2533         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2534         r_shadow_selectedlight = NULL;
2535 }
2536
2537 void R_Shadow_EditLights_Remove_f(void)
2538 {
2539         if (!r_editlights.integer)
2540         {
2541                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2542                 return;
2543         }
2544         if (!r_shadow_selectedlight)
2545         {
2546                 Con_Printf("No selected light.\n");
2547                 return;
2548         }
2549         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2550         r_shadow_selectedlight = NULL;
2551 }
2552
2553 void R_Shadow_EditLights_Init(void)
2554 {
2555         Cvar_RegisterVariable(&r_editlights);
2556         Cvar_RegisterVariable(&r_editlights_cursordistance);
2557         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2558         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2559         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2560         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2561         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2562         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2563         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2564         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2565         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2566         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2567         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2568         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2569         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2570         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2571         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2572 }