1 #include "engine.h"
2 
setlightdir(vec & dir,float yaw,float pitch)3 void setlightdir(vec &dir, float yaw, float pitch)
4 {
5     dir = vec(yaw*RAD, pitch*RAD);
6     loopk(3) if(fabs(dir[k]) < 1e-5f) dir[k] = 0;
7     dir.normalize();
8     clearradiancehintscache();
9 }
10 
11 #define PIESKYVARS(name, type) \
12     CVAR1F(IDF_WORLD, sunlight##name, 0, \
13     { \
14         if(!checkmapvariant(type)) return; \
15         clearradiancehintscache(); \
16         cleardeferredlightshaders(); \
17         clearshadowcache(); \
18     }); \
19     FVARF(IDF_WORLD, sunlightscale##name, 0, 1, 16, if(checkmapvariant(type)) clearradiancehintscache()); \
20     vec sunlightdir##name(0, 0, 1); \
21     extern float sunlightpitch##name; \
22     FVARF(IDF_WORLD, sunlightyaw##name, 0, 0, 360, setlightdir(sunlightdir##name, sunlightyaw##name, sunlightpitch##name)); \
23     FVARF(IDF_WORLD, sunlightpitch##name, -90, 90, 90, setlightdir(sunlightdir##name, sunlightyaw##name, sunlightpitch##name)); \
24 
25 PIESKYVARS(, MPV_DEF);
26 PIESKYVARS(alt, MPV_ALT);
27 
28 #define GETSKYPIE(name, type) \
29     type getpie##name() \
30     { \
31         if(checkmapvariant(MPV_ALT)) return sun##name##alt; \
32         return sun##name; \
33     }
34 
35 GETSKYPIE(light, bvec &);
36 GETSKYPIE(lightscale, float);
37 GETSKYPIE(lightdir, vec &);
38 GETSKYPIE(lightyaw, float);
39 GETSKYPIE(lightpitch, float);
40 
getlightfx(const extentity & e,int * radius,int * spotlight,vec * color,bool normalize)41 bool getlightfx(const extentity &e, int *radius, int *spotlight, vec *color, bool normalize)
42 {
43     if(!checkmapvariant(e.attrs[9]) || !checkmapeffects(e.attrs[10])) return false;
44     if(color)
45     {
46         *color = vec(e.attrs[1], e.attrs[2], e.attrs[3]);
47         if(e.attrs[7] || e.attrs[8]) color->mul(game::getpalette(e.attrs[7], e.attrs[8]));
48         if(normalize) color->div(255.f);
49         color->max(0);
50     }
51     static int tempradius;
52     if(!radius) radius = &tempradius;
53     *radius = e.attrs[0] ? e.attrs[0] : worldsize; // after this, "0" becomes "off"
54 
55     const vector<extentity *> &ents = entities::getents();
56     loopv(e.links) if(ents.inrange(e.links[i]))
57     {
58         extentity &f = *ents[e.links[i]];
59         if(f.type != ET_LIGHTFX || f.attrs[0] < 0 || f.attrs[0] >= LFX_MAX || !checkmapvariant(f.attrs[5]) || !checkmapeffects(f.attrs[6])) continue;
60         bool hastrigger = false;
61         loopvk(f.links) if(ents.inrange(f.links[k]) && ents[f.links[k]]->type != ET_LIGHT)
62         {
63             hastrigger = true;
64             break;
65         }
66         if(hastrigger && !f.spawned())
67         {
68             *radius = 0;
69             break;
70         }
71         int effect = f.attrs[0], millis = lastmillis-f.emit[2], interval = f.emit[0]+f.emit[1];
72         bool hasemit = f.emit[0] && f.emit[1] && f.emit[2], expired = millis >= interval;
73         if(!hasemit || expired)
74         {
75             bool israndom = false;
76             loopk(2)
77             {
78                 int val = f.attrs[k+2] > 0 ? f.attrs[k+2] : 500;
79                 if(f.attrs[4]&(1<<k))
80                 {
81                     f.emit[k] = 1+(val >= 2 ? rnd(val-1) : 0);
82                     israndom = true;
83                 }
84                 else f.emit[k] = val;
85             }
86             int oldinterval = interval;
87             interval = f.emit[0]+f.emit[1];
88             if(israndom && interval == oldinterval) israndom = false;
89             f.emit[2] = lastmillis;
90             if(israndom) f.emit[2] -= millis-oldinterval;
91             else f.emit[2] -= f.emit[2]%interval;
92             millis = lastmillis-f.emit[2];
93         }
94         if(millis >= f.emit[0]) loopi(LFX_MAX-1) if(f.attrs[4]&(1<<(LFX_S_MAX+i)))
95         {
96             effect = i+1;
97             break;
98         }
99         float skew = clamp(millis < f.emit[0] ? 1.f-(float(millis)/float(f.emit[0])) : float(millis-f.emit[0])/float(f.emit[1]), 0.f, 1.f);
100         switch(effect)
101         {
102             case LFX_SPOTLIGHT:
103             {
104                 if(spotlight && *spotlight <= 0) *spotlight = e.links[i];
105                 break;
106             }
107             case LFX_FLICKER:
108             {
109                 if(millis >= f.emit[0])
110                     *radius -= (f.attrs[1] ? f.attrs[1] : *radius);
111                 break;
112             }
113             case LFX_INVPULSE: skew = 1-skew;
114             case LFX_PULSE:
115             {
116                 *radius -= (f.attrs[1] ? f.attrs[1] : *radius)*skew;
117                 break;
118             }
119             case LFX_INVGLOW: skew = 1-skew;
120             case LFX_GLOW:
121             {
122                 if(color) color->mul(skew);
123                 *radius -= f.attrs[1]*skew;
124                 break;
125             }
126             default: break;
127         }
128     }
129     return *radius > 0;
130 }
131 
132 static const surfaceinfo brightsurfaces[6] =
133 {
134     brightsurface,
135     brightsurface,
136     brightsurface,
137     brightsurface,
138     brightsurface,
139     brightsurface
140 };
141 
brightencube(cube & c)142 void brightencube(cube &c)
143 {
144     if(!c.ext) newcubeext(c, 0, false);
145     memcpy(c.ext->surfaces, brightsurfaces, sizeof(brightsurfaces));
146 }
147 
setsurfaces(cube & c,const surfaceinfo * surfs,const vertinfo * verts,int numverts)148 void setsurfaces(cube &c, const surfaceinfo *surfs, const vertinfo *verts, int numverts)
149 {
150     if(!c.ext || c.ext->maxverts < numverts) newcubeext(c, numverts, false);
151     memcpy(c.ext->surfaces, surfs, sizeof(c.ext->surfaces));
152     memcpy(c.ext->verts(), verts, numverts*sizeof(vertinfo));
153 }
154 
setsurface(cube & c,int orient,const surfaceinfo & src,const vertinfo * srcverts,int numsrcverts)155 void setsurface(cube &c, int orient, const surfaceinfo &src, const vertinfo *srcverts, int numsrcverts)
156 {
157     int dstoffset = 0;
158     if(!c.ext) newcubeext(c, numsrcverts, true);
159     else
160     {
161         int numbefore = 0, beforeoffset = 0;
162         loopi(orient)
163         {
164             surfaceinfo &surf = c.ext->surfaces[i];
165             int numverts = surf.totalverts();
166             if(!numverts) continue;
167             numbefore += numverts;
168             beforeoffset = surf.verts + numverts;
169         }
170         int numafter = 0, afteroffset = c.ext->maxverts;
171         for(int i = 5; i > orient; i--)
172         {
173             surfaceinfo &surf = c.ext->surfaces[i];
174             int numverts = surf.totalverts();
175             if(!numverts) continue;
176             numafter += numverts;
177             afteroffset = surf.verts;
178         }
179         if(afteroffset - beforeoffset >= numsrcverts) dstoffset = beforeoffset;
180         else
181         {
182             cubeext *ext = c.ext;
183             if(numbefore + numsrcverts + numafter > c.ext->maxverts)
184             {
185                 ext = growcubeext(c.ext, numbefore + numsrcverts + numafter);
186                 memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
187             }
188             int offset = 0;
189             if(numbefore == beforeoffset)
190             {
191                 if(numbefore && c.ext != ext) memcpy(ext->verts(), c.ext->verts(), numbefore*sizeof(vertinfo));
192                 offset = numbefore;
193             }
194             else loopi(orient)
195             {
196                 surfaceinfo &surf = ext->surfaces[i];
197                 int numverts = surf.totalverts();
198                 if(!numverts) continue;
199                 memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
200                 surf.verts = offset;
201                 offset += numverts;
202             }
203             dstoffset = offset;
204             offset += numsrcverts;
205             if(numafter && offset > afteroffset)
206             {
207                 offset += numafter;
208                 for(int i = 5; i > orient; i--)
209                 {
210                     surfaceinfo &surf = ext->surfaces[i];
211                     int numverts = surf.totalverts();
212                     if(!numverts) continue;
213                     offset -= numverts;
214                     memmove(ext->verts() + offset, c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
215                     surf.verts = offset;
216                 }
217             }
218             if(c.ext != ext) setcubeext(c, ext);
219         }
220     }
221     surfaceinfo &dst = c.ext->surfaces[orient];
222     dst = src;
223     dst.verts = dstoffset;
224     if(srcverts) memcpy(c.ext->verts() + dstoffset, srcverts, numsrcverts*sizeof(vertinfo));
225 }
226 
insert(ushort & tx,ushort & ty,ushort tw,ushort th)227 bool PackNode::insert(ushort &tx, ushort &ty, ushort tw, ushort th)
228 {
229     if((available < tw && available < th) || w < tw || h < th)
230         return false;
231     if(child1)
232     {
233         bool inserted = child1->insert(tx, ty, tw, th) ||
234                         child2->insert(tx, ty, tw, th);
235         available = max(child1->available, child2->available);
236         if(!available) discardchildren();
237         return inserted;
238     }
239     if(w == tw && h == th)
240     {
241         available = 0;
242         tx = x;
243         ty = y;
244         return true;
245     }
246 
247     if(w - tw > h - th)
248     {
249         child1 = new PackNode(x, y, tw, h);
250         child2 = new PackNode(x + tw, y, w - tw, h);
251     }
252     else
253     {
254         child1 = new PackNode(x, y, w, th);
255         child2 = new PackNode(x, y + th, w, h - th);
256     }
257 
258     bool inserted = child1->insert(tx, ty, tw, th);
259     available = max(child1->available, child2->available);
260     return inserted;
261 }
262 
reserve(ushort tx,ushort ty,ushort tw,ushort th)263 void PackNode::reserve(ushort tx, ushort ty, ushort tw, ushort th)
264 {
265     if(tx + tw <= x || tx >= x + w || ty + th <= y || ty >= y + h) return;
266     if(child1)
267     {
268         child1->reserve(tx, ty, tw, th);
269         child2->reserve(tx, ty, tw, th);
270         available = max(child1->available, child2->available);
271         return;
272     }
273     int dx1 = tx - x, dx2 = x + w - tx - tw, dx = max(dx1, dx2),
274         dy1 = ty - y, dy2 = y + h - ty - th, dy = max(dy1, dy2),
275         split;
276     if(dx > dy)
277     {
278         if(dx1 > dx2) split = min(dx1, int(w));
279         else split = w - max(dx2, 0);
280         if(w - split <= 0)
281         {
282             w = split;
283             available = min(w, h);
284             if(dy > 0) reserve(tx, ty, tw, th);
285             else if(tx <= x && tx + tw >= x + w) available = 0;
286             return;
287         }
288         if(split <= 0)
289         {
290             x += split;
291             w -= split;
292             available = min(w, h);
293             if(dy > 0) reserve(tx, ty, tw, th);
294             else if(tx <= x && tx + tw >= x + w) available = 0;
295             return;
296         }
297         child1 = new PackNode(x, y, split, h);
298         child2 = new PackNode(x + split, y, w - split, h);
299     }
300     else
301     {
302         if(dy1 > dy2) split = min(dy1, int(h));
303         else split = h - max(dy2, 0);
304         if(h - split <= 0)
305         {
306             h = split;
307             available = min(w, h);
308             if(dx > 0) reserve(tx, ty, tw, th);
309             else if(ty <= y && ty + th >= y + h) available = 0;
310             return;
311         }
312         if(split <= 0)
313         {
314             y += split;
315             h -= split;
316             available = min(w, h);
317             if(dx > 0) reserve(tx, ty, tw, th);
318             else if(ty <= y && ty + th >= y + h) available = 0;
319             return;
320         }
321         child1 = new PackNode(x, y, w, split);
322         child2 = new PackNode(x, y + split, w, h - split);
323     }
324     child1->reserve(tx, ty, tw, th);
325     child2->reserve(tx, ty, tw, th);
326     available = max(child1->available, child2->available);
327 }
328 
clearsurfaces(cube * c)329 static void clearsurfaces(cube *c)
330 {
331     loopi(8)
332     {
333         if(c[i].ext)
334         {
335             loopj(6)
336             {
337                 surfaceinfo &surf = c[i].ext->surfaces[j];
338                 if(!surf.used()) continue;
339                 surf.clear();
340                 int numverts = surf.numverts&MAXFACEVERTS;
341                 if(numverts)
342                 {
343                     if(!(c[i].merged&(1<<j))) { surf.numverts &= ~MAXFACEVERTS; continue; }
344 
345                     vertinfo *verts = c[i].ext->verts() + surf.verts;
346                     loopk(numverts)
347                     {
348                         vertinfo &v = verts[k];
349                         v.norm = 0;
350                     }
351                 }
352             }
353         }
354         if(c[i].children) clearsurfaces(c[i].children);
355     }
356 }
357 
358 #define LIGHTCACHESIZE 1024
359 
360 static struct lightcacheentry
361 {
362     int x, y;
363     vector<int> lights;
364 } lightcache[LIGHTCACHESIZE];
365 
366 #define LIGHTCACHEHASH(x, y) (((((x)^(y))<<5) + (((x)^(y))>>5)) & (LIGHTCACHESIZE - 1))
367 
368 VARF(0, lightcachesize, 4, 6, 12, clearlightcache());
369 
clearlightcache(int id)370 void clearlightcache(int id)
371 {
372     if(id >= 0)
373     {
374         const extentity &light = *entities::getents()[id];
375         int radius = light.attrs[0];
376         if(radius <= 0) return;
377         for(int x = int(max(light.o.x-radius, 0.0f))>>lightcachesize, ex = int(min(light.o.x+radius, worldsize-1.0f))>>lightcachesize; x <= ex; x++)
378         for(int y = int(max(light.o.y-radius, 0.0f))>>lightcachesize, ey = int(min(light.o.y+radius, worldsize-1.0f))>>lightcachesize; y <= ey; y++)
379         {
380             lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
381             if(lce.x != x || lce.y != y) continue;
382             lce.x = -1;
383             lce.lights.setsize(0);
384         }
385         return;
386     }
387 
388     for(lightcacheentry *lce = lightcache; lce < &lightcache[LIGHTCACHESIZE]; lce++)
389     {
390         lce->x = -1;
391         lce->lights.setsize(0);
392     }
393 }
394 
checklightcache(int x,int y)395 const vector<int> &checklightcache(int x, int y)
396 {
397     x >>= lightcachesize;
398     y >>= lightcachesize;
399     lightcacheentry &lce = lightcache[LIGHTCACHEHASH(x, y)];
400     if(lce.x == x && lce.y == y) return lce.lights;
401 
402     lce.lights.setsize(0);
403     int csize = 1<<lightcachesize, cx = x<<lightcachesize, cy = y<<lightcachesize;
404     const vector<extentity *> &ents = entities::getents();
405     loopv(ents)
406     {
407         const extentity &light = *ents[i];
408         switch(light.type)
409         {
410             case ET_LIGHT:
411             {
412                 int radius = light.attrs[0];
413                 if(!getlightfx(light, &radius) ||
414                    light.o.x + radius < cx || light.o.x - radius > cx + csize ||
415                    light.o.y + radius < cy || light.o.y - radius > cy + csize)
416                     continue;
417                 break;
418             }
419             default: continue;
420         }
421         lce.lights.add(i);
422     }
423 
424     lce.x = x;
425     lce.y = y;
426     return lce.lights;
427 }
428 
429 static uint lightprogress = 0;
430 
431 bool calclight_canceled = false;
432 volatile bool check_calclight_progress = false;
433 
check_calclight_canceled()434 void check_calclight_canceled()
435 {
436     if(interceptkey(SDLK_ESCAPE))
437     {
438         calclight_canceled = true;
439     }
440     if(!calclight_canceled) check_calclight_progress = false;
441 }
442 
show_calclight_progress()443 void show_calclight_progress()
444 {
445     float amt = float(lightprogress)/float(allocnodes);
446     progress(amt, "Computing lighting... (ESC to abort)");
447 }
448 
calcsurfaces(cube & c,const ivec & co,int size,int usefacemask,int preview=0)449 static void calcsurfaces(cube &c, const ivec &co, int size, int usefacemask, int preview = 0)
450 {
451     surfaceinfo surfaces[6];
452     vertinfo litverts[6*2*MAXFACEVERTS];
453     int numlitverts = 0;
454     memset(surfaces, 0, sizeof(surfaces));
455     loopi(6)
456     {
457         int usefaces = usefacemask&0xF;
458         usefacemask >>= 4;
459         if(!usefaces)
460         {
461             if(!c.ext) continue;
462             surfaceinfo &surf = surfaces[i];
463             surf = c.ext->surfaces[i];
464             int numverts = surf.totalverts();
465             if(numverts)
466             {
467                 memcpy(&litverts[numlitverts], c.ext->verts() + surf.verts, numverts*sizeof(vertinfo));
468                 surf.verts = numlitverts;
469                 numlitverts += numverts;
470             }
471             continue;
472         }
473 
474         VSlot &vslot = lookupvslot(c.texture[i], false);
475         surfaceinfo &surf = surfaces[i];
476         vertinfo *curlitverts = &litverts[numlitverts];
477         int numverts = c.ext ? c.ext->surfaces[i].numverts&MAXFACEVERTS : 0;
478         ivec mo(co);
479         int msz = size, convex = 0;
480         if(numverts)
481         {
482             vertinfo *verts = c.ext->verts() + c.ext->surfaces[i].verts;
483             loopj(numverts) curlitverts[j].set(verts[j].getxyz());
484             if(c.merged&(1<<i))
485             {
486                 msz = 1<<calcmergedsize(i, mo, size, verts, numverts);
487                 mo.mask(~(msz-1));
488 
489                 if(!(surf.numverts&MAXFACEVERTS))
490                 {
491                     surf.verts = numlitverts;
492                     surf.numverts |= numverts;
493                     numlitverts += numverts;
494                 }
495             }
496             else if(!flataxisface(c, i)) convex = faceconvexity(verts, numverts, size);
497         }
498         else
499         {
500             ivec v[4];
501             genfaceverts(c, i, v);
502             if(!flataxisface(c, i)) convex = faceconvexity(v);
503             int order = usefaces&4 || convex < 0 ? 1 : 0;
504             ivec vo = ivec(co).mask(0xFFF).shl(3);
505             curlitverts[numverts++].set(v[order].mul(size).add(vo));
506             if(usefaces&1) curlitverts[numverts++].set(v[order+1].mul(size).add(vo));
507             curlitverts[numverts++].set(v[order+2].mul(size).add(vo));
508             if(usefaces&2) curlitverts[numverts++].set(v[(order+3)&3].mul(size).add(vo));
509         }
510 
511         vec pos[MAXFACEVERTS], n[MAXFACEVERTS], po(ivec(co).mask(~0xFFF));
512         loopj(numverts) pos[j] = vec(curlitverts[j].getxyz()).mul(1.0f/8).add(po);
513 
514         int smooth = vslot.slot->smooth;
515         plane planes[2];
516         int numplanes = 0;
517         planes[numplanes++].toplane(pos[0], pos[1], pos[2]);
518         if(numverts < 4 || !convex) loopk(numverts) findnormal(pos[k], smooth, planes[0], n[k]);
519         else
520         {
521             planes[numplanes++].toplane(pos[0], pos[2], pos[3]);
522             vec avg = vec(planes[0]).add(planes[1]).normalize();
523             findnormal(pos[0], smooth, avg, n[0]);
524             findnormal(pos[1], smooth, planes[0], n[1]);
525             findnormal(pos[2], smooth, avg, n[2]);
526             for(int k = 3; k < numverts; k++) findnormal(pos[k], smooth, planes[1], n[k]);
527         }
528 
529         loopk(numverts) curlitverts[k].norm = encodenormal(n[k]);
530         if(!(surf.numverts&MAXFACEVERTS))
531         {
532             surf.verts = numlitverts;
533             surf.numverts |= numverts;
534             numlitverts += numverts;
535         }
536 
537         if(preview) { surf.numverts |= preview; continue; }
538 
539         int surflayer = LAYER_TOP;
540         if(vslot.layer)
541         {
542             int x1 = curlitverts[numverts-1].x, y1 = curlitverts[numverts-1].y, x2 = x1, y2 = y1;
543             loopj(numverts-1)
544             {
545                 const vertinfo &v = curlitverts[j];
546                 x1 = min(x1, int(v.x));
547                 y1 = min(y1, int(v.y));
548                 x2 = max(x2, int(v.x));
549                 y2 = max(y2, int(v.y));
550             }
551             x2 = max(x2, x1+1);
552             y2 = max(y2, y1+1);
553             x1 = (x1>>3) + (co.x&~0xFFF);
554             y1 = (y1>>3) + (co.y&~0xFFF);
555             x2 = ((x2+7)>>3) + (co.x&~0xFFF);
556             y2 = ((y2+7)>>3) + (co.y&~0xFFF);
557             surflayer = calcblendlayer(x1, y1, x2, y2);
558         }
559         surf.numverts |= surflayer;
560     }
561     if(preview) setsurfaces(c, surfaces, litverts, numlitverts);
562     else loopk(6)
563     {
564         surfaceinfo &surf = surfaces[k];
565         if(surf.used())
566         {
567             cubeext *ext = c.ext && c.ext->maxverts >= numlitverts ? c.ext : growcubeext(c.ext, numlitverts);
568             memcpy(ext->surfaces, surfaces, sizeof(ext->surfaces));
569             memcpy(ext->verts(), litverts, numlitverts*sizeof(vertinfo));
570             if(c.ext != ext) setcubeext(c, ext);
571             break;
572         }
573     }
574 }
575 
calcsurfaces(cube * c,const ivec & co,int size)576 static void calcsurfaces(cube *c, const ivec &co, int size)
577 {
578     CHECK_CALCLIGHT_PROGRESS(return, show_calclight_progress);
579 
580     lightprogress++;
581 
582     loopi(8)
583     {
584         ivec o(i, co, size);
585         if(c[i].children)
586             calcsurfaces(c[i].children, o, size >> 1);
587         else if(!isempty(c[i]))
588         {
589             if(c[i].ext)
590             {
591                 loopj(6) c[i].ext->surfaces[j].clear();
592             }
593             int usefacemask = 0;
594             loopj(6) if(c[i].texture[j] != DEFAULT_SKY && (!(c[i].merged&(1<<j)) || (c[i].ext && c[i].ext->surfaces[j].numverts&MAXFACEVERTS)))
595             {
596                 usefacemask |= visibletris(c[i], j, o, size)<<(4*j);
597             }
598             if(usefacemask) calcsurfaces(c[i], o, size, usefacemask);
599         }
600     }
601 }
602 
previewblends(cube & c,const ivec & o,int size)603 static inline bool previewblends(cube &c, const ivec &o, int size)
604 {
605     if(isempty(c) || c.material&MAT_ALPHA) return false;
606     int usefacemask = 0;
607     loopj(6) if(c.texture[j] != DEFAULT_SKY && lookupvslot(c.texture[j], false).layer)
608         usefacemask |= visibletris(c, j, o, size)<<(4*j);
609     if(!usefacemask) return false;
610     int layer = calcblendlayer(o.x, o.y, o.x + size, o.y + size);
611     if(!(layer&LAYER_BOTTOM))
612     {
613         if(!c.ext) return false;
614         bool blends = false;
615         loopi(6) if(c.ext->surfaces[i].numverts&LAYER_BOTTOM)
616         {
617             c.ext->surfaces[i].brighten();
618             blends = true;
619         }
620         return blends;
621     }
622     calcsurfaces(c, o, size, usefacemask, layer);
623     return true;
624 }
625 
previewblends(cube * c,const ivec & co,int size,const ivec & bo,const ivec & bs)626 static bool previewblends(cube *c, const ivec &co, int size, const ivec &bo, const ivec &bs)
627 {
628     bool changed = false;
629     loopoctabox(co, size, bo, bs)
630     {
631         ivec o(i, co, size);
632         cubeext *ext = c[i].ext;
633         if(ext && ext->va && ext->va->hasmerges)
634         {
635             changed = true;
636             destroyva(ext->va);
637             ext->va = NULL;
638             invalidatemerges(c[i], co, size, true);
639         }
640         if(c[i].children ? previewblends(c[i].children, o, size/2, bo, bs) : previewblends(c[i], o, size))
641         {
642             changed = true;
643             ext = c[i].ext;
644             if(ext && ext->va)
645             {
646                 destroyva(ext->va);
647                 ext->va = NULL;
648             }
649         }
650     }
651     return changed;
652 }
653 
previewblends(const ivec & bo,const ivec & bs)654 void previewblends(const ivec &bo, const ivec &bs)
655 {
656     updateblendtextures(bo.x, bo.y, bo.x+bs.x, bo.y+bs.y);
657     if(previewblends(worldroot, ivec(0, 0, 0), worldsize/2, bo, bs))
658         commitchanges(true);
659 }
660 
661 extern int filltjoints;
662 
calclighttimer(Uint32 interval,void * param)663 static Uint32 calclighttimer(Uint32 interval, void *param)
664 {
665     check_calclight_progress = true;
666     return interval;
667 }
668 
calclight()669 void calclight()
670 {
671     progress(-1, "Computing lighting... (ESC to abort)");
672     remip();
673     optimizeblendmap();
674     clearsurfaces(worldroot);
675     lightprogress = 0;
676     calclight_canceled = false;
677     check_calclight_progress = false;
678     SDL_TimerID timer = SDL_AddTimer(250, calclighttimer, NULL);
679     Uint32 start = SDL_GetTicks();
680     calcnormals(filltjoints > 0);
681     calcsurfaces(worldroot, ivec(0, 0, 0), worldsize >> 1);
682     clearnormals();
683     Uint32 end = SDL_GetTicks();
684     if(timer) SDL_RemoveTimer(timer);
685     progress(0, "Lighting done...");
686     allchanged();
687     if(calclight_canceled) conoutf("Calclight aborted");
688     else conoutf("Computed lighting (%.1f seconds)", (end - start) / 1000.0f);
689 }
690 
mpcalclight(bool local)691 void mpcalclight(bool local)
692 {
693     if(local) client::edittrigger(sel, EDIT_CALCLIGHT);
694     calclight();
695 }
696 
697 ICOMMAND(0, calclight, "", (), mpcalclight(true));
698 
699 VAR(0, fullbright, 0, 0, 1);
700 VAR(0, fullbrightlevel, 0, 160, 255);
701 
clearlights()702 void clearlights()
703 {
704     clearlightcache();
705     clearshadowcache();
706     cleardeferredlightshaders();
707     resetsmoothgroups();
708 }
709 
initlights()710 void initlights()
711 {
712     clearlightcache();
713     clearshadowcache();
714     loaddeferredlightshaders();
715 }
716 
lightreaching(const vec & target,vec & color,vec & dir,bool fast,extentity * t,float minambient)717 void lightreaching(const vec &target, vec &color, vec &dir, bool fast, extentity *t, float minambient)
718 {
719     if(fullbright && editmode)
720     {
721         color = vec(1, 1, 1);
722         dir = vec(0, 0, 1);
723         return;
724     }
725 
726     color = dir = vec(0, 0, 0);
727     const vector<extentity *> &ents = entities::getents();
728     const vector<int> &lights = checklightcache(int(target.x), int(target.y));
729     loopv(lights)
730     {
731         extentity &e = *ents[lights[i]];
732         if(e.type != ET_LIGHT) continue;
733 
734         float intensity = 1;
735         int radius = e.attrs[0], slight = -1;
736         vec lightcol(1, 1, 1);
737         if(!getlightfx(e, &radius, &slight, &lightcol)) continue;
738 
739         vec ray(target);
740         ray.sub(e.o);
741         if(ents.inrange(slight))
742         {
743             extentity &spotlight = *ents[slight];
744             vec spot = vec(spotlight.o).sub(e.o).normalize();
745             float spotatten = 1 - (1 - ray.dot(spot)) / (1 - cos360(clamp(int(spotlight.attrs[1]), 1, 89)));
746             if(spotatten <= 0) continue;
747             intensity *= spotatten;
748         }
749         float mag = ray.magnitude();
750         if(mag >= float(radius)) continue;
751 
752         intensity *= 1 - mag / float(radius);
753 
754         if(mag < 1e-4f) ray = vec(0, 0, -1);
755         else
756         {
757             ray.div(mag);
758             if(shadowray(e.o, ray, mag, RAY_SHADOW | RAY_POLY, t) < mag)
759                 continue;
760         }
761 
762         color.add(vec(lightcol).mul(intensity));
763         dir.add(vec(ray).mul(-intensity*lightcol.x*lightcol.y*lightcol.z));
764     }
765     bvec pie = getpielight();
766     vec piedir = getpielightdir();
767     if(!pie.iszero() && shadowray(target, piedir, 1e16f, RAY_SHADOW | RAY_POLY, t) > 1e15f)
768     {
769         vec lightcol = pie.tocolor().mul(getpielightscale());
770         color.add(lightcol);
771         dir.add(vec(piedir).mul(lightcol.x*lightcol.y*lightcol.z));
772     }
773     color.max(getambient().tocolor().max(minambient)).min(1.5f);
774     if(dir.iszero()) dir = vec(0, 0, 1);
775     else dir.normalize();
776 }
777 
778