]> icculus.org git repositories - divverent/darkplaces.git/blob - r_shadow.c
cd audio now tied to sound system
[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 cvar_t r_shadow_shadows = {CVAR_SAVE, "r_shadow_shadows", "1"};
160
161 int c_rt_lights, c_rt_clears, c_rt_scissored;
162 int c_rt_shadowmeshes, c_rt_shadowtris, c_rt_lightmeshes, c_rt_lighttris;
163 int c_rtcached_shadowmeshes, c_rtcached_shadowtris;
164
165 void R_Shadow_ClearWorldLights(void);
166 void R_Shadow_SaveWorldLights(void);
167 void R_Shadow_LoadWorldLights(void);
168 void R_Shadow_LoadLightsFile(void);
169 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void);
170
171 void r_shadow_start(void)
172 {
173         // allocate vertex processing arrays
174         r_shadow_mempool = Mem_AllocPool("R_Shadow");
175         maxshadowelements = 0;
176         shadowelements = NULL;
177         maxvertexupdate = 0;
178         vertexupdate = NULL;
179         vertexremap = NULL;
180         vertexupdatenum = 0;
181         maxtrianglefacinglight = 0;
182         trianglefacinglight = NULL;
183         trianglefacinglightlist = NULL;
184         r_shadow_normalcubetexture = NULL;
185         r_shadow_attenuation2dtexture = NULL;
186         r_shadow_attenuation3dtexture = NULL;
187         r_shadow_blankbumptexture = NULL;
188         r_shadow_blankglosstexture = NULL;
189         r_shadow_blankwhitetexture = NULL;
190         r_shadow_texturepool = NULL;
191         R_Shadow_ClearWorldLights();
192         r_shadow_reloadlights = true;
193 }
194
195 void r_shadow_shutdown(void)
196 {
197         R_Shadow_ClearWorldLights();
198         r_shadow_reloadlights = true;
199         r_shadow_normalcubetexture = NULL;
200         r_shadow_attenuation2dtexture = NULL;
201         r_shadow_attenuation3dtexture = NULL;
202         r_shadow_blankbumptexture = NULL;
203         r_shadow_blankglosstexture = NULL;
204         r_shadow_blankwhitetexture = NULL;
205         R_FreeTexturePool(&r_shadow_texturepool);
206         maxshadowelements = 0;
207         shadowelements = NULL;
208         maxvertexupdate = 0;
209         vertexupdate = NULL;
210         vertexremap = NULL;
211         vertexupdatenum = 0;
212         maxtrianglefacinglight = 0;
213         trianglefacinglight = NULL;
214         trianglefacinglightlist = NULL;
215         Mem_FreePool(&r_shadow_mempool);
216 }
217
218 void r_shadow_newmap(void)
219 {
220         R_Shadow_ClearWorldLights();
221         r_shadow_reloadlights = true;
222 }
223
224 void R_Shadow_Init(void)
225 {
226         Cvar_RegisterVariable(&r_shadow_lightattenuationpower);
227         Cvar_RegisterVariable(&r_shadow_lightattenuationscale);
228         Cvar_RegisterVariable(&r_shadow_lightintensityscale);
229         Cvar_RegisterVariable(&r_shadow_realtime_world);
230         Cvar_RegisterVariable(&r_shadow_realtime_dlight);
231         Cvar_RegisterVariable(&r_shadow_visiblevolumes);
232         Cvar_RegisterVariable(&r_shadow_gloss);
233         Cvar_RegisterVariable(&r_shadow_glossintensity);
234         Cvar_RegisterVariable(&r_shadow_gloss2intensity);
235         Cvar_RegisterVariable(&r_shadow_debuglight);
236         Cvar_RegisterVariable(&r_shadow_scissor);
237         Cvar_RegisterVariable(&r_shadow_bumpscale_bumpmap);
238         Cvar_RegisterVariable(&r_shadow_bumpscale_basetexture);
239         Cvar_RegisterVariable(&r_shadow_polygonoffset);
240         Cvar_RegisterVariable(&r_shadow_portallight);
241         Cvar_RegisterVariable(&r_shadow_projectdistance);
242         Cvar_RegisterVariable(&r_shadow_texture3d);
243         Cvar_RegisterVariable(&r_shadow_singlepassvolumegeneration);
244         Cvar_RegisterVariable(&r_shadow_shadows);
245         R_Shadow_EditLights_Init();
246         R_RegisterModule("R_Shadow", r_shadow_start, r_shadow_shutdown, r_shadow_newmap);
247 }
248
249 void R_Shadow_ResizeTriangleFacingLight(int numtris)
250 {
251         // make sure trianglefacinglight is big enough for this volume
252         // ameks ru ertaignelaficgnilhg tsib gie ongu hof rhtsiv lomu e
253         // m4k3 5ur3 7r14ng13f4c1n5115h7 15 b15 3n0u5h f0r 7h15 v01um3
254         if (maxtrianglefacinglight < numtris)
255         {
256                 maxtrianglefacinglight = numtris;
257                 if (trianglefacinglight)
258                         Mem_Free(trianglefacinglight);
259                 if (trianglefacinglightlist)
260                         Mem_Free(trianglefacinglightlist);
261                 trianglefacinglight = Mem_Alloc(r_shadow_mempool, maxtrianglefacinglight);
262                 trianglefacinglightlist = Mem_Alloc(r_shadow_mempool, sizeof(int) * maxtrianglefacinglight);
263         }
264 }
265
266 int *R_Shadow_ResizeShadowElements(int numtris)
267 {
268         // make sure shadowelements is big enough for this volume
269         if (maxshadowelements < numtris * 24)
270         {
271                 maxshadowelements = numtris * 24;
272                 if (shadowelements)
273                         Mem_Free(shadowelements);
274                 shadowelements = Mem_Alloc(r_shadow_mempool, maxshadowelements * sizeof(int));
275         }
276         return shadowelements;
277 }
278
279 /*
280 // readable version of some code found below
281 //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]))))
282 int PointInfrontOfTriangle(const float *p, const float *a, const float *b, const float *c)
283 {
284         float dir0[3], dir1[3], normal[3];
285
286         // calculate two mostly perpendicular edge directions
287         VectorSubtract(a, b, dir0);
288         VectorSubtract(c, b, dir1);
289
290         // we have two edge directions, we can calculate a third vector from
291         // them, which is the direction of the surface normal (it's magnitude
292         // is not 1 however)
293         CrossProduct(dir0, dir1, normal);
294
295         // compare distance of light along normal, with distance of any point
296         // of the triangle along the same normal (the triangle is planar,
297         // I.E. flat, so all points give the same answer)
298         return DotProduct(p, normal) > DotProduct(a, normal);
299 }
300 int checkcastshadowfromedge(int t, int i)
301 {
302         int *te;
303         float *v[3];
304         if (t >= trianglerange_start && t < trianglerange_end)
305         {
306                 if (t < i && !trianglefacinglight[t])
307                         return true;
308                 else
309                         return false;
310         }
311         else
312         {
313                 if (t < 0)
314                         return true;
315                 else
316                 {
317                         te = inelement3i + t * 3;
318                         v[0] = invertex3f + te[0] * 3;
319                         v[1] = invertex3f + te[1] * 3;
320                         v[2] = invertex3f + te[2] * 3;
321                         if (!PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2]))))
322                                 return true;
323                         else
324                                 return false;
325                 }
326         }
327 }
328 */
329
330 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)
331 {
332         int i, j, tris = 0, numfacing = 0, vr[3], t, outvertices = 0;
333         const float *v[3];
334         const int *e, *n, *te;
335         float f, temp[3];
336
337         // make sure trianglefacinglight is big enough for this volume
338         if (maxtrianglefacinglight < trianglerange_end)
339                 R_Shadow_ResizeTriangleFacingLight(trianglerange_end);
340
341         if (maxvertexupdate < innumvertices)
342         {
343                 maxvertexupdate = innumvertices;
344                 if (vertexupdate)
345                         Mem_Free(vertexupdate);
346                 if (vertexremap)
347                         Mem_Free(vertexremap);
348                 vertexupdate = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
349                 vertexremap = Mem_Alloc(r_shadow_mempool, maxvertexupdate * sizeof(int));
350         }
351         vertexupdatenum++;
352
353         if (r_shadow_singlepassvolumegeneration.integer)
354         {
355                 // one pass approach (identify lit/dark faces and generate sides while doing so)
356                 for (i = trianglerange_start, e = inelement3i + i * 3, n = inneighbor3i + i * 3;i < trianglerange_end;i++, e += 3, n += 3)
357                 {
358                         // calculate triangle facing flag
359                         v[0] = invertex3f + e[0] * 3;
360                         v[1] = invertex3f + e[1] * 3;
361                         v[2] = invertex3f + e[2] * 3;
362                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
363                         {
364                                 // make sure the vertices are created
365                                 for (j = 0;j < 3;j++)
366                                 {
367                                         if (vertexupdate[e[j]] != vertexupdatenum)
368                                         {
369                                                 vertexupdate[e[j]] = vertexupdatenum;
370                                                 vertexremap[e[j]] = outvertices;
371                                                 VectorCopy(v[j], outvertex3f);
372                                                 VectorSubtract(v[j], relativelightorigin, temp);
373                                                 f = projectdistance / VectorLength(temp);
374                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
375                                                 outvertex3f += 6;
376                                                 outvertices += 2;
377                                         }
378                                 }
379                                 // output the front and back triangles
380                                 vr[0] = vertexremap[e[0]];
381                                 vr[1] = vertexremap[e[1]];
382                                 vr[2] = vertexremap[e[2]];
383                                 outelement3i[0] = vr[0];
384                                 outelement3i[1] = vr[1];
385                                 outelement3i[2] = vr[2];
386                                 outelement3i[3] = vr[2] + 1;
387                                 outelement3i[4] = vr[1] + 1;
388                                 outelement3i[5] = vr[0] + 1;
389                                 outelement3i += 6;
390                                 tris += 2;
391                                 // output the sides (facing outward from this triangle)
392                                 t = n[0];
393                                 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]))))
394                                 {
395                                         outelement3i[0] = vr[1];
396                                         outelement3i[1] = vr[0];
397                                         outelement3i[2] = vr[0] + 1;
398                                         outelement3i[3] = vr[1];
399                                         outelement3i[4] = vr[0] + 1;
400                                         outelement3i[5] = vr[1] + 1;
401                                         outelement3i += 6;
402                                         tris += 2;
403                                 }
404                                 t = n[1];
405                                 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]))))
406                                 {
407                                         outelement3i[0] = vr[2];
408                                         outelement3i[1] = vr[1];
409                                         outelement3i[2] = vr[1] + 1;
410                                         outelement3i[3] = vr[2];
411                                         outelement3i[4] = vr[1] + 1;
412                                         outelement3i[5] = vr[2] + 1;
413                                         outelement3i += 6;
414                                         tris += 2;
415                                 }
416                                 t = n[2];
417                                 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]))))
418                                 {
419                                         outelement3i[0] = vr[0];
420                                         outelement3i[1] = vr[2];
421                                         outelement3i[2] = vr[2] + 1;
422                                         outelement3i[3] = vr[0];
423                                         outelement3i[4] = vr[2] + 1;
424                                         outelement3i[5] = vr[0] + 1;
425                                         outelement3i += 6;
426                                         tris += 2;
427                                 }
428                         }
429                         else
430                         {
431                                 // this triangle is not facing the light
432                                 // output the sides (facing inward to this triangle)
433                                 t = n[0];
434                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
435                                 {
436                                         vr[0] = vertexremap[e[0]];
437                                         vr[1] = vertexremap[e[1]];
438                                         outelement3i[0] = vr[1];
439                                         outelement3i[1] = vr[0] + 1;
440                                         outelement3i[2] = vr[0];
441                                         outelement3i[3] = vr[1];
442                                         outelement3i[4] = vr[1] + 1;
443                                         outelement3i[5] = vr[0] + 1;
444                                         outelement3i += 6;
445                                         tris += 2;
446                                 }
447                                 t = n[1];
448                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
449                                 {
450                                         vr[1] = vertexremap[e[1]];
451                                         vr[2] = vertexremap[e[2]];
452                                         outelement3i[0] = vr[2];
453                                         outelement3i[1] = vr[1] + 1;
454                                         outelement3i[2] = vr[1];
455                                         outelement3i[3] = vr[2];
456                                         outelement3i[4] = vr[2] + 1;
457                                         outelement3i[5] = vr[1] + 1;
458                                         outelement3i += 6;
459                                         tris += 2;
460                                 }
461                                 t = n[2];
462                                 if (t < i && t >= trianglerange_start && t < trianglerange_end && trianglefacinglight[t])
463                                 {
464                                         vr[0] = vertexremap[e[0]];
465                                         vr[2] = vertexremap[e[2]];
466                                         outelement3i[0] = vr[0];
467                                         outelement3i[1] = vr[2] + 1;
468                                         outelement3i[2] = vr[2];
469                                         outelement3i[3] = vr[0];
470                                         outelement3i[4] = vr[0] + 1;
471                                         outelement3i[5] = vr[2] + 1;
472                                         outelement3i += 6;
473                                         tris += 2;
474                                 }
475                         }
476                 }
477         }
478         else
479         {
480                 // two pass approach (identify lit/dark faces and then generate sides)
481                 for (i = trianglerange_start, e = inelement3i + i * 3, numfacing = 0;i < trianglerange_end;i++, e += 3)
482                 {
483                         // calculate triangle facing flag
484                         v[0] = invertex3f + e[0] * 3;
485                         v[1] = invertex3f + e[1] * 3;
486                         v[2] = invertex3f + e[2] * 3;
487                         if((trianglefacinglight[i] = PointInfrontOfTriangle(relativelightorigin, v[0], v[1], v[2])))
488                         {
489                                 trianglefacinglightlist[numfacing++] = i;
490                                 // make sure the vertices are created
491                                 for (j = 0;j < 3;j++)
492                                 {
493                                         if (vertexupdate[e[j]] != vertexupdatenum)
494                                         {
495                                                 vertexupdate[e[j]] = vertexupdatenum;
496                                                 vertexremap[e[j]] = outvertices;
497                                                 VectorSubtract(v[j], relativelightorigin, temp);
498                                                 f = projectdistance / VectorLength(temp);
499                                                 VectorCopy(v[j], outvertex3f);
500                                                 VectorMA(relativelightorigin, f, temp, (outvertex3f + 3));
501                                                 outvertex3f += 6;
502                                                 outvertices += 2;
503                                         }
504                                 }
505                                 // output the front and back triangles
506                                 outelement3i[0] = vertexremap[e[0]];
507                                 outelement3i[1] = vertexremap[e[1]];
508                                 outelement3i[2] = vertexremap[e[2]];
509                                 outelement3i[3] = vertexremap[e[2]] + 1;
510                                 outelement3i[4] = vertexremap[e[1]] + 1;
511                                 outelement3i[5] = vertexremap[e[0]] + 1;
512                                 outelement3i += 6;
513                                 tris += 2;
514                         }
515                 }
516                 for (i = 0;i < numfacing;i++)
517                 {
518                         t = trianglefacinglightlist[i];
519                         e = inelement3i + t * 3;
520                         n = inneighbor3i + t * 3;
521                         // output the sides (facing outward from this triangle)
522                         t = n[0];
523                         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]))))
524                         {
525                                 vr[0] = vertexremap[e[0]];
526                                 vr[1] = vertexremap[e[1]];
527                                 outelement3i[0] = vr[1];
528                                 outelement3i[1] = vr[0];
529                                 outelement3i[2] = vr[0] + 1;
530                                 outelement3i[3] = vr[1];
531                                 outelement3i[4] = vr[0] + 1;
532                                 outelement3i[5] = vr[1] + 1;
533                                 outelement3i += 6;
534                                 tris += 2;
535                         }
536                         t = n[1];
537                         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]))))
538                         {
539                                 vr[1] = vertexremap[e[1]];
540                                 vr[2] = vertexremap[e[2]];
541                                 outelement3i[0] = vr[2];
542                                 outelement3i[1] = vr[1];
543                                 outelement3i[2] = vr[1] + 1;
544                                 outelement3i[3] = vr[2];
545                                 outelement3i[4] = vr[1] + 1;
546                                 outelement3i[5] = vr[2] + 1;
547                                 outelement3i += 6;
548                                 tris += 2;
549                         }
550                         t = n[2];
551                         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]))))
552                         {
553                                 vr[0] = vertexremap[e[0]];
554                                 vr[2] = vertexremap[e[2]];
555                                 outelement3i[0] = vr[0];
556                                 outelement3i[1] = vr[2];
557                                 outelement3i[2] = vr[2] + 1;
558                                 outelement3i[3] = vr[0];
559                                 outelement3i[4] = vr[2] + 1;
560                                 outelement3i[5] = vr[0] + 1;
561                                 outelement3i += 6;
562                                 tris += 2;
563                         }
564                 }
565         }
566         if (outnumvertices)
567                 *outnumvertices = outvertices;
568         return tris;
569 }
570
571 float varray_vertex3f2[65536*3];
572
573 void R_Shadow_Volume(int numverts, int numtris, const float *invertex3f, int *elements, int *neighbors, vec3_t relativelightorigin, float lightradius, float projectdistance)
574 {
575         int tris, outverts;
576         if (projectdistance < 0.1)
577         {
578                 Con_Printf("R_Shadow_Volume: projectdistance %f\n");
579                 return;
580         }
581         if (!numverts)
582                 return;
583
584         // make sure shadowelements is big enough for this volume
585         if (maxshadowelements < numtris * 24)
586                 R_Shadow_ResizeShadowElements(numtris);
587
588         // check which triangles are facing the light, and then output
589         // triangle elements and vertices...  by clever use of elements we
590         // can construct the whole shadow from the unprojected vertices and
591         // the projected vertices
592         if ((tris = R_Shadow_ConstructShadowVolume(numverts, 0, numtris, elements, neighbors, invertex3f, &outverts, shadowelements, varray_vertex3f2, relativelightorigin, r_shadow_projectdistance.value/*projectdistance*/)))
593         {
594                 GL_VertexPointer(varray_vertex3f2);
595                 if (r_shadowstage == SHADOWSTAGE_STENCIL)
596                 {
597                         // increment stencil if backface is behind depthbuffer
598                         qglCullFace(GL_BACK); // quake is backwards, this culls front faces
599                         qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
600                         R_Mesh_Draw(outverts, tris, shadowelements);
601                         c_rt_shadowmeshes++;
602                         c_rt_shadowtris += numtris;
603                         // decrement stencil if frontface is behind depthbuffer
604                         qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
605                         qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
606                 }
607                 R_Mesh_Draw(outverts, tris, shadowelements);
608                 c_rt_shadowmeshes++;
609                 c_rt_shadowtris += numtris;
610         }
611 }
612
613 void R_Shadow_RenderShadowMeshVolume(shadowmesh_t *firstmesh)
614 {
615         shadowmesh_t *mesh;
616         if (r_shadowstage == SHADOWSTAGE_STENCIL)
617         {
618                 // increment stencil if backface is behind depthbuffer
619                 qglCullFace(GL_BACK); // quake is backwards, this culls front faces
620                 qglStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
621                 for (mesh = firstmesh;mesh;mesh = mesh->next)
622                 {
623                         GL_VertexPointer(mesh->vertex3f);
624                         R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
625                         c_rtcached_shadowmeshes++;
626                         c_rtcached_shadowtris += mesh->numtriangles;
627                 }
628                 // decrement stencil if frontface is behind depthbuffer
629                 qglCullFace(GL_FRONT); // quake is backwards, this culls back faces
630                 qglStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
631         }
632         for (mesh = firstmesh;mesh;mesh = mesh->next)
633         {
634                 GL_VertexPointer(mesh->vertex3f);
635                 R_Mesh_Draw(mesh->numverts, mesh->numtriangles, mesh->element3i);
636                 c_rtcached_shadowmeshes++;
637                 c_rtcached_shadowtris += mesh->numtriangles;
638         }
639 }
640
641 float r_shadow_attenpower, r_shadow_attenscale;
642 static void R_Shadow_MakeTextures(void)
643 {
644         int x, y, z, d, side;
645         float v[3], s, t, intensity;
646         qbyte *data;
647         R_FreeTexturePool(&r_shadow_texturepool);
648         r_shadow_texturepool = R_AllocTexturePool();
649         r_shadow_attenpower = r_shadow_lightattenuationpower.value;
650         r_shadow_attenscale = r_shadow_lightattenuationscale.value;
651 #define NORMSIZE 64
652 #define ATTEN2DSIZE 64
653 #define ATTEN3DSIZE 32
654         data = Mem_Alloc(tempmempool, max(6*NORMSIZE*NORMSIZE*4, max(ATTEN3DSIZE*ATTEN3DSIZE*ATTEN3DSIZE*4, ATTEN2DSIZE*ATTEN2DSIZE*4)));
655         data[0] = 128;
656         data[1] = 128;
657         data[2] = 255;
658         data[3] = 255;
659         r_shadow_blankbumptexture = R_LoadTexture2D(r_shadow_texturepool, "blankbump", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
660         data[0] = 255;
661         data[1] = 255;
662         data[2] = 255;
663         data[3] = 255;
664         r_shadow_blankglosstexture = R_LoadTexture2D(r_shadow_texturepool, "blankgloss", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
665         data[0] = 255;
666         data[1] = 255;
667         data[2] = 255;
668         data[3] = 255;
669         r_shadow_blankwhitetexture = R_LoadTexture2D(r_shadow_texturepool, "blankwhite", 1, 1, data, TEXTYPE_RGBA, TEXF_PRECACHE, NULL);
670         if (gl_texturecubemap)
671         {
672                 for (side = 0;side < 6;side++)
673                 {
674                         for (y = 0;y < NORMSIZE;y++)
675                         {
676                                 for (x = 0;x < NORMSIZE;x++)
677                                 {
678                                         s = (x + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
679                                         t = (y + 0.5f) * (2.0f / NORMSIZE) - 1.0f;
680                                         switch(side)
681                                         {
682                                         case 0:
683                                                 v[0] = 1;
684                                                 v[1] = -t;
685                                                 v[2] = -s;
686                                                 break;
687                                         case 1:
688                                                 v[0] = -1;
689                                                 v[1] = -t;
690                                                 v[2] = s;
691                                                 break;
692                                         case 2:
693                                                 v[0] = s;
694                                                 v[1] = 1;
695                                                 v[2] = t;
696                                                 break;
697                                         case 3:
698                                                 v[0] = s;
699                                                 v[1] = -1;
700                                                 v[2] = -t;
701                                                 break;
702                                         case 4:
703                                                 v[0] = s;
704                                                 v[1] = -t;
705                                                 v[2] = 1;
706                                                 break;
707                                         case 5:
708                                                 v[0] = -s;
709                                                 v[1] = -t;
710                                                 v[2] = -1;
711                                                 break;
712                                         }
713                                         intensity = 127.0f / sqrt(DotProduct(v, v));
714                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+0] = 128.0f + intensity * v[0];
715                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+1] = 128.0f + intensity * v[1];
716                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+2] = 128.0f + intensity * v[2];
717                                         data[((side*NORMSIZE+y)*NORMSIZE+x)*4+3] = 255;
718                                 }
719                         }
720                 }
721                 r_shadow_normalcubetexture = R_LoadTextureCubeMap(r_shadow_texturepool, "normalcube", NORMSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP, NULL);
722         }
723         else
724                 r_shadow_normalcubetexture = NULL;
725         for (y = 0;y < ATTEN2DSIZE;y++)
726         {
727                 for (x = 0;x < ATTEN2DSIZE;x++)
728                 {
729                         v[0] = ((x + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
730                         v[1] = ((y + 0.5f) * (2.0f / ATTEN2DSIZE) - 1.0f) * (1.0f / 0.9375);
731                         v[2] = 0;
732                         intensity = 1.0f - sqrt(DotProduct(v, v));
733                         if (intensity > 0)
734                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
735                         d = bound(0, intensity, 255);
736                         data[(y*ATTEN2DSIZE+x)*4+0] = d;
737                         data[(y*ATTEN2DSIZE+x)*4+1] = d;
738                         data[(y*ATTEN2DSIZE+x)*4+2] = d;
739                         data[(y*ATTEN2DSIZE+x)*4+3] = d;
740                 }
741         }
742         r_shadow_attenuation2dtexture = R_LoadTexture2D(r_shadow_texturepool, "attenuation2d", ATTEN2DSIZE, ATTEN2DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
743         if (r_shadow_texture3d.integer)
744         {
745                 for (z = 0;z < ATTEN3DSIZE;z++)
746                 {
747                         for (y = 0;y < ATTEN3DSIZE;y++)
748                         {
749                                 for (x = 0;x < ATTEN3DSIZE;x++)
750                                 {
751                                         v[0] = ((x + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
752                                         v[1] = ((y + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
753                                         v[2] = ((z + 0.5f) * (2.0f / ATTEN3DSIZE) - 1.0f) * (1.0f / 0.9375);
754                                         intensity = 1.0f - sqrt(DotProduct(v, v));
755                                         if (intensity > 0)
756                                                 intensity = pow(intensity, r_shadow_attenpower) * r_shadow_attenscale * 256.0f;
757                                         d = bound(0, intensity, 255);
758                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+0] = d;
759                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+1] = d;
760                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+2] = d;
761                                         data[((z*ATTEN3DSIZE+y)*ATTEN3DSIZE+x)*4+3] = d;
762                                 }
763                         }
764                 }
765                 r_shadow_attenuation3dtexture = R_LoadTexture3D(r_shadow_texturepool, "attenuation3d", ATTEN3DSIZE, ATTEN3DSIZE, ATTEN3DSIZE, data, TEXTYPE_RGBA, TEXF_PRECACHE | TEXF_CLAMP | TEXF_ALPHA, NULL);
766         }
767         Mem_Free(data);
768 }
769
770 void R_Shadow_Stage_Begin(void)
771 {
772         rmeshstate_t m;
773
774         if (r_shadow_texture3d.integer && !gl_texture3d)
775                 Cvar_SetValueQuick(&r_shadow_texture3d, 0);
776
777         if (!r_shadow_attenuation2dtexture
778          || (!r_shadow_attenuation3dtexture && r_shadow_texture3d.integer)
779          || r_shadow_lightattenuationpower.value != r_shadow_attenpower
780          || r_shadow_lightattenuationscale.value != r_shadow_attenscale)
781                 R_Shadow_MakeTextures();
782
783         memset(&m, 0, sizeof(m));
784         GL_BlendFunc(GL_ONE, GL_ZERO);
785         GL_DepthMask(false);
786         GL_DepthTest(true);
787         R_Mesh_State_Texture(&m);
788         GL_Color(0, 0, 0, 1);
789         qglDisable(GL_SCISSOR_TEST);
790         r_shadowstage = SHADOWSTAGE_NONE;
791
792         c_rt_lights = c_rt_clears = c_rt_scissored = 0;
793         c_rt_shadowmeshes = c_rt_shadowtris = c_rt_lightmeshes = c_rt_lighttris = 0;
794         c_rtcached_shadowmeshes = c_rtcached_shadowtris = 0;
795 }
796
797 void R_Shadow_LoadWorldLightsIfNeeded(void)
798 {
799         if (r_shadow_reloadlights && cl.worldmodel)
800         {
801                 R_Shadow_ClearWorldLights();
802                 r_shadow_reloadlights = false;
803                 R_Shadow_LoadWorldLights();
804                 if (r_shadow_worldlightchain == NULL)
805                 {
806                         R_Shadow_LoadLightsFile();
807                         if (r_shadow_worldlightchain == NULL)
808                                 R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
809                 }
810         }
811 }
812
813 void R_Shadow_Stage_ShadowVolumes(void)
814 {
815         rmeshstate_t m;
816         memset(&m, 0, sizeof(m));
817         R_Mesh_State_Texture(&m);
818         GL_Color(1, 1, 1, 1);
819         qglColorMask(0, 0, 0, 0);
820         GL_BlendFunc(GL_ONE, GL_ZERO);
821         GL_DepthMask(false);
822         GL_DepthTest(true);
823         if (r_shadow_polygonoffset.value != 0)
824         {
825                 qglPolygonOffset(1.0f, r_shadow_polygonoffset.value);
826                 qglEnable(GL_POLYGON_OFFSET_FILL);
827         }
828         else
829                 qglDisable(GL_POLYGON_OFFSET_FILL);
830         qglDepthFunc(GL_LESS);
831         qglEnable(GL_STENCIL_TEST);
832         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
833         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
834         r_shadowstage = SHADOWSTAGE_STENCIL;
835         qglClear(GL_STENCIL_BUFFER_BIT);
836         c_rt_clears++;
837         // LordHavoc note: many shadow volumes reside entirely inside the world
838         // (that is to say they are entirely bounded by their lit surfaces),
839         // which can be optimized by handling things as an inverted light volume,
840         // with the shadow boundaries of the world being simulated by an altered
841         // (129) bias to stencil clearing on such lights
842         // FIXME: generate inverted light volumes for use as shadow volumes and
843         // optimize for them as noted above
844 }
845
846 void R_Shadow_Stage_LightWithoutShadows(void)
847 {
848         rmeshstate_t m;
849         memset(&m, 0, sizeof(m));
850         R_Mesh_State_Texture(&m);
851         GL_BlendFunc(GL_ONE, GL_ONE);
852         GL_DepthMask(false);
853         GL_DepthTest(true);
854         qglDisable(GL_POLYGON_OFFSET_FILL);
855         GL_Color(1, 1, 1, 1);
856         qglColorMask(1, 1, 1, 1);
857         qglDepthFunc(GL_EQUAL);
858         qglDisable(GL_STENCIL_TEST);
859         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
860         qglStencilFunc(GL_EQUAL, 128, 0xFF);
861         r_shadowstage = SHADOWSTAGE_LIGHT;
862         c_rt_lights++;
863 }
864
865 void R_Shadow_Stage_LightWithShadows(void)
866 {
867         rmeshstate_t m;
868         memset(&m, 0, sizeof(m));
869         R_Mesh_State_Texture(&m);
870         GL_BlendFunc(GL_ONE, GL_ONE);
871         GL_DepthMask(false);
872         GL_DepthTest(true);
873         qglDisable(GL_POLYGON_OFFSET_FILL);
874         GL_Color(1, 1, 1, 1);
875         qglColorMask(1, 1, 1, 1);
876         qglDepthFunc(GL_EQUAL);
877         qglEnable(GL_STENCIL_TEST);
878         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
879         // only draw light where this geometry was already rendered AND the
880         // stencil is 128 (values other than this mean shadow)
881         qglStencilFunc(GL_EQUAL, 128, 0xFF);
882         r_shadowstage = SHADOWSTAGE_LIGHT;
883         c_rt_lights++;
884 }
885
886 void R_Shadow_Stage_End(void)
887 {
888         rmeshstate_t m;
889         memset(&m, 0, sizeof(m));
890         R_Mesh_State_Texture(&m);
891         GL_BlendFunc(GL_ONE, GL_ZERO);
892         GL_DepthMask(true);
893         GL_DepthTest(true);
894         qglDisable(GL_POLYGON_OFFSET_FILL);
895         GL_Color(1, 1, 1, 1);
896         qglColorMask(1, 1, 1, 1);
897         qglDisable(GL_SCISSOR_TEST);
898         qglDepthFunc(GL_LEQUAL);
899         qglDisable(GL_STENCIL_TEST);
900         qglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
901         qglStencilFunc(GL_ALWAYS, 128, 0xFF);
902         r_shadowstage = SHADOWSTAGE_NONE;
903 }
904
905 int R_Shadow_ScissorForBBox(const float *mins, const float *maxs)
906 {
907         int i, ix1, iy1, ix2, iy2;
908         float x1, y1, x2, y2, x, y, f;
909         vec3_t smins, smaxs;
910         vec4_t v, v2;
911         if (!r_shadow_scissor.integer)
912                 return false;
913         // if view is inside the box, just say yes it's visible
914         // LordHavoc: for some odd reason scissor seems broken without stencil
915         // (?!?  seems like a driver bug) so abort if gl_stencil is false
916         if (!gl_stencil || BoxesOverlap(r_origin, r_origin, mins, maxs))
917         {
918                 qglDisable(GL_SCISSOR_TEST);
919                 return false;
920         }
921         for (i = 0;i < 3;i++)
922         {
923                 if (vpn[i] >= 0)
924                 {
925                         v[i] = mins[i];
926                         v2[i] = maxs[i];
927                 }
928                 else
929                 {
930                         v[i] = maxs[i];
931                         v2[i] = mins[i];
932                 }
933         }
934         f = DotProduct(vpn, r_origin) + 1;
935         if (DotProduct(vpn, v2) <= f)
936         {
937                 // entirely behind nearclip plane
938                 return true;
939         }
940         if (DotProduct(vpn, v) >= f)
941         {
942                 // entirely infront of nearclip plane
943                 x1 = y1 = x2 = y2 = 0;
944                 for (i = 0;i < 8;i++)
945                 {
946                         v[0] = (i & 1) ? mins[0] : maxs[0];
947                         v[1] = (i & 2) ? mins[1] : maxs[1];
948                         v[2] = (i & 4) ? mins[2] : maxs[2];
949                         v[3] = 1.0f;
950                         GL_TransformToScreen(v, v2);
951                         //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]);
952                         x = v2[0];
953                         y = v2[1];
954                         if (i)
955                         {
956                                 if (x1 > x) x1 = x;
957                                 if (x2 < x) x2 = x;
958                                 if (y1 > y) y1 = y;
959                                 if (y2 < y) y2 = y;
960                         }
961                         else
962                         {
963                                 x1 = x2 = x;
964                                 y1 = y2 = y;
965                         }
966                 }
967         }
968         else
969         {
970                 // clipped by nearclip plane
971                 // this is nasty and crude...
972                 // create viewspace bbox
973                 for (i = 0;i < 8;i++)
974                 {
975                         v[0] = ((i & 1) ? mins[0] : maxs[0]) - r_origin[0];
976                         v[1] = ((i & 2) ? mins[1] : maxs[1]) - r_origin[1];
977                         v[2] = ((i & 4) ? mins[2] : maxs[2]) - r_origin[2];
978                         v2[0] = DotProduct(v, vright);
979                         v2[1] = DotProduct(v, vup);
980                         v2[2] = DotProduct(v, vpn);
981                         if (i)
982                         {
983                                 if (smins[0] > v2[0]) smins[0] = v2[0];
984                                 if (smaxs[0] < v2[0]) smaxs[0] = v2[0];
985                                 if (smins[1] > v2[1]) smins[1] = v2[1];
986                                 if (smaxs[1] < v2[1]) smaxs[1] = v2[1];
987                                 if (smins[2] > v2[2]) smins[2] = v2[2];
988                                 if (smaxs[2] < v2[2]) smaxs[2] = v2[2];
989                         }
990                         else
991                         {
992                                 smins[0] = smaxs[0] = v2[0];
993                                 smins[1] = smaxs[1] = v2[1];
994                                 smins[2] = smaxs[2] = v2[2];
995                         }
996                 }
997                 // now we have a bbox in viewspace
998                 // clip it to the view plane
999                 if (smins[2] < 1)
1000                         smins[2] = 1;
1001                 // return true if that culled the box
1002                 if (smins[2] >= smaxs[2])
1003                         return true;
1004                 // ok some of it is infront of the view, transform each corner back to
1005                 // worldspace and then to screenspace and make screen rect
1006                 // initialize these variables just to avoid compiler warnings
1007                 x1 = y1 = x2 = y2 = 0;
1008                 for (i = 0;i < 8;i++)
1009                 {
1010                         v2[0] = (i & 1) ? smins[0] : smaxs[0];
1011                         v2[1] = (i & 2) ? smins[1] : smaxs[1];
1012                         v2[2] = (i & 4) ? smins[2] : smaxs[2];
1013                         v[0] = v2[0] * vright[0] + v2[1] * vup[0] + v2[2] * vpn[0] + r_origin[0];
1014                         v[1] = v2[0] * vright[1] + v2[1] * vup[1] + v2[2] * vpn[1] + r_origin[1];
1015                         v[2] = v2[0] * vright[2] + v2[1] * vup[2] + v2[2] * vpn[2] + r_origin[2];
1016                         v[3] = 1.0f;
1017                         GL_TransformToScreen(v, v2);
1018                         //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]);
1019                         x = v2[0];
1020                         y = v2[1];
1021                         if (i)
1022                         {
1023                                 if (x1 > x) x1 = x;
1024                                 if (x2 < x) x2 = x;
1025                                 if (y1 > y) y1 = y;
1026                                 if (y2 < y) y2 = y;
1027                         }
1028                         else
1029                         {
1030                                 x1 = x2 = x;
1031                                 y1 = y2 = y;
1032                         }
1033                 }
1034                 /*
1035                 // this code doesn't handle boxes with any points behind view properly
1036                 x1 = 1000;x2 = -1000;
1037                 y1 = 1000;y2 = -1000;
1038                 for (i = 0;i < 8;i++)
1039                 {
1040                         v[0] = (i & 1) ? mins[0] : maxs[0];
1041                         v[1] = (i & 2) ? mins[1] : maxs[1];
1042                         v[2] = (i & 4) ? mins[2] : maxs[2];
1043                         v[3] = 1.0f;
1044                         GL_TransformToScreen(v, v2);
1045                         //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]);
1046                         if (v2[2] > 0)
1047                         {
1048                                 x = v2[0];
1049                                 y = v2[1];
1050
1051                                 if (x1 > x) x1 = x;
1052                                 if (x2 < x) x2 = x;
1053                                 if (y1 > y) y1 = y;
1054                                 if (y2 < y) y2 = y;
1055                         }
1056                 }
1057                 */
1058         }
1059         ix1 = x1 - 1.0f;
1060         iy1 = y1 - 1.0f;
1061         ix2 = x2 + 1.0f;
1062         iy2 = y2 + 1.0f;
1063         //Con_Printf("%f %f %f %f\n", x1, y1, x2, y2);
1064         if (ix1 < r_refdef.x) ix1 = r_refdef.x;
1065         if (iy1 < r_refdef.y) iy1 = r_refdef.y;
1066         if (ix2 > r_refdef.x + r_refdef.width) ix2 = r_refdef.x + r_refdef.width;
1067         if (iy2 > r_refdef.y + r_refdef.height) iy2 = r_refdef.y + r_refdef.height;
1068         if (ix2 <= ix1 || iy2 <= iy1)
1069                 return true;
1070         // set up the scissor rectangle
1071         qglScissor(ix1, iy1, ix2 - ix1, iy2 - iy1);
1072         qglEnable(GL_SCISSOR_TEST);
1073         c_rt_scissored++;
1074         return false;
1075 }
1076
1077 void R_Shadow_VertexLighting(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1078 {
1079         float *color4f = varray_color4f;
1080         float dist, dot, intensity, v[3], n[3];
1081         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1082         {
1083                 Matrix4x4_Transform(m, vertex3f, v);
1084                 if ((dist = DotProduct(v, v)) < 1)
1085                 {
1086                         Matrix4x4_Transform3x3(m, normal3f, n);
1087                         if ((dot = DotProduct(n, v)) > 0)
1088                         {
1089                                 dist = sqrt(dist);
1090                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1091                                 VectorScale(lightcolor, intensity, color4f);
1092                                 color4f[3] = 1;
1093                         }
1094                         else
1095                         {
1096                                 VectorClear(color4f);
1097                                 color4f[3] = 1;
1098                         }
1099                 }
1100                 else
1101                 {
1102                         VectorClear(color4f);
1103                         color4f[3] = 1;
1104                 }
1105         }
1106 }
1107
1108 void R_Shadow_VertexLightingWithXYAttenuationTexture(int numverts, const float *vertex3f, const float *normal3f, const float *lightcolor, const matrix4x4_t *m)
1109 {
1110         float *color4f = varray_color4f;
1111         float dist, dot, intensity, v[3], n[3];
1112         for (;numverts > 0;numverts--, vertex3f += 3, normal3f += 3, color4f += 4)
1113         {
1114                 Matrix4x4_Transform(m, vertex3f, v);
1115                 if ((dist = fabs(v[2])) < 1)
1116                 {
1117                         Matrix4x4_Transform3x3(m, normal3f, n);
1118                         if ((dot = DotProduct(n, v)) > 0)
1119                         {
1120                                 intensity = pow(1 - dist, r_shadow_attenpower) * r_shadow_attenscale * dot / sqrt(DotProduct(n,n));
1121                                 VectorScale(lightcolor, intensity, color4f);
1122                                 color4f[3] = 1;
1123                         }
1124                         else
1125                         {
1126                                 VectorClear(color4f);
1127                                 color4f[3] = 1;
1128                         }
1129                 }
1130                 else
1131                 {
1132                         VectorClear(color4f);
1133                         color4f[3] = 1;
1134                 }
1135         }
1136 }
1137
1138 // FIXME: this should be done in a vertex program when possible
1139 // FIXME: if vertex program not available, this would really benefit from 3DNow! or SSE
1140 void R_Shadow_Transform_Vertex3f_TexCoord3f(float *tc3f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1141 {
1142         do
1143         {
1144                 tc3f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1145                 tc3f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1146                 tc3f[2] = vertex3f[0] * matrix->m[2][0] + vertex3f[1] * matrix->m[2][1] + vertex3f[2] * matrix->m[2][2] + matrix->m[2][3];
1147                 vertex3f += 3;
1148                 tc3f += 3;
1149         }
1150         while (--numverts);
1151 }
1152
1153 void R_Shadow_Transform_Vertex3f_TexCoord2f(float *tc2f, int numverts, const float *vertex3f, const matrix4x4_t *matrix)
1154 {
1155         do
1156         {
1157                 tc2f[0] = vertex3f[0] * matrix->m[0][0] + vertex3f[1] * matrix->m[0][1] + vertex3f[2] * matrix->m[0][2] + matrix->m[0][3];
1158                 tc2f[1] = vertex3f[0] * matrix->m[1][0] + vertex3f[1] * matrix->m[1][1] + vertex3f[2] * matrix->m[1][2] + matrix->m[1][3];
1159                 vertex3f += 3;
1160                 tc2f += 2;
1161         }
1162         while (--numverts);
1163 }
1164
1165 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)
1166 {
1167         int i;
1168         float lightdir[3];
1169         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1170         {
1171                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1172                 // the cubemap normalizes this for us
1173                 out3f[0] = DotProduct(svector3f, lightdir);
1174                 out3f[1] = DotProduct(tvector3f, lightdir);
1175                 out3f[2] = DotProduct(normal3f, lightdir);
1176         }
1177 }
1178
1179 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)
1180 {
1181         int i;
1182         float lightdir[3], eyedir[3], halfdir[3];
1183         for (i = 0;i < numverts;i++, vertex3f += 3, svector3f += 3, tvector3f += 3, normal3f += 3, out3f += 3)
1184         {
1185                 VectorSubtract(vertex3f, relativelightorigin, lightdir);
1186                 VectorNormalizeFast(lightdir);
1187                 VectorSubtract(vertex3f, relativeeyeorigin, eyedir);
1188                 VectorNormalizeFast(eyedir);
1189                 VectorAdd(lightdir, eyedir, halfdir);
1190                 // the cubemap normalizes this for us
1191                 out3f[0] = DotProduct(svector3f, halfdir);
1192                 out3f[1] = DotProduct(tvector3f, halfdir);
1193                 out3f[2] = DotProduct(normal3f, halfdir);
1194         }
1195 }
1196
1197 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)
1198 {
1199         int renders;
1200         float color[3], color2[3];
1201         rmeshstate_t m;
1202         GL_VertexPointer(vertex3f);
1203         if (gl_dot3arb && gl_texturecubemap && gl_combine.integer && gl_stencil)
1204         {
1205                 if (!bumptexture)
1206                         bumptexture = r_shadow_blankbumptexture;
1207                 GL_Color(1,1,1,1);
1208                 // colorscale accounts for how much we multiply the brightness during combine
1209                 // mult is how many times the final pass of the lighting will be
1210                 // performed to get more brightness than otherwise possible
1211                 // limit mult to 64 for sanity sake
1212                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 4)
1213                 {
1214                         // 3/2 3D combine path (Geforce3, Radeon 8500)
1215                         memset(&m, 0, sizeof(m));
1216                         m.tex[0] = R_GetTexture(bumptexture);
1217                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1218                         m.tex3d[2] = R_GetTexture(r_shadow_attenuation3dtexture);
1219                         m.texcombinergb[0] = GL_REPLACE;
1220                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1221                         m.pointer_texcoord[0] = texcoord2f;
1222                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1223                         m.pointer_texcoord[2] = varray_texcoord3f[2];
1224                         R_Mesh_State_Texture(&m);
1225                         qglColorMask(0,0,0,1);
1226                         GL_BlendFunc(GL_ONE, GL_ZERO);
1227                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1228                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1229                         R_Mesh_Draw(numverts, numtriangles, elements);
1230                         c_rt_lightmeshes++;
1231                         c_rt_lighttris += numtriangles;
1232
1233                         memset(&m, 0, sizeof(m));
1234                         m.tex[0] = R_GetTexture(basetexture);
1235                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1236                         m.pointer_texcoord[0] = texcoord2f;
1237                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1238                         R_Mesh_State_Texture(&m);
1239                         qglColorMask(1,1,1,0);
1240                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1241                         if (lightcubemap)
1242                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1243                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1244                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1245                         {
1246                                 color[0] = bound(0, color2[0], 1);
1247                                 color[1] = bound(0, color2[1], 1);
1248                                 color[2] = bound(0, color2[2], 1);
1249                                 GL_Color(color[0], color[1], color[2], 1);
1250                                 R_Mesh_Draw(numverts, numtriangles, elements);
1251                                 c_rt_lightmeshes++;
1252                                 c_rt_lighttris += numtriangles;
1253                         }
1254                 }
1255                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap)
1256                 {
1257                         // 1/2/2 3D combine path (original Radeon)
1258                         memset(&m, 0, sizeof(m));
1259                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1260                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1261                         R_Mesh_State_Texture(&m);
1262                         qglColorMask(0,0,0,1);
1263                         GL_BlendFunc(GL_ONE, GL_ZERO);
1264                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1265                         R_Mesh_Draw(numverts, numtriangles, elements);
1266                         c_rt_lightmeshes++;
1267                         c_rt_lighttris += numtriangles;
1268
1269                         memset(&m, 0, sizeof(m));
1270                         m.tex[0] = R_GetTexture(bumptexture);
1271                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1272                         m.texcombinergb[0] = GL_REPLACE;
1273                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1274                         m.pointer_texcoord[0] = texcoord2f;
1275                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1276                         R_Mesh_State_Texture(&m);
1277                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1278                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1279                         R_Mesh_Draw(numverts, numtriangles, elements);
1280                         c_rt_lightmeshes++;
1281                         c_rt_lighttris += numtriangles;
1282
1283                         memset(&m, 0, sizeof(m));
1284                         m.tex[0] = R_GetTexture(basetexture);
1285                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1286                         m.pointer_texcoord[0] = texcoord2f;
1287                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1288                         R_Mesh_State_Texture(&m);
1289                         qglColorMask(1,1,1,0);
1290                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1291                         if (lightcubemap)
1292                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1293                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1294                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1295                         {
1296                                 color[0] = bound(0, color2[0], 1);
1297                                 color[1] = bound(0, color2[1], 1);
1298                                 color[2] = bound(0, color2[2], 1);
1299                                 GL_Color(color[0], color[1], color[2], 1);
1300                                 R_Mesh_Draw(numverts, numtriangles, elements);
1301                                 c_rt_lightmeshes++;
1302                                 c_rt_lighttris += numtriangles;
1303                         }
1304                 }
1305                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap)
1306                 {
1307                         // 2/2 3D combine path (original Radeon)
1308                         memset(&m, 0, sizeof(m));
1309                         m.tex[0] = R_GetTexture(bumptexture);
1310                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1311                         m.texcombinergb[0] = GL_REPLACE;
1312                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1313                         m.pointer_texcoord[0] = texcoord2f;
1314                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1315                         R_Mesh_State_Texture(&m);
1316                         qglColorMask(0,0,0,1);
1317                         GL_BlendFunc(GL_ONE, GL_ZERO);
1318                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1319                         R_Mesh_Draw(numverts, numtriangles, elements);
1320                         c_rt_lightmeshes++;
1321                         c_rt_lighttris += numtriangles;
1322
1323                         memset(&m, 0, sizeof(m));
1324                         m.tex[0] = R_GetTexture(basetexture);
1325                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1326                         m.pointer_texcoord[0] = texcoord2f;
1327                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1328                         R_Mesh_State_Texture(&m);
1329                         qglColorMask(1,1,1,0);
1330                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1331                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1332                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1333                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1334                         {
1335                                 color[0] = bound(0, color2[0], 1);
1336                                 color[1] = bound(0, color2[1], 1);
1337                                 color[2] = bound(0, color2[2], 1);
1338                                 GL_Color(color[0], color[1], color[2], 1);
1339                                 R_Mesh_Draw(numverts, numtriangles, elements);
1340                                 c_rt_lightmeshes++;
1341                                 c_rt_lighttris += numtriangles;
1342                         }
1343                 }
1344                 else if (r_textureunits.integer >= 4)
1345                 {
1346                         // 4/2 2D combine path (Geforce3, Radeon 8500)
1347                         memset(&m, 0, sizeof(m));
1348                         m.tex[0] = R_GetTexture(bumptexture);
1349                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1350                         m.texcombinergb[0] = GL_REPLACE;
1351                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1352                         m.tex[2] = R_GetTexture(r_shadow_attenuation2dtexture);
1353                         m.tex[3] = R_GetTexture(r_shadow_attenuation2dtexture);
1354                         m.pointer_texcoord[0] = texcoord2f;
1355                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1356                         m.pointer_texcoord[2] = varray_texcoord2f[2];
1357                         m.pointer_texcoord[3] = varray_texcoord2f[3];
1358                         R_Mesh_State_Texture(&m);
1359                         qglColorMask(0,0,0,1);
1360                         GL_BlendFunc(GL_ONE, GL_ZERO);
1361                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1362                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[2], numverts, vertex3f, matrix_modeltoattenuationxyz);
1363                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[3], numverts, vertex3f, matrix_modeltoattenuationz);
1364                         R_Mesh_Draw(numverts, numtriangles, elements);
1365                         c_rt_lightmeshes++;
1366                         c_rt_lighttris += numtriangles;
1367
1368                         memset(&m, 0, sizeof(m));
1369                         m.tex[0] = R_GetTexture(basetexture);
1370                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1371                         m.pointer_texcoord[0] = texcoord2f;
1372                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1373                         R_Mesh_State_Texture(&m);
1374                         qglColorMask(1,1,1,0);
1375                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1376                         if (lightcubemap)
1377                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1378                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1379                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1380                         {
1381                                 color[0] = bound(0, color2[0], 1);
1382                                 color[1] = bound(0, color2[1], 1);
1383                                 color[2] = bound(0, color2[2], 1);
1384                                 GL_Color(color[0], color[1], color[2], 1);
1385                                 R_Mesh_Draw(numverts, numtriangles, elements);
1386                                 c_rt_lightmeshes++;
1387                                 c_rt_lighttris += numtriangles;
1388                         }
1389                 }
1390                 else
1391                 {
1392                         // 2/2/2 2D combine path (any dot3 card)
1393                         memset(&m, 0, sizeof(m));
1394                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1395                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1396                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1397                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1398                         R_Mesh_State_Texture(&m);
1399                         qglColorMask(0,0,0,1);
1400                         GL_BlendFunc(GL_ONE, GL_ZERO);
1401                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1402                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1403                         R_Mesh_Draw(numverts, numtriangles, elements);
1404                         c_rt_lightmeshes++;
1405                         c_rt_lighttris += numtriangles;
1406
1407                         memset(&m, 0, sizeof(m));
1408                         m.tex[0] = R_GetTexture(bumptexture);
1409                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1410                         m.texcombinergb[0] = GL_REPLACE;
1411                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1412                         m.pointer_texcoord[0] = texcoord2f;
1413                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1414                         R_Mesh_State_Texture(&m);
1415                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1416                         R_Shadow_GenTexCoords_Diffuse_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin);
1417                         R_Mesh_Draw(numverts, numtriangles, elements);
1418                         c_rt_lightmeshes++;
1419                         c_rt_lighttris += numtriangles;
1420
1421                         memset(&m, 0, sizeof(m));
1422                         m.tex[0] = R_GetTexture(basetexture);
1423                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1424                         m.pointer_texcoord[0] = texcoord2f;
1425                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1426                         R_Mesh_State_Texture(&m);
1427                         qglColorMask(1,1,1,0);
1428                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1429                         if (lightcubemap)
1430                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1431                         VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1432                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1433                         {
1434                                 color[0] = bound(0, color2[0], 1);
1435                                 color[1] = bound(0, color2[1], 1);
1436                                 color[2] = bound(0, color2[2], 1);
1437                                 GL_Color(color[0], color[1], color[2], 1);
1438                                 R_Mesh_Draw(numverts, numtriangles, elements);
1439                                 c_rt_lightmeshes++;
1440                                 c_rt_lighttris += numtriangles;
1441                         }
1442                 }
1443         }
1444         else
1445         {
1446                 GL_BlendFunc(GL_SRC_ALPHA, GL_ONE);
1447                 GL_DepthMask(false);
1448                 GL_DepthTest(true);
1449                 GL_ColorPointer(varray_color4f);
1450                 VectorScale(lightcolor, r_shadow_lightintensityscale.value, color2);
1451                 memset(&m, 0, sizeof(m));
1452                 m.tex[0] = R_GetTexture(basetexture);
1453                 m.pointer_texcoord[0] = texcoord2f;
1454                 if (r_textureunits.integer >= 2)
1455                 {
1456                         // voodoo2
1457                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1458                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1459                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1460                 }
1461                 R_Mesh_State_Texture(&m);
1462                 for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1463                 {
1464                         color[0] = bound(0, color2[0], 1);
1465                         color[1] = bound(0, color2[1], 1);
1466                         color[2] = bound(0, color2[2], 1);
1467                         if (r_textureunits.integer >= 2)
1468                                 R_Shadow_VertexLightingWithXYAttenuationTexture(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1469                         else
1470                                 R_Shadow_VertexLighting(numverts, vertex3f, normal3f, color, matrix_modeltofilter);
1471                         R_Mesh_Draw(numverts, numtriangles, elements);
1472                         c_rt_lightmeshes++;
1473                         c_rt_lighttris += numtriangles;
1474                 }
1475         }
1476 }
1477
1478 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)
1479 {
1480         int renders;
1481         float color[3], color2[3], colorscale;
1482         rmeshstate_t m;
1483         if (!gl_dot3arb || !gl_texturecubemap || !gl_combine.integer || !gl_stencil)
1484                 return;
1485         if (!glosstexture)
1486                 glosstexture = r_shadow_blankglosstexture;
1487         if (r_shadow_gloss.integer >= 2 || (r_shadow_gloss.integer >= 1 && glosstexture != r_shadow_blankglosstexture))
1488         {
1489                 colorscale = r_shadow_glossintensity.value;
1490                 if (!bumptexture)
1491                         bumptexture = r_shadow_blankbumptexture;
1492                 if (glosstexture == r_shadow_blankglosstexture)
1493                         colorscale *= r_shadow_gloss2intensity.value;
1494                 GL_VertexPointer(vertex3f);
1495                 GL_Color(1,1,1,1);
1496                 if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1497                 {
1498                         // 2/0/0/1/2 3D combine blendsquare path
1499                         memset(&m, 0, sizeof(m));
1500                         m.tex[0] = R_GetTexture(bumptexture);
1501                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1502                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1503                         m.pointer_texcoord[0] = texcoord2f;
1504                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1505                         R_Mesh_State_Texture(&m);
1506                         qglColorMask(0,0,0,1);
1507                         // this squares the result
1508                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1509                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1510                         R_Mesh_Draw(numverts, numtriangles, elements);
1511                         c_rt_lightmeshes++;
1512                         c_rt_lighttris += numtriangles;
1513
1514                         memset(&m, 0, sizeof(m));
1515                         R_Mesh_State_Texture(&m);
1516                         // square alpha in framebuffer a few times to make it shiny
1517                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1518                         // these comments are a test run through this math for intensity 0.5
1519                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1520                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1521                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1522                         R_Mesh_Draw(numverts, numtriangles, elements);
1523                         c_rt_lightmeshes++;
1524                         c_rt_lighttris += numtriangles;
1525                         R_Mesh_Draw(numverts, numtriangles, elements);
1526                         c_rt_lightmeshes++;
1527                         c_rt_lighttris += numtriangles;
1528
1529                         memset(&m, 0, sizeof(m));
1530                         m.tex3d[0] = R_GetTexture(r_shadow_attenuation3dtexture);
1531                         m.pointer_texcoord[0] = varray_texcoord3f[0];
1532                         R_Mesh_State_Texture(&m);
1533                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1534                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1535                         R_Mesh_Draw(numverts, numtriangles, elements);
1536                         c_rt_lightmeshes++;
1537                         c_rt_lighttris += numtriangles;
1538
1539                         memset(&m, 0, sizeof(m));
1540                         m.tex[0] = R_GetTexture(glosstexture);
1541                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1542                         m.pointer_texcoord[0] = texcoord2f;
1543                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1544                         R_Mesh_State_Texture(&m);
1545                         qglColorMask(1,1,1,0);
1546                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1547                         if (lightcubemap)
1548                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1549                         VectorScale(lightcolor, colorscale, color2);
1550                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1551                         {
1552                                 color[0] = bound(0, color2[0], 1);
1553                                 color[1] = bound(0, color2[1], 1);
1554                                 color[2] = bound(0, color2[2], 1);
1555                                 GL_Color(color[0], color[1], color[2], 1);
1556                                 R_Mesh_Draw(numverts, numtriangles, elements);
1557                                 c_rt_lightmeshes++;
1558                                 c_rt_lighttris += numtriangles;
1559                         }
1560                 }
1561                 else if (r_shadow_texture3d.integer && r_textureunits.integer >= 2 && !lightcubemap /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1562                 {
1563                         // 2/0/0/2 3D combine blendsquare path
1564                         memset(&m, 0, sizeof(m));
1565                         m.tex[0] = R_GetTexture(bumptexture);
1566                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1567                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1568                         m.pointer_texcoord[0] = texcoord2f;
1569                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1570                         R_Mesh_State_Texture(&m);
1571                         qglColorMask(0,0,0,1);
1572                         // this squares the result
1573                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1574                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1575                         R_Mesh_Draw(numverts, numtriangles, elements);
1576                         c_rt_lightmeshes++;
1577                         c_rt_lighttris += numtriangles;
1578
1579                         memset(&m, 0, sizeof(m));
1580                         R_Mesh_State_Texture(&m);
1581                         // square alpha in framebuffer a few times to make it shiny
1582                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1583                         // these comments are a test run through this math for intensity 0.5
1584                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1585                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1586                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1587                         R_Mesh_Draw(numverts, numtriangles, elements);
1588                         c_rt_lightmeshes++;
1589                         c_rt_lighttris += numtriangles;
1590                         R_Mesh_Draw(numverts, numtriangles, elements);
1591                         c_rt_lightmeshes++;
1592                         c_rt_lighttris += numtriangles;
1593
1594                         memset(&m, 0, sizeof(m));
1595                         m.tex[0] = R_GetTexture(glosstexture);
1596                         m.tex3d[1] = R_GetTexture(r_shadow_attenuation3dtexture);
1597                         m.pointer_texcoord[0] = texcoord2f;
1598                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1599                         R_Mesh_State_Texture(&m);
1600                         qglColorMask(1,1,1,0);
1601                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1602                         R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltoattenuationxyz);
1603                         VectorScale(lightcolor, colorscale, color2);
1604                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1605                         {
1606                                 color[0] = bound(0, color2[0], 1);
1607                                 color[1] = bound(0, color2[1], 1);
1608                                 color[2] = bound(0, color2[2], 1);
1609                                 GL_Color(color[0], color[1], color[2], 1);
1610                                 R_Mesh_Draw(numverts, numtriangles, elements);
1611                                 c_rt_lightmeshes++;
1612                                 c_rt_lighttris += numtriangles;
1613                         }
1614                 }
1615                 else if (r_textureunits.integer >= 2 /*&& gl_support_blendsquare*/) // FIXME: detect blendsquare!
1616                 {
1617                         // 2/0/0/2/2 2D combine blendsquare path
1618                         memset(&m, 0, sizeof(m));
1619                         m.tex[0] = R_GetTexture(bumptexture);
1620                         m.texcubemap[1] = R_GetTexture(r_shadow_normalcubetexture);
1621                         m.texcombinergb[1] = GL_DOT3_RGBA_ARB;
1622                         m.pointer_texcoord[0] = texcoord2f;
1623                         m.pointer_texcoord[1] = varray_texcoord3f[1];
1624                         R_Mesh_State_Texture(&m);
1625                         qglColorMask(0,0,0,1);
1626                         // this squares the result
1627                         GL_BlendFunc(GL_SRC_ALPHA, GL_ZERO);
1628                         R_Shadow_GenTexCoords_Specular_NormalCubeMap(varray_texcoord3f[1], numverts, vertex3f, svector3f, tvector3f, normal3f, relativelightorigin, relativeeyeorigin);
1629                         R_Mesh_Draw(numverts, numtriangles, elements);
1630                         c_rt_lightmeshes++;
1631                         c_rt_lighttris += numtriangles;
1632
1633                         memset(&m, 0, sizeof(m));
1634                         R_Mesh_State_Texture(&m);
1635                         // square alpha in framebuffer a few times to make it shiny
1636                         GL_BlendFunc(GL_ZERO, GL_DST_ALPHA);
1637                         // these comments are a test run through this math for intensity 0.5
1638                         // 0.5 * 0.5 = 0.25 (done by the BlendFunc earlier)
1639                         // 0.25 * 0.25 = 0.0625 (this is another pass)
1640                         // 0.0625 * 0.0625 = 0.00390625 (this is another pass)
1641                         R_Mesh_Draw(numverts, numtriangles, elements);
1642                         c_rt_lightmeshes++;
1643                         c_rt_lighttris += numtriangles;
1644                         R_Mesh_Draw(numverts, numtriangles, elements);
1645                         c_rt_lightmeshes++;
1646                         c_rt_lighttris += numtriangles;
1647
1648                         memset(&m, 0, sizeof(m));
1649                         m.tex[0] = R_GetTexture(r_shadow_attenuation2dtexture);
1650                         m.tex[1] = R_GetTexture(r_shadow_attenuation2dtexture);
1651                         m.pointer_texcoord[0] = varray_texcoord2f[0];
1652                         m.pointer_texcoord[1] = varray_texcoord2f[1];
1653                         R_Mesh_State_Texture(&m);
1654                         GL_BlendFunc(GL_DST_ALPHA, GL_ZERO);
1655                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[0], numverts, vertex3f, matrix_modeltoattenuationxyz);
1656                         R_Shadow_Transform_Vertex3f_TexCoord2f(varray_texcoord2f[1], numverts, vertex3f, matrix_modeltoattenuationz);
1657                         R_Mesh_Draw(numverts, numtriangles, elements);
1658                         c_rt_lightmeshes++;
1659                         c_rt_lighttris += numtriangles;
1660
1661                         memset(&m, 0, sizeof(m));
1662                         m.tex[0] = R_GetTexture(glosstexture);
1663                         m.texcubemap[1] = R_GetTexture(lightcubemap);
1664                         m.pointer_texcoord[0] = texcoord2f;
1665                         m.pointer_texcoord[1] = lightcubemap ? varray_texcoord3f[1] : NULL;
1666                         R_Mesh_State_Texture(&m);
1667                         qglColorMask(1,1,1,0);
1668                         GL_BlendFunc(GL_DST_ALPHA, GL_ONE);
1669                         if (lightcubemap)
1670                                 R_Shadow_Transform_Vertex3f_TexCoord3f(varray_texcoord3f[1], numverts, vertex3f, matrix_modeltofilter);
1671                         VectorScale(lightcolor, colorscale, color2);
1672                         for (renders = 0;renders < 64 && (color2[0] > 0 || color2[1] > 0 || color2[2] > 0);renders++, color2[0]--, color2[1]--, color2[2]--)
1673                         {
1674                                 color[0] = bound(0, color2[0], 1);
1675                                 color[1] = bound(0, color2[1], 1);
1676                                 color[2] = bound(0, color2[2], 1);
1677                                 GL_Color(color[0], color[1], color[2], 1);
1678                                 R_Mesh_Draw(numverts, numtriangles, elements);
1679                                 c_rt_lightmeshes++;
1680                                 c_rt_lighttris += numtriangles;
1681                         }
1682                 }
1683         }
1684 }
1685
1686 void R_Shadow_DrawWorldLightShadowVolume(matrix4x4_t *matrix, worldlight_t *light)
1687 {
1688         R_Mesh_Matrix(matrix);
1689         R_Shadow_RenderShadowMeshVolume(light->shadowvolume);
1690 }
1691
1692 cvar_t r_editlights = {0, "r_editlights", "0"};
1693 cvar_t r_editlights_cursordistance = {0, "r_editlights_distance", "1024"};
1694 cvar_t r_editlights_cursorpushback = {0, "r_editlights_pushback", "0"};
1695 cvar_t r_editlights_cursorpushoff = {0, "r_editlights_pushoff", "4"};
1696 cvar_t r_editlights_cursorgrid = {0, "r_editlights_grid", "4"};
1697 cvar_t r_editlights_quakelightsizescale = {CVAR_SAVE, "r_editlights_quakelightsizescale", "0.8"};
1698 cvar_t r_editlights_rtlightssizescale = {CVAR_SAVE, "r_editlights_rtlightssizescale", "0.7"};
1699 cvar_t r_editlights_rtlightscolorscale = {CVAR_SAVE, "r_editlights_rtlightscolorscale", "2"};
1700 worldlight_t *r_shadow_worldlightchain;
1701 worldlight_t *r_shadow_selectedlight;
1702 vec3_t r_editlights_cursorlocation;
1703
1704 static int lightpvsbytes;
1705 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1706
1707 static int castshadowcount = 1;
1708 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1709 {
1710         int i, j, k, l, maxverts = 256, *mark, tris, numsurfaces;
1711         float *vertex3f = NULL, mins[3], maxs[3];
1712         worldlight_t *e;
1713         shadowmesh_t *mesh, *castmesh;
1714         mleaf_t *leaf;
1715         msurface_t *surf;
1716
1717         if (radius < 15 || DotProduct(color, color) < 0.03)
1718         {
1719                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1720                 return;
1721         }
1722
1723         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1724         VectorCopy(origin, e->origin);
1725         VectorCopy(color, e->light);
1726         e->lightradius = radius;
1727         e->style = style;
1728         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1729         {
1730                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1731                 e->style = 0;
1732         }
1733         e->castshadows = castshadow;
1734
1735         e->cullradius = e->lightradius;
1736         for (k = 0;k < 3;k++)
1737         {
1738                 mins[k] = e->origin[k] - e->lightradius;
1739                 maxs[k] = e->origin[k] + e->lightradius;
1740         }
1741
1742         e->next = r_shadow_worldlightchain;
1743         r_shadow_worldlightchain = e;
1744         if (cubemapname && cubemapname[0])
1745         {
1746                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1747                 strcpy(e->cubemapname, cubemapname);
1748                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1749         }
1750         if (cl.worldmodel)
1751         {
1752                 castshadowcount++;
1753                 VectorCopy(e->origin, e->mins);
1754                 VectorCopy(e->origin, e->maxs);
1755                 i = CL_PointQ1Contents(e->origin);
1756                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1757                 {
1758                         //qbyte *byteleafpvs;
1759                         qbyte *bytesurfacepvs;
1760
1761                         //byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1762                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1763
1764                         Portal_Visibility(cl.worldmodel, e->origin, NULL/*byteleafpvs*/, bytesurfacepvs, NULL, 0, true, mins, maxs, e->mins, e->maxs);
1765
1766                         /*
1767                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1768                         {
1769                                 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1770                                 {
1771                                         for (k = 0;k < 3;k++)
1772                                         {
1773                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1774                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1775                                         }
1776                                 }
1777                         }
1778                         */
1779
1780                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1781                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1782                                         surf->castshadow = castshadowcount;
1783
1784                         //Mem_Free(byteleafpvs);
1785                         Mem_Free(bytesurfacepvs);
1786                 }
1787                 else
1788                 {
1789                         lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1790                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.visleafs;i++, leaf++)
1791                         {
1792                                 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, mins, maxs))
1793                                 {
1794                                         for (k = 0;k < 3;k++)
1795                                         {
1796                                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1797                                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1798                                         }
1799                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1800                                         {
1801                                                 surf = cl.worldmodel->brushq1.surfaces + *mark;
1802                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, mins, maxs))
1803                                                         surf->castshadow = castshadowcount;
1804                                         }
1805                                 }
1806                         }
1807                 }
1808
1809                 for (k = 0;k < 3;k++)
1810                 {
1811                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1812                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1813                 }
1814                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1815
1816                 numsurfaces = 0;
1817                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1818                         if (surf->castshadow == castshadowcount)
1819                                 numsurfaces++;
1820                 if (numsurfaces)
1821                         e->surfaces = Mem_Alloc(r_shadow_mempool, numsurfaces * sizeof(msurface_t *));
1822                 e->numsurfaces = 0;
1823                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1824                         if (surf->castshadow == castshadowcount)
1825                                 e->surfaces[e->numsurfaces++] = surf;
1826
1827                 if (e->castshadows)
1828                 {
1829                         castshadowcount++;
1830                         for (j = 0;j < e->numsurfaces;j++)
1831                         {
1832                                 surf = e->surfaces[j];
1833                                 if (surf->flags & SURF_SHADOWCAST)
1834                                 {
1835                                         surf->castshadow = castshadowcount;
1836                                         if (maxverts < surf->poly_numverts)
1837                                                 maxverts = surf->poly_numverts;
1838                                 }
1839                         }
1840                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1841                         // make a mesh to cast a shadow volume from
1842                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1843                         for (j = 0;j < e->numsurfaces;j++)
1844                                 if ((surf = e->surfaces[j])->castshadow == castshadowcount)
1845                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surf->mesh.data_vertex3f, surf->mesh.num_triangles, surf->mesh.data_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, sizeof (name));
1987         strlcat (name, ".rtlights", sizeof (name));
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, sizeof (name));
2044         strlcat (name, ".rtlights", sizeof (name));
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, sizeof (name));
2085         strlcat (name, ".lights", sizeof (name));
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 }