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