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