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