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