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