]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - r_shadow.c
3995057d949958aa19ec6da7014c79843b05f7b1
[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 lightpvsbytes;
1845 static qbyte lightpvs[(MAX_MAP_LEAFS + 7)/ 8];
1846
1847 static int castshadowcount = 1;
1848 void R_Shadow_NewWorldLight(vec3_t origin, float radius, vec3_t color, int style, const char *cubemapname, int castshadow)
1849 {
1850         int i, j, k, l, maxverts = 256, *mark, tris;
1851         float *vertex3f = NULL;
1852         worldlight_t *e;
1853         shadowmesh_t *mesh, *castmesh;
1854         mleaf_t *leaf;
1855         msurface_t *surf;
1856         surfmesh_t *surfmesh;
1857
1858         if (radius < 15 || DotProduct(color, color) < 0.03)
1859         {
1860                 Con_Printf("R_Shadow_NewWorldLight: refusing to create a light too small/dim\n");
1861                 return;
1862         }
1863
1864         e = Mem_Alloc(r_shadow_mempool, sizeof(worldlight_t));
1865         VectorCopy(origin, e->origin);
1866         VectorCopy(color, e->light);
1867         e->lightradius = radius;
1868         e->style = style;
1869         if (e->style < 0 || e->style >= MAX_LIGHTSTYLES)
1870         {
1871                 Con_Printf("R_Shadow_NewWorldLight: invalid light style number %i, must be >= 0 and < %i\n", e->style, MAX_LIGHTSTYLES);
1872                 e->style = 0;
1873         }
1874         e->castshadows = castshadow;
1875
1876         e->cullradius = e->lightradius;
1877         for (k = 0;k < 3;k++)
1878         {
1879                 e->mins[k] = e->origin[k] - e->lightradius;
1880                 e->maxs[k] = e->origin[k] + e->lightradius;
1881         }
1882
1883         e->next = r_shadow_worldlightchain;
1884         r_shadow_worldlightchain = e;
1885         if (cubemapname && cubemapname[0])
1886         {
1887                 e->cubemapname = Mem_Alloc(r_shadow_mempool, strlen(cubemapname) + 1);
1888                 strcpy(e->cubemapname, cubemapname);
1889                 // FIXME: add cubemap loading (and don't load a cubemap twice)
1890         }
1891         if (cl.worldmodel)
1892         {
1893                 castshadowcount++;
1894                 i = CL_PointContents(e->origin);
1895                 if (r_shadow_portallight.integer && i != CONTENTS_SOLID && i != CONTENTS_SKY)
1896                 {
1897                         qbyte *byteleafpvs;
1898                         qbyte *bytesurfacepvs;
1899
1900                         byteleafpvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numleafs);
1901                         bytesurfacepvs = Mem_Alloc(tempmempool, cl.worldmodel->brushq1.numsurfaces);
1902
1903                         Portal_Visibility(cl.worldmodel, e->origin, byteleafpvs, bytesurfacepvs, NULL, 0, true, RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin));
1904
1905                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1906                                 if (byteleafpvs[i] && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1907                                         leaf->worldnodeframe = castshadowcount;
1908
1909                         for (i = 0, surf = cl.worldmodel->brushq1.surfaces;i < cl.worldmodel->brushq1.numsurfaces;i++, surf++)
1910                                 if (bytesurfacepvs[i] && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1911                                         surf->castshadow = castshadowcount;
1912
1913                         Mem_Free(byteleafpvs);
1914                         Mem_Free(bytesurfacepvs);
1915                 }
1916                 else
1917                 {
1918                         lightpvsbytes = cl.worldmodel->brush.FatPVS(cl.worldmodel, origin, 0, lightpvs, sizeof(lightpvs));
1919                         for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs - 1;i++, leaf++)
1920                         {
1921                                 if (lightpvs[i >> 3] & (1 << (i & 7)) && BoxesOverlap(leaf->mins, leaf->maxs, e->mins, e->maxs))
1922                                 {
1923                                         leaf->worldnodeframe = castshadowcount;
1924                                         for (j = 0, mark = leaf->firstmarksurface;j < leaf->nummarksurfaces;j++, mark++)
1925                                         {
1926                                                 surf = cl.worldmodel->brushq1.surfaces + *mark;
1927                                                 if (surf->castshadow != castshadowcount && BoxesOverlap(surf->poly_mins, surf->poly_maxs, e->mins, e->maxs))
1928                                                         surf->castshadow = castshadowcount;
1929                                         }
1930                                 }
1931                         }
1932                 }
1933
1934                 e->numleafs = 0;
1935                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1936                         if (leaf->worldnodeframe == castshadowcount)
1937                                 e->numleafs++;
1938                 e->numsurfaces = 0;
1939                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1940                         if (surf->castshadow == castshadowcount)
1941                                 e->numsurfaces++;
1942
1943                 if (e->numleafs)
1944                         e->leafs = Mem_Alloc(r_shadow_mempool, e->numleafs * sizeof(mleaf_t *));
1945                 if (e->numsurfaces)
1946                         e->surfaces = Mem_Alloc(r_shadow_mempool, e->numsurfaces * sizeof(msurface_t *));
1947                 e->numleafs = 0;
1948                 for (i = 0, leaf = cl.worldmodel->brushq1.leafs + 1;i < cl.worldmodel->brushq1.numleafs;i++, leaf++)
1949                         if (leaf->worldnodeframe == castshadowcount)
1950                                 e->leafs[e->numleafs++] = leaf;
1951                 e->numsurfaces = 0;
1952                 for (i = 0, surf = cl.worldmodel->brushq1.surfaces + cl.worldmodel->brushq1.firstmodelsurface;i < cl.worldmodel->brushq1.nummodelsurfaces;i++, surf++)
1953                         if (surf->castshadow == castshadowcount)
1954                                 e->surfaces[e->numsurfaces++] = surf;
1955
1956                 // find bounding box of lit leafs
1957                 VectorCopy(e->origin, e->mins);
1958                 VectorCopy(e->origin, e->maxs);
1959                 for (j = 0;j < e->numleafs;j++)
1960                 {
1961                         leaf = e->leafs[j];
1962                         for (k = 0;k < 3;k++)
1963                         {
1964                                 if (e->mins[k] > leaf->mins[k]) e->mins[k] = leaf->mins[k];
1965                                 if (e->maxs[k] < leaf->maxs[k]) e->maxs[k] = leaf->maxs[k];
1966                         }
1967                 }
1968
1969                 for (k = 0;k < 3;k++)
1970                 {
1971                         if (e->mins[k] < e->origin[k] - e->lightradius) e->mins[k] = e->origin[k] - e->lightradius;
1972                         if (e->maxs[k] > e->origin[k] + e->lightradius) e->maxs[k] = e->origin[k] + e->lightradius;
1973                 }
1974                 e->cullradius = RadiusFromBoundsAndOrigin(e->mins, e->maxs, e->origin);
1975
1976                 if (e->castshadows)
1977                 {
1978                         castshadowcount++;
1979                         for (j = 0;j < e->numsurfaces;j++)
1980                         {
1981                                 surf = e->surfaces[j];
1982                                 if (surf->flags & SURF_SHADOWCAST)
1983                                 {
1984                                         surf->castshadow = castshadowcount;
1985                                         if (maxverts < surf->poly_numverts)
1986                                                 maxverts = surf->poly_numverts;
1987                                 }
1988                         }
1989                         e->shadowvolume = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1990                         // make a mesh to cast a shadow volume from
1991                         castmesh = Mod_ShadowMesh_Begin(r_shadow_mempool, 32768);
1992                         for (j = 0;j < e->numsurfaces;j++)
1993                                 if (e->surfaces[j]->castshadow == castshadowcount)
1994                                         for (surfmesh = e->surfaces[j]->mesh;surfmesh;surfmesh = surfmesh->chain)
1995                                                 Mod_ShadowMesh_AddMesh(r_shadow_mempool, castmesh, surfmesh->vertex3f, surfmesh->numtriangles, surfmesh->element3i);
1996                         castmesh = Mod_ShadowMesh_Finish(r_shadow_mempool, castmesh);
1997
1998                         // cast shadow volume from castmesh
1999                         for (mesh = castmesh;mesh;mesh = mesh->next)
2000                         {
2001                                 R_Shadow_ResizeShadowElements(castmesh->numtriangles);
2002
2003                                 if (maxverts < castmesh->numverts * 2)
2004                                 {
2005                                         maxverts = castmesh->numverts * 2;
2006                                         if (vertex3f)
2007                                                 Mem_Free(vertex3f);
2008                                         vertex3f = NULL;
2009                                 }
2010                                 if (vertex3f == NULL && maxverts > 0)
2011                                         vertex3f = Mem_Alloc(r_shadow_mempool, maxverts * sizeof(float[3]));
2012
2013                                 // now that we have the buffers big enough, construct and add
2014                                 // the shadow volume mesh
2015                                 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)))
2016                                         Mod_ShadowMesh_AddMesh(r_shadow_mempool, e->shadowvolume, vertex3f, tris, shadowelements);
2017                         }
2018                         if (vertex3f)
2019                                 Mem_Free(vertex3f);
2020                         vertex3f = NULL;
2021                         // we're done with castmesh now
2022                         Mod_ShadowMesh_Free(castmesh);
2023                         e->shadowvolume = Mod_ShadowMesh_Finish(r_shadow_mempool, e->shadowvolume);
2024                         for (l = 0, mesh = e->shadowvolume;mesh;mesh = mesh->next)
2025                                 l += mesh->numtriangles;
2026                         Con_Printf("static shadow volume built containing %i triangles\n", l);
2027                 }
2028         }
2029         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);
2030 }
2031
2032 void R_Shadow_FreeWorldLight(worldlight_t *light)
2033 {
2034         worldlight_t **lightpointer;
2035         for (lightpointer = &r_shadow_worldlightchain;*lightpointer && *lightpointer != light;lightpointer = &(*lightpointer)->next);
2036         if (*lightpointer != light)
2037                 Sys_Error("R_Shadow_FreeWorldLight: light not linked into chain\n");
2038         *lightpointer = light->next;
2039         if (light->cubemapname)
2040                 Mem_Free(light->cubemapname);
2041         if (light->shadowvolume)
2042                 Mod_ShadowMesh_Free(light->shadowvolume);
2043         if (light->surfaces)
2044                 Mem_Free(light->surfaces);
2045         if (light->leafs)
2046                 Mem_Free(light->leafs);
2047         Mem_Free(light);
2048 }
2049
2050 void R_Shadow_ClearWorldLights(void)
2051 {
2052         while (r_shadow_worldlightchain)
2053                 R_Shadow_FreeWorldLight(r_shadow_worldlightchain);
2054         r_shadow_selectedlight = NULL;
2055 }
2056
2057 void R_Shadow_SelectLight(worldlight_t *light)
2058 {
2059         if (r_shadow_selectedlight)
2060                 r_shadow_selectedlight->selected = false;
2061         r_shadow_selectedlight = light;
2062         if (r_shadow_selectedlight)
2063                 r_shadow_selectedlight->selected = true;
2064 }
2065
2066 rtexture_t *lighttextures[5];
2067
2068 void R_Shadow_DrawCursorCallback(const void *calldata1, int calldata2)
2069 {
2070         float scale = r_editlights_cursorgrid.value * 0.5f;
2071         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[0], false, r_editlights_cursorlocation, vright, vup, scale, -scale, -scale, scale, 1, 1, 1, 0.5f);
2072 }
2073
2074 void R_Shadow_DrawLightSpriteCallback(const void *calldata1, int calldata2)
2075 {
2076         float intensity;
2077         const worldlight_t *light;
2078         light = calldata1;
2079         intensity = 0.5;
2080         if (light->selected)
2081                 intensity = 0.75 + 0.25 * sin(realtime * M_PI * 4.0);
2082         if (!light->shadowvolume)
2083                 intensity *= 0.5f;
2084         R_DrawSprite(GL_SRC_ALPHA, GL_ONE, lighttextures[calldata2], false, light->origin, vright, vup, 8, -8, -8, 8, intensity, intensity, intensity, 0.5);
2085 }
2086
2087 void R_Shadow_DrawLightSprites(void)
2088 {
2089         int i;
2090         cachepic_t *pic;
2091         worldlight_t *light;
2092
2093         for (i = 0;i < 5;i++)
2094         {
2095                 lighttextures[i] = NULL;
2096                 if ((pic = Draw_CachePic(va("gfx/crosshair%i.tga", i + 1))))
2097                         lighttextures[i] = pic->tex;
2098         }
2099
2100         for (light = r_shadow_worldlightchain;light;light = light->next)
2101                 R_MeshQueue_AddTransparent(light->origin, R_Shadow_DrawLightSpriteCallback, light, ((int) light) % 5);
2102         R_MeshQueue_AddTransparent(r_editlights_cursorlocation, R_Shadow_DrawCursorCallback, NULL, 0);
2103 }
2104
2105 void R_Shadow_SelectLightInView(void)
2106 {
2107         float bestrating, rating, temp[3];
2108         worldlight_t *best, *light;
2109         best = NULL;
2110         bestrating = 0;
2111         for (light = r_shadow_worldlightchain;light;light = light->next)
2112         {
2113                 VectorSubtract(light->origin, r_refdef.vieworg, temp);
2114                 rating = (DotProduct(temp, vpn) / sqrt(DotProduct(temp, temp)));
2115                 if (rating >= 0.95)
2116                 {
2117                         rating /= (1 + 0.0625f * sqrt(DotProduct(temp, temp)));
2118                         if (bestrating < rating && CL_TraceLine(light->origin, r_refdef.vieworg, NULL, NULL, 0, true, NULL) == 1.0f)
2119                         {
2120                                 bestrating = rating;
2121                                 best = light;
2122                         }
2123                 }
2124         }
2125         R_Shadow_SelectLight(best);
2126 }
2127
2128 void R_Shadow_LoadWorldLights(void)
2129 {
2130         int n, a, style, shadow;
2131         char name[MAX_QPATH], cubemapname[MAX_QPATH], *lightsstring, *s, *t;
2132         float origin[3], radius, color[3];
2133         if (cl.worldmodel == NULL)
2134         {
2135                 Con_Printf("No map loaded.\n");
2136                 return;
2137         }
2138         FS_StripExtension(cl.worldmodel->name, name);
2139         strcat(name, ".rtlights");
2140         lightsstring = FS_LoadFile(name, false);
2141         if (lightsstring)
2142         {
2143                 s = lightsstring;
2144                 n = 0;
2145                 while (*s)
2146                 {
2147                         t = s;
2148                         while (*s && *s != '\n')
2149                                 s++;
2150                         if (!*s)
2151                                 break;
2152                         *s = 0;
2153                         shadow = true;
2154                         // check for modifier flags
2155                         if (*t == '!')
2156                         {
2157                                 shadow = false;
2158                                 t++;
2159                         }
2160                         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);
2161                         if (a < 9)
2162                                 cubemapname[0] = 0;
2163                         *s = '\n';
2164                         if (a < 8)
2165                         {
2166                                 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);
2167                                 break;
2168                         }
2169                         VectorScale(color, r_editlights_rtlightscolorscale.value, color);
2170                         radius *= r_editlights_rtlightssizescale.value;
2171                         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadow);
2172                         s++;
2173                         n++;
2174                 }
2175                 if (*s)
2176                         Con_Printf("invalid rtlights file \"%s\"\n", name);
2177                 Mem_Free(lightsstring);
2178         }
2179 }
2180
2181 void R_Shadow_SaveWorldLights(void)
2182 {
2183         worldlight_t *light;
2184         int bufchars, bufmaxchars;
2185         char *buf, *oldbuf;
2186         char name[MAX_QPATH];
2187         char line[1024];
2188         if (!r_shadow_worldlightchain)
2189                 return;
2190         if (cl.worldmodel == NULL)
2191         {
2192                 Con_Printf("No map loaded.\n");
2193                 return;
2194         }
2195         FS_StripExtension(cl.worldmodel->name, name);
2196         strcat(name, ".rtlights");
2197         bufchars = bufmaxchars = 0;
2198         buf = NULL;
2199         for (light = r_shadow_worldlightchain;light;light = light->next)
2200         {
2201                 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 : "");
2202                 if (bufchars + (int) strlen(line) > bufmaxchars)
2203                 {
2204                         bufmaxchars = bufchars + strlen(line) + 2048;
2205                         oldbuf = buf;
2206                         buf = Mem_Alloc(r_shadow_mempool, bufmaxchars);
2207                         if (oldbuf)
2208                         {
2209                                 if (bufchars)
2210                                         memcpy(buf, oldbuf, bufchars);
2211                                 Mem_Free(oldbuf);
2212                         }
2213                 }
2214                 if (strlen(line))
2215                 {
2216                         memcpy(buf + bufchars, line, strlen(line));
2217                         bufchars += strlen(line);
2218                 }
2219         }
2220         if (bufchars)
2221                 FS_WriteFile(name, buf, bufchars);
2222         if (buf)
2223                 Mem_Free(buf);
2224 }
2225
2226 void R_Shadow_LoadLightsFile(void)
2227 {
2228         int n, a, style;
2229         char name[MAX_QPATH], *lightsstring, *s, *t;
2230         float origin[3], radius, color[3], subtract, spotdir[3], spotcone, falloff, distbias;
2231         if (cl.worldmodel == NULL)
2232         {
2233                 Con_Printf("No map loaded.\n");
2234                 return;
2235         }
2236         FS_StripExtension(cl.worldmodel->name, name);
2237         strcat(name, ".lights");
2238         lightsstring = FS_LoadFile(name, false);
2239         if (lightsstring)
2240         {
2241                 s = lightsstring;
2242                 n = 0;
2243                 while (*s)
2244                 {
2245                         t = s;
2246                         while (*s && *s != '\n')
2247                                 s++;
2248                         if (!*s)
2249                                 break;
2250                         *s = 0;
2251                         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);
2252                         *s = '\n';
2253                         if (a < 14)
2254                         {
2255                                 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);
2256                                 break;
2257                         }
2258                         radius = sqrt(DotProduct(color, color) / (falloff * falloff * 8192.0f * 8192.0f));
2259                         radius = bound(15, radius, 4096);
2260                         VectorScale(color, (2.0f / (8388608.0f)), color);
2261                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2262                         s++;
2263                         n++;
2264                 }
2265                 if (*s)
2266                         Con_Printf("invalid lights file \"%s\"\n", name);
2267                 Mem_Free(lightsstring);
2268         }
2269 }
2270
2271 void R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite(void)
2272 {
2273         int entnum, style, islight;
2274         char key[256], value[1024];
2275         float origin[3], radius, color[3], light, scale, originhack[3], overridecolor[3];
2276         const char *data;
2277
2278         if (cl.worldmodel == NULL)
2279         {
2280                 Con_Printf("No map loaded.\n");
2281                 return;
2282         }
2283         data = cl.worldmodel->brush.entities;
2284         if (!data)
2285                 return;
2286         for (entnum = 0;COM_ParseToken(&data, false) && com_token[0] == '{';entnum++)
2287         {
2288                 light = 0;
2289                 origin[0] = origin[1] = origin[2] = 0;
2290                 originhack[0] = originhack[1] = originhack[2] = 0;
2291                 color[0] = color[1] = color[2] = 1;
2292                 overridecolor[0] = overridecolor[1] = overridecolor[2] = 1;
2293                 scale = 1;
2294                 style = 0;
2295                 islight = false;
2296                 while (1)
2297                 {
2298                         if (!COM_ParseToken(&data, false))
2299                                 break; // error
2300                         if (com_token[0] == '}')
2301                                 break; // end of entity
2302                         if (com_token[0] == '_')
2303                                 strcpy(key, com_token + 1);
2304                         else
2305                                 strcpy(key, com_token);
2306                         while (key[strlen(key)-1] == ' ') // remove trailing spaces
2307                                 key[strlen(key)-1] = 0;
2308                         if (!COM_ParseToken(&data, false))
2309                                 break; // error
2310                         strcpy(value, com_token);
2311
2312                         // now that we have the key pair worked out...
2313                         if (!strcmp("light", key))
2314                                 light = atof(value);
2315                         else if (!strcmp("origin", key))
2316                                 sscanf(value, "%f %f %f", &origin[0], &origin[1], &origin[2]);
2317                         else if (!strcmp("color", key))
2318                                 sscanf(value, "%f %f %f", &color[0], &color[1], &color[2]);
2319                         else if (!strcmp("wait", key))
2320                                 scale = atof(value);
2321                         else if (!strcmp("classname", key))
2322                         {
2323                                 if (!strncmp(value, "light", 5))
2324                                 {
2325                                         islight = true;
2326                                         if (!strcmp(value, "light_fluoro"))
2327                                         {
2328                                                 originhack[0] = 0;
2329                                                 originhack[1] = 0;
2330                                                 originhack[2] = 0;
2331                                                 overridecolor[0] = 1;
2332                                                 overridecolor[1] = 1;
2333                                                 overridecolor[2] = 1;
2334                                         }
2335                                         if (!strcmp(value, "light_fluorospark"))
2336                                         {
2337                                                 originhack[0] = 0;
2338                                                 originhack[1] = 0;
2339                                                 originhack[2] = 0;
2340                                                 overridecolor[0] = 1;
2341                                                 overridecolor[1] = 1;
2342                                                 overridecolor[2] = 1;
2343                                         }
2344                                         if (!strcmp(value, "light_globe"))
2345                                         {
2346                                                 originhack[0] = 0;
2347                                                 originhack[1] = 0;
2348                                                 originhack[2] = 0;
2349                                                 overridecolor[0] = 1;
2350                                                 overridecolor[1] = 0.8;
2351                                                 overridecolor[2] = 0.4;
2352                                         }
2353                                         if (!strcmp(value, "light_flame_large_yellow"))
2354                                         {
2355                                                 originhack[0] = 0;
2356                                                 originhack[1] = 0;
2357                                                 originhack[2] = 48;
2358                                                 overridecolor[0] = 1;
2359                                                 overridecolor[1] = 0.5;
2360                                                 overridecolor[2] = 0.1;
2361                                         }
2362                                         if (!strcmp(value, "light_flame_small_yellow"))
2363                                         {
2364                                                 originhack[0] = 0;
2365                                                 originhack[1] = 0;
2366                                                 originhack[2] = 40;
2367                                                 overridecolor[0] = 1;
2368                                                 overridecolor[1] = 0.5;
2369                                                 overridecolor[2] = 0.1;
2370                                         }
2371                                         if (!strcmp(value, "light_torch_small_white"))
2372                                         {
2373                                                 originhack[0] = 0;
2374                                                 originhack[1] = 0;
2375                                                 originhack[2] = 40;
2376                                                 overridecolor[0] = 1;
2377                                                 overridecolor[1] = 0.5;
2378                                                 overridecolor[2] = 0.1;
2379                                         }
2380                                         if (!strcmp(value, "light_torch_small_walltorch"))
2381                                         {
2382                                                 originhack[0] = 0;
2383                                                 originhack[1] = 0;
2384                                                 originhack[2] = 40;
2385                                                 overridecolor[0] = 1;
2386                                                 overridecolor[1] = 0.5;
2387                                                 overridecolor[2] = 0.1;
2388                                         }
2389                                 }
2390                         }
2391                         else if (!strcmp("style", key))
2392                                 style = atoi(value);
2393                 }
2394                 if (light <= 0 && islight)
2395                         light = 300;
2396                 radius = min(light * r_editlights_quakelightsizescale.value / scale, 1048576);
2397                 light = sqrt(bound(0, light, 1048576)) * (1.0f / 16.0f);
2398                 if (color[0] == 1 && color[1] == 1 && color[2] == 1)
2399                         VectorCopy(overridecolor, color);
2400                 VectorScale(color, light, color);
2401                 VectorAdd(origin, originhack, origin);
2402                 if (radius >= 15)
2403                         R_Shadow_NewWorldLight(origin, radius, color, style, NULL, true);
2404         }
2405 }
2406
2407
2408 void R_Shadow_SetCursorLocationForView(void)
2409 {
2410         vec_t dist, push, frac;
2411         vec3_t dest, endpos, normal;
2412         VectorMA(r_refdef.vieworg, r_editlights_cursordistance.value, vpn, dest);
2413         frac = CL_TraceLine(r_refdef.vieworg, dest, endpos, normal, 0, true, NULL);
2414         if (frac < 1)
2415         {
2416                 dist = frac * r_editlights_cursordistance.value;
2417                 push = r_editlights_cursorpushback.value;
2418                 if (push > dist)
2419                         push = dist;
2420                 push = -push;
2421                 VectorMA(endpos, push, vpn, endpos);
2422                 VectorMA(endpos, r_editlights_cursorpushoff.value, normal, endpos);
2423         }
2424         r_editlights_cursorlocation[0] = floor(endpos[0] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2425         r_editlights_cursorlocation[1] = floor(endpos[1] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2426         r_editlights_cursorlocation[2] = floor(endpos[2] / r_editlights_cursorgrid.value + 0.5f) * r_editlights_cursorgrid.value;
2427 }
2428
2429 void R_Shadow_UpdateWorldLightSelection(void)
2430 {
2431         R_Shadow_SetCursorLocationForView();
2432         if (r_editlights.integer)
2433         {
2434                 R_Shadow_SelectLightInView();
2435                 R_Shadow_DrawLightSprites();
2436         }
2437         else
2438                 R_Shadow_SelectLight(NULL);
2439 }
2440
2441 void R_Shadow_EditLights_Clear_f(void)
2442 {
2443         R_Shadow_ClearWorldLights();
2444 }
2445
2446 void R_Shadow_EditLights_Reload_f(void)
2447 {
2448         r_shadow_reloadlights = true;
2449 }
2450
2451 void R_Shadow_EditLights_Save_f(void)
2452 {
2453         if (cl.worldmodel)
2454                 R_Shadow_SaveWorldLights();
2455 }
2456
2457 void R_Shadow_EditLights_ImportLightEntitiesFromMap_f(void)
2458 {
2459         R_Shadow_ClearWorldLights();
2460         R_Shadow_LoadWorldLightsFromMap_LightArghliteTyrlite();
2461 }
2462
2463 void R_Shadow_EditLights_ImportLightsFile_f(void)
2464 {
2465         R_Shadow_ClearWorldLights();
2466         R_Shadow_LoadLightsFile();
2467 }
2468
2469 void R_Shadow_EditLights_Spawn_f(void)
2470 {
2471         vec3_t color;
2472         if (!r_editlights.integer)
2473         {
2474                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2475                 return;
2476         }
2477         if (Cmd_Argc() != 1)
2478         {
2479                 Con_Printf("r_editlights_spawn does not take parameters\n");
2480                 return;
2481         }
2482         color[0] = color[1] = color[2] = 1;
2483         R_Shadow_NewWorldLight(r_editlights_cursorlocation, 200, color, 0, NULL, true);
2484 }
2485
2486 void R_Shadow_EditLights_Edit_f(void)
2487 {
2488         vec3_t origin, color;
2489         vec_t radius;
2490         int style, shadows;
2491         char cubemapname[1024];
2492         if (!r_editlights.integer)
2493         {
2494                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2495                 return;
2496         }
2497         if (!r_shadow_selectedlight)
2498         {
2499                 Con_Printf("No selected light.\n");
2500                 return;
2501         }
2502         VectorCopy(r_shadow_selectedlight->origin, origin);
2503         radius = r_shadow_selectedlight->lightradius;
2504         VectorCopy(r_shadow_selectedlight->light, color);
2505         style = r_shadow_selectedlight->style;
2506         if (r_shadow_selectedlight->cubemapname)
2507                 strcpy(cubemapname, r_shadow_selectedlight->cubemapname);
2508         else
2509                 cubemapname[0] = 0;
2510         shadows = r_shadow_selectedlight->castshadows;
2511         if (!strcmp(Cmd_Argv(1), "origin"))
2512         {
2513                 if (Cmd_Argc() != 5)
2514                 {
2515                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2516                         return;
2517                 }
2518                 origin[0] = atof(Cmd_Argv(2));
2519                 origin[1] = atof(Cmd_Argv(3));
2520                 origin[2] = atof(Cmd_Argv(4));
2521         }
2522         else if (!strcmp(Cmd_Argv(1), "originx"))
2523         {
2524                 if (Cmd_Argc() != 3)
2525                 {
2526                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2527                         return;
2528                 }
2529                 origin[0] = atof(Cmd_Argv(2));
2530         }
2531         else if (!strcmp(Cmd_Argv(1), "originy"))
2532         {
2533                 if (Cmd_Argc() != 3)
2534                 {
2535                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2536                         return;
2537                 }
2538                 origin[1] = atof(Cmd_Argv(2));
2539         }
2540         else if (!strcmp(Cmd_Argv(1), "originz"))
2541         {
2542                 if (Cmd_Argc() != 3)
2543                 {
2544                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2545                         return;
2546                 }
2547                 origin[2] = atof(Cmd_Argv(2));
2548         }
2549         else if (!strcmp(Cmd_Argv(1), "move"))
2550         {
2551                 if (Cmd_Argc() != 5)
2552                 {
2553                         Con_Printf("usage: r_editlights_edit %s x y z\n", Cmd_Argv(0));
2554                         return;
2555                 }
2556                 origin[0] += atof(Cmd_Argv(2));
2557                 origin[1] += atof(Cmd_Argv(3));
2558                 origin[2] += atof(Cmd_Argv(4));
2559         }
2560         else if (!strcmp(Cmd_Argv(1), "movex"))
2561         {
2562                 if (Cmd_Argc() != 3)
2563                 {
2564                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2565                         return;
2566                 }
2567                 origin[0] += atof(Cmd_Argv(2));
2568         }
2569         else if (!strcmp(Cmd_Argv(1), "movey"))
2570         {
2571                 if (Cmd_Argc() != 3)
2572                 {
2573                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2574                         return;
2575                 }
2576                 origin[1] += atof(Cmd_Argv(2));
2577         }
2578         else if (!strcmp(Cmd_Argv(1), "movez"))
2579         {
2580                 if (Cmd_Argc() != 3)
2581                 {
2582                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2583                         return;
2584                 }
2585                 origin[2] += atof(Cmd_Argv(2));
2586         }
2587         else if (!strcmp(Cmd_Argv(1), "color"))
2588         {
2589                 if (Cmd_Argc() != 5)
2590                 {
2591                         Con_Printf("usage: r_editlights_edit %s red green blue\n", Cmd_Argv(0));
2592                         return;
2593                 }
2594                 color[0] = atof(Cmd_Argv(2));
2595                 color[1] = atof(Cmd_Argv(3));
2596                 color[2] = atof(Cmd_Argv(4));
2597         }
2598         else if (!strcmp(Cmd_Argv(1), "radius"))
2599         {
2600                 if (Cmd_Argc() != 3)
2601                 {
2602                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2603                         return;
2604                 }
2605                 radius = atof(Cmd_Argv(2));
2606         }
2607         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "style"))
2608         {
2609                 if (Cmd_Argc() != 3)
2610                 {
2611                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2612                         return;
2613                 }
2614                 style = atoi(Cmd_Argv(2));
2615         }
2616         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "cubemap"))
2617         {
2618                 if (Cmd_Argc() > 3)
2619                 {
2620                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2621                         return;
2622                 }
2623                 if (Cmd_Argc() == 3)
2624                         strcpy(cubemapname, Cmd_Argv(2));
2625                 else
2626                         cubemapname[0] = 0;
2627         }
2628         else if (Cmd_Argc() == 3 && !strcmp(Cmd_Argv(1), "shadows"))
2629         {
2630                 if (Cmd_Argc() != 3)
2631                 {
2632                         Con_Printf("usage: r_editlights_edit %s value\n", Cmd_Argv(0));
2633                         return;
2634                 }
2635                 shadows = Cmd_Argv(2)[0] == 'y' || Cmd_Argv(2)[0] == 'Y' || Cmd_Argv(2)[0] == 't' || atoi(Cmd_Argv(2));
2636         }
2637         else
2638         {
2639                 Con_Printf("usage: r_editlights_edit [property] [value]\n");
2640                 Con_Printf("Selected light's properties:\n");
2641                 Con_Printf("Origin: %f %f %f\n", r_shadow_selectedlight->origin[0], r_shadow_selectedlight->origin[1], r_shadow_selectedlight->origin[2]);
2642                 Con_Printf("Radius: %f\n", r_shadow_selectedlight->lightradius);
2643                 Con_Printf("Color: %f %f %f\n", r_shadow_selectedlight->light[0], r_shadow_selectedlight->light[1], r_shadow_selectedlight->light[2]);
2644                 Con_Printf("Style: %i\n", r_shadow_selectedlight->style);
2645                 Con_Printf("Cubemap: %s\n", r_shadow_selectedlight->cubemapname);
2646                 Con_Printf("Shadows: %s\n", r_shadow_selectedlight->castshadows ? "yes" : "no");
2647                 return;
2648         }
2649         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2650         r_shadow_selectedlight = NULL;
2651         R_Shadow_NewWorldLight(origin, radius, color, style, cubemapname, shadows);
2652 }
2653
2654 extern int con_vislines;
2655 void R_Shadow_EditLights_DrawSelectedLightProperties(void)
2656 {
2657         float x, y;
2658         char temp[256];
2659         if (r_shadow_selectedlight == NULL)
2660                 return;
2661         x = 0;
2662         y = con_vislines;
2663         sprintf(temp, "Light properties");DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2664         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;
2665         sprintf(temp, "Radius %f", r_shadow_selectedlight->lightradius);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2666         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;
2667         sprintf(temp, "Style %i", r_shadow_selectedlight->style);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2668         sprintf(temp, "Cubemap %s", r_shadow_selectedlight->cubemapname);DrawQ_String(x, y, temp, 0, 8, 8, 1, 1, 1, 1, 0);y += 8;
2669         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;
2670 }
2671
2672 void R_Shadow_EditLights_ToggleShadow_f(void)
2673 {
2674         if (!r_editlights.integer)
2675         {
2676                 Con_Printf("Cannot spawn light when not in editing mode.  Set r_editlights to 1.\n");
2677                 return;
2678         }
2679         if (!r_shadow_selectedlight)
2680         {
2681                 Con_Printf("No selected light.\n");
2682                 return;
2683         }
2684         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);
2685         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2686         r_shadow_selectedlight = NULL;
2687 }
2688
2689 void R_Shadow_EditLights_Remove_f(void)
2690 {
2691         if (!r_editlights.integer)
2692         {
2693                 Con_Printf("Cannot remove light when not in editing mode.  Set r_editlights to 1.\n");
2694                 return;
2695         }
2696         if (!r_shadow_selectedlight)
2697         {
2698                 Con_Printf("No selected light.\n");
2699                 return;
2700         }
2701         R_Shadow_FreeWorldLight(r_shadow_selectedlight);
2702         r_shadow_selectedlight = NULL;
2703 }
2704
2705 void R_Shadow_EditLights_Init(void)
2706 {
2707         Cvar_RegisterVariable(&r_editlights);
2708         Cvar_RegisterVariable(&r_editlights_cursordistance);
2709         Cvar_RegisterVariable(&r_editlights_cursorpushback);
2710         Cvar_RegisterVariable(&r_editlights_cursorpushoff);
2711         Cvar_RegisterVariable(&r_editlights_cursorgrid);
2712         Cvar_RegisterVariable(&r_editlights_quakelightsizescale);
2713         Cvar_RegisterVariable(&r_editlights_rtlightssizescale);
2714         Cvar_RegisterVariable(&r_editlights_rtlightscolorscale);
2715         Cmd_AddCommand("r_editlights_clear", R_Shadow_EditLights_Clear_f);
2716         Cmd_AddCommand("r_editlights_reload", R_Shadow_EditLights_Reload_f);
2717         Cmd_AddCommand("r_editlights_save", R_Shadow_EditLights_Save_f);
2718         Cmd_AddCommand("r_editlights_spawn", R_Shadow_EditLights_Spawn_f);
2719         Cmd_AddCommand("r_editlights_edit", R_Shadow_EditLights_Edit_f);
2720         Cmd_AddCommand("r_editlights_remove", R_Shadow_EditLights_Remove_f);
2721         Cmd_AddCommand("r_editlights_toggleshadow", R_Shadow_EditLights_ToggleShadow_f);
2722         Cmd_AddCommand("r_editlights_importlightentitiesfrommap", R_Shadow_EditLights_ImportLightEntitiesFromMap_f);
2723         Cmd_AddCommand("r_editlights_importlightsfile", R_Shadow_EditLights_ImportLightsFile_f);
2724 }