1 // core world management routines
2 
3 #include "engine.h"
4 
5 static struct emptycube : cube
6 {
emptycubeemptycube7     emptycube()
8     {
9         children = NULL;
10         ext = NULL;
11         visible = 0;
12         merged = 0;
13         material = MAT_AIR;
14         setfaces(*this, F_EMPTY);
15         loopi(6) texture[i] = DEFAULT_SKY;
16     }
17 } emptycube;
18 
19 cube *worldroot = newcubes(F_SOLID);
20 int allocnodes = 0;
21 
growcubeext(cubeext * old,int maxverts)22 cubeext *growcubeext(cubeext *old, int maxverts)
23 {
24     cubeext *ext = (cubeext *)new uchar[sizeof(cubeext) + maxverts*sizeof(vertinfo)];
25     if(old)
26     {
27         ext->va = old->va;
28         ext->ents = old->ents;
29         ext->tjoints = old->tjoints;
30     }
31     else
32     {
33         ext->va = NULL;
34         ext->ents = NULL;
35         ext->tjoints = -1;
36     }
37     ext->maxverts = maxverts;
38     return ext;
39 }
40 
setcubeext(cube & c,cubeext * ext)41 void setcubeext(cube &c, cubeext *ext)
42 {
43     cubeext *old = c.ext;
44     if(old == ext) return;
45     c.ext = ext;
46     if(old) delete[] (uchar *)old;
47 }
48 
newcubeext(cube & c,int maxverts,bool init)49 cubeext *newcubeext(cube &c, int maxverts, bool init)
50 {
51     if(c.ext && c.ext->maxverts >= maxverts) return c.ext;
52     cubeext *ext = growcubeext(c.ext, maxverts);
53     if(init)
54     {
55         if(c.ext)
56         {
57             memcpy(ext->surfaces, c.ext->surfaces, sizeof(ext->surfaces));
58             memcpy(ext->verts(), c.ext->verts(), c.ext->maxverts*sizeof(vertinfo));
59         }
60         else memset(ext->surfaces, 0, sizeof(ext->surfaces));
61     }
62     setcubeext(c, ext);
63     return ext;
64 }
65 
newcubes(uint face,int mat)66 cube *newcubes(uint face, int mat)
67 {
68     cube *c = new cube[8];
69     loopi(8)
70     {
71         c->children = NULL;
72         c->ext = NULL;
73         c->visible = 0;
74         c->merged = 0;
75         setfaces(*c, face);
76         loopl(6) c->texture[l] = DEFAULT_GEOM;
77         c->material = mat;
78         c++;
79     }
80     allocnodes++;
81     return c-8;
82 }
83 
familysize(const cube & c)84 int familysize(const cube &c)
85 {
86     int size = 1;
87     if(c.children) loopi(8) size += familysize(c.children[i]);
88     return size;
89 }
90 
freeocta(cube * c)91 void freeocta(cube *c)
92 {
93     if(!c) return;
94     loopi(8) discardchildren(c[i]);
95     delete[] c;
96     allocnodes--;
97 }
98 
freecubeext(cube & c)99 void freecubeext(cube &c)
100 {
101     if(c.ext)
102     {
103         delete[] (uchar *)c.ext;
104         c.ext = NULL;
105     }
106 }
107 
discardchildren(cube & c,bool fixtex,int depth)108 void discardchildren(cube &c, bool fixtex, int depth)
109 {
110     c.material = MAT_AIR;
111     c.visible = 0;
112     c.merged = 0;
113     if(c.ext)
114     {
115         if(c.ext->va) destroyva(c.ext->va);
116         c.ext->va = NULL;
117         c.ext->tjoints = -1;
118         freeoctaentities(c);
119         freecubeext(c);
120     }
121     if(c.children)
122     {
123         uint filled = F_EMPTY;
124         loopi(8)
125         {
126             discardchildren(c.children[i], fixtex, depth+1);
127             filled |= c.children[i].faces[0];
128         }
129         if(fixtex)
130         {
131             loopi(6) c.texture[i] = getmippedtexture(c, i);
132             if(depth > 0 && filled != F_EMPTY) c.faces[0] = F_SOLID;
133         }
134         DELETEA(c.children);
135         allocnodes--;
136     }
137 }
138 
getcubevector(cube & c,int d,int x,int y,int z,ivec & p)139 void getcubevector(cube &c, int d, int x, int y, int z, ivec &p)
140 {
141     ivec v(d, x, y, z);
142 
143     loopi(3)
144         p[i] = edgeget(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]]);
145 }
146 
setcubevector(cube & c,int d,int x,int y,int z,const ivec & p)147 void setcubevector(cube &c, int d, int x, int y, int z, const ivec &p)
148 {
149     ivec v(d, x, y, z);
150 
151     loopi(3)
152         edgeset(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]], p[i]);
153 }
154 
getcubevector(cube & c,int i,ivec & p)155 static inline void getcubevector(cube &c, int i, ivec &p)
156 {
157     p.x = edgeget(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1);
158     p.y = edgeget(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1);
159     p.z = edgeget(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1);
160 }
161 
setcubevector(cube & c,int i,const ivec & p)162 static inline void setcubevector(cube &c, int i, const ivec &p)
163 {
164     edgeset(cubeedge(c, 0, (i>>R[0])&1, (i>>C[0])&1), (i>>D[0])&1, p.x);
165     edgeset(cubeedge(c, 1, (i>>R[1])&1, (i>>C[1])&1), (i>>D[1])&1, p.y);
166     edgeset(cubeedge(c, 2, (i>>R[2])&1, (i>>C[2])&1), (i>>D[2])&1, p.z);
167 }
168 
optiface(uchar * p,cube & c)169 void optiface(uchar *p, cube &c)
170 {
171     uint f = *(uint *)p;
172     if(((f>>4)&0x0F0F0F0FU) == (f&0x0F0F0F0FU)) emptyfaces(c);
173 }
174 
printcube()175 void printcube()
176 {
177     cube &c = lookupcube(lu); // assume this is cube being pointed at
178     conoutf("\fa= %p = (%d, %d, %d) @ %d", (void *)&c, lu.x, lu.y, lu.z, lusize);
179     conoutf("\fa x  %.8x", c.faces[0]);
180     conoutf("\fa y  %.8x", c.faces[1]);
181     conoutf("\fa z  %.8x", c.faces[2]);
182 }
183 
184 COMMAND(0, printcube, "");
185 
isvalidcube(const cube & c)186 bool isvalidcube(const cube &c)
187 {
188     clipplanes p;
189     genclipbounds(c, ivec(0, 0, 0), 256, p);
190     genclipplanes(c, ivec(0, 0, 0), 256, p);
191     loopi(8) // test that cube is convex
192     {
193         vec v = p.v[i];
194         loopj(p.size) if(p.p[j].dist(v)>1e-3f) return false;
195     }
196     return true;
197 }
198 
validatec(cube * c,int size)199 void validatec(cube *c, int size)
200 {
201     loopi(8)
202     {
203         if(c[i].children)
204         {
205             if(size<=1)
206             {
207                 solidfaces(c[i]);
208                 discardchildren(c[i], true);
209             }
210             else validatec(c[i].children, size>>1);
211         }
212         else if(size > 0x1000)
213         {
214             subdividecube(c[i], true, false);
215             validatec(c[i].children, size>>1);
216         }
217         else
218         {
219             loopj(3)
220             {
221                 uint f = c[i].faces[j], e0 = f&0x0F0F0F0FU, e1 = (f>>4)&0x0F0F0F0FU;
222                 if(e0 == e1 || ((e1+0x07070707U)|(e1-e0))&0xF0F0F0F0U)
223                 {
224                     emptyfaces(c[i]);
225                     break;
226                 }
227             }
228         }
229     }
230 }
231 
232 ivec lu;
233 int lusize;
lookupcube(const ivec & to,int tsize,ivec & ro,int & rsize)234 cube &lookupcube(const ivec &to, int tsize, ivec &ro, int &rsize)
235 {
236     int tx = clamp(to.x, 0, worldsize-1),
237         ty = clamp(to.y, 0, worldsize-1),
238         tz = clamp(to.z, 0, worldsize-1);
239     int scale = worldscale-1, csize = abs(tsize);
240     cube *c = &worldroot[octastep(tx, ty, tz, scale)];
241     if(!(csize>>scale)) do
242     {
243         if(!c->children)
244         {
245             if(tsize > 0) do
246             {
247                 subdividecube(*c);
248                 scale--;
249                 c = &c->children[octastep(tx, ty, tz, scale)];
250             } while(!(csize>>scale));
251             break;
252         }
253         scale--;
254         c = &c->children[octastep(tx, ty, tz, scale)];
255     } while(!(csize>>scale));
256     ro = ivec(tx, ty, tz).mask(~0U<<scale);
257     rsize = 1<<scale;
258     return *c;
259 }
260 
lookupmaterial(const vec & v)261 int lookupmaterial(const vec &v)
262 {
263     ivec o(v);
264     if(!insideworld(o)) return MAT_AIR;
265     int scale = worldscale-1;
266     cube *c = &worldroot[octastep(o.x, o.y, o.z, scale)];
267     while(c->children)
268     {
269         scale--;
270         c = &c->children[octastep(o.x, o.y, o.z, scale)];
271     }
272     return c->material;
273 }
274 
275 const cube *neighbourstack[32];
276 int neighbourdepth = -1;
277 
neighbourcube(const cube & c,int orient,const ivec & co,int size,ivec & ro,int & rsize)278 const cube &neighbourcube(const cube &c, int orient, const ivec &co, int size, ivec &ro, int &rsize)
279 {
280     ivec n = co;
281     int dim = dimension(orient);
282     uint diff = n[dim];
283     if(dimcoord(orient)) n[dim] += size; else n[dim] -= size;
284     diff ^= n[dim];
285     if(diff >= uint(worldsize)) { ro = n; rsize = size; return emptycube; }
286     int scale = worldscale;
287     const cube *nc = worldroot;
288     if(neighbourdepth >= 0)
289     {
290         scale -= neighbourdepth + 1;
291         diff >>= scale;
292         do { scale++; diff >>= 1; } while(diff);
293         nc = neighbourstack[worldscale - scale];
294     }
295     scale--;
296     nc = &nc[octastep(n.x, n.y, n.z, scale)];
297     if(!(size>>scale) && nc->children) do
298     {
299         scale--;
300         nc = &nc->children[octastep(n.x, n.y, n.z, scale)];
301     } while(!(size>>scale) && nc->children);
302     ro = n.mask(~0U<<scale);
303     rsize = 1<<scale;
304     return *nc;
305 }
306 
307 ////////// (re)mip //////////
308 
getmippedtexture(const cube & p,int orient)309 int getmippedtexture(const cube &p, int orient)
310 {
311     cube *c = p.children;
312     int d = dimension(orient), dc = dimcoord(orient), texs[4] = { -1, -1, -1, -1 }, numtexs = 0;
313     loop(x, 2) loop(y, 2)
314     {
315         int n = octaindex(d, x, y, dc);
316         if(isempty(c[n]))
317         {
318             n = oppositeocta(d, n);
319             if(isempty(c[n]))
320                 continue;
321         }
322         int tex = c[n].texture[orient];
323         if(tex > DEFAULT_SKY) loopi(numtexs) if(texs[i] == tex) return tex;
324         texs[numtexs++] = tex;
325     }
326     loopirev(numtexs) if(!i || texs[i] > DEFAULT_SKY) return texs[i];
327     return DEFAULT_GEOM;
328 }
329 
forcemip(cube & c,bool fixtex)330 void forcemip(cube &c, bool fixtex)
331 {
332     cube *ch = c.children;
333     emptyfaces(c);
334 
335     loopi(8) loopj(8)
336     {
337         int n = i^(j==3 ? 4 : (j==4 ? 3 : j));
338         if(!isempty(ch[n])) // breadth first search for cube near vert
339         {
340             ivec v;
341             getcubevector(ch[n], i, v);
342             // adjust vert to parent size
343             setcubevector(c, i, ivec(n, v, 8).shr(1));
344             break;
345         }
346     }
347 
348     if(fixtex) loopj(6)
349         c.texture[j] = getmippedtexture(c, j);
350 }
351 
midedge(const ivec & a,const ivec & b,int xd,int yd,bool & perfect)352 static int midedge(const ivec &a, const ivec &b, int xd, int yd, bool &perfect)
353 {
354     int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
355     if(ay==by) return ay;
356     if(ax==bx) { perfect = false; return ay; }
357     bool crossx = (ax<8 && bx>8) || (ax>8 && bx<8);
358     bool crossy = (ay<8 && by>8) || (ay>8 && by<8);
359     if(crossy && !crossx) { midedge(a,b,yd,xd,perfect); return 8; } // to test perfection
360     if(ax<=8 && bx<=8) return ax>bx ? ay : by;
361     if(ax>=8 && bx>=8) return ax<bx ? ay : by;
362     int risex = (by-ay)*(8-ax)*256;
363     int s = risex/(bx-ax);
364     int y = s/256 + ay;
365     if(((abs(s)&0xFF)!=0) || // ie: rounding error
366         (crossy && y!=8) ||
367         (y<0 || y>16)) perfect = false;
368     return crossy ? 8 : min(max(y, 0), 16);
369 }
370 
crosscenter(const ivec & a,const ivec & b,int xd,int yd)371 static inline bool crosscenter(const ivec &a, const ivec &b, int xd, int yd)
372 {
373     int ax = a[xd], ay = a[yd], bx = b[xd], by = b[yd];
374     return (((ax <= 8 && bx <= 8) || (ax >= 8 && bx >= 8)) &&
375             ((ay <= 8 && by <= 8) || (ay >= 8 && by >= 8))) ||
376            (ax + bx == 16 && ay + by == 16);
377 }
378 
subdividecube(cube & c,bool fullcheck,bool brighten)379 bool subdividecube(cube &c, bool fullcheck, bool brighten)
380 {
381     if(c.children) return true;
382     if(c.ext) memset(c.ext->surfaces, 0, sizeof(c.ext->surfaces));
383     if(isempty(c) || isentirelysolid(c))
384     {
385         c.children = newcubes(isempty(c) ? F_EMPTY : F_SOLID, c.material);
386         loopi(8)
387         {
388             loopl(6) c.children[i].texture[l] = c.texture[l];
389             if(brighten && !isempty(c)) brightencube(c.children[i]);
390         }
391         return true;
392     }
393     cube *ch = c.children = newcubes(F_SOLID, c.material);
394     bool perfect = true;
395     ivec v[8];
396     loopi(8)
397     {
398         getcubevector(c, i, v[i]);
399         v[i].mul(2);
400     }
401 
402     loopj(6)
403     {
404         int d = dimension(j), z = dimcoord(j);
405         const ivec &v00 = v[octaindex(d, 0, 0, z)],
406                    &v10 = v[octaindex(d, 1, 0, z)],
407                    &v01 = v[octaindex(d, 0, 1, z)],
408                    &v11 = v[octaindex(d, 1, 1, z)];
409         int e[3][3];
410         // corners
411         e[0][0] = v00[d];
412         e[0][2] = v01[d];
413         e[2][0] = v10[d];
414         e[2][2] = v11[d];
415         // edges
416         e[0][1] = midedge(v00, v01, C[d], d, perfect);
417         e[1][0] = midedge(v00, v10, R[d], d, perfect);
418         e[1][2] = midedge(v11, v01, R[d], d, perfect);
419         e[2][1] = midedge(v11, v10, C[d], d, perfect);
420         // center
421         bool p1 = perfect, p2 = perfect;
422         int c1 = midedge(v00, v11, R[d], d, p1);
423         int c2 = midedge(v01, v10, R[d], d, p2);
424         if(z ? c1 > c2 : c1 < c2)
425         {
426             e[1][1] = c1;
427             perfect = p1 && (c1 == c2 || crosscenter(v00, v11, C[d], R[d]));
428         }
429         else
430         {
431             e[1][1] = c2;
432             perfect = p2 && (c1 == c2 || crosscenter(v01, v10, C[d], R[d]));
433         }
434 
435         loopi(8)
436         {
437             ch[i].texture[j] = c.texture[j];
438             int rd = (i>>R[d])&1, cd = (i>>C[d])&1, dd = (i>>D[d])&1;
439             edgeset(cubeedge(ch[i], d, 0, 0), z, clamp(e[rd][cd] - dd*8, 0, 8));
440             edgeset(cubeedge(ch[i], d, 1, 0), z, clamp(e[1+rd][cd] - dd*8, 0, 8));
441             edgeset(cubeedge(ch[i], d, 0, 1), z, clamp(e[rd][1+cd] - dd*8, 0, 8));
442             edgeset(cubeedge(ch[i], d, 1, 1), z, clamp(e[1+rd][1+cd] - dd*8, 0, 8));
443         }
444     }
445 
446     validatec(ch);
447     if(fullcheck) loopi(8) if(!isvalidcube(ch[i])) // not so good...
448     {
449         emptyfaces(ch[i]);
450         perfect=false;
451     }
452     if(brighten) loopi(8) if(!isempty(ch[i])) brightencube(ch[i]);
453     return perfect;
454 }
455 
crushededge(uchar e,int dc)456 bool crushededge(uchar e, int dc) { return dc ? e==0 : e==0x88; }
457 
visibleorient(const cube & c,int orient)458 int visibleorient(const cube &c, int orient)
459 {
460     loopi(2)
461     {
462         int a = faceedgesidx[orient][i*2 + 0];
463         int b = faceedgesidx[orient][i*2 + 1];
464         loopj(2)
465         {
466             if(crushededge(c.edges[a],j) &&
467                crushededge(c.edges[b],j) &&
468                 touchingface(c, orient)) return ((a>>2)<<1) + j;
469         }
470     }
471     return orient;
472 }
473 
474 VAR(0, mipvis, 0, 0, 1);
475 
476 static int remipprogress = 0, remiptotal = 0;
477 
remip(cube & c,const ivec & co,int size)478 bool remip(cube &c, const ivec &co, int size)
479 {
480     cube *ch = c.children;
481     if(!ch)
482     {
483         if(size<<1 <= 0x1000) return true;
484         subdividecube(c);
485         ch = c.children;
486     }
487     else if((remipprogress++&0xFFF)==1) progress(float(remipprogress)/remiptotal, "Remipping...");
488 
489     bool perfect = true;
490     loopi(8)
491     {
492         ivec o(i, co, size);
493         if(!remip(ch[i], o, size>>1)) perfect = false;
494     }
495 
496     solidfaces(c); // so texmip is more consistent
497     loopj(6)
498         c.texture[j] = getmippedtexture(c, j); // parents get child texs regardless
499 
500     if(!perfect) return false;
501     if(size<<1 > 0x1000) return false;
502 
503     ushort mat = MAT_AIR;
504     loopi(8)
505     {
506         mat = ch[i].material;
507         if((mat&MATF_CLIP) == MAT_NOCLIP || mat&MAT_ALPHA)
508         {
509             if(i > 0) return false;
510             while(++i < 8) if(ch[i].material != mat) return false;
511             break;
512         }
513         else if(!isentirelysolid(ch[i]))
514         {
515             while(++i < 8)
516             {
517                 int omat = ch[i].material;
518                 if(isentirelysolid(ch[i]) ? (omat&MATF_CLIP) == MAT_NOCLIP || omat&MAT_ALPHA : mat != omat) return false;
519             }
520             break;
521         }
522     }
523 
524     cube n = c;
525     n.ext = NULL;
526     forcemip(n);
527     n.children = NULL;
528     if(!subdividecube(n, false, false))
529         { freeocta(n.children); return false; }
530 
531     cube *nh = n.children;
532     uchar vis[6] = {0, 0, 0, 0, 0, 0};
533     loopi(8)
534     {
535         if(ch[i].faces[0] != nh[i].faces[0] ||
536            ch[i].faces[1] != nh[i].faces[1] ||
537            ch[i].faces[2] != nh[i].faces[2])
538             { freeocta(nh); return false; }
539 
540         if(isempty(ch[i]) && isempty(nh[i])) continue;
541 
542         ivec o(i, co, size);
543         loop(orient, 6)
544             if(visibleface(ch[i], orient, o, size, MAT_AIR, (mat&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA))
545             {
546                 if(ch[i].texture[orient] != n.texture[orient]) { freeocta(nh); return false; }
547                 vis[orient] |= 1<<i;
548             }
549     }
550     if(mipvis) loop(orient, 6)
551     {
552         int mask = 0;
553         loop(x, 2) loop(y, 2) mask |= 1<<octaindex(dimension(orient), x, y, dimcoord(orient));
554         if(vis[orient]&mask && (vis[orient]&mask)!=mask) { freeocta(nh); return false; }
555     }
556 
557     freeocta(nh);
558     discardchildren(c);
559     loopi(3) c.faces[i] = n.faces[i];
560     c.material = mat;
561     loopi(6) if(vis[i]) c.visible |= 1<<i;
562     if(c.visible) c.visible |= 0x40;
563     brightencube(c);
564     return true;
565 }
566 
remip()567 void remip()
568 {
569     remipprogress = 1;
570     remiptotal = allocnodes;
571     loopi(8)
572     {
573         ivec o(i, ivec(0, 0, 0), worldsize>>1);
574         remip(worldroot[i], o, worldsize>>2);
575     }
576     calcmerges();
577 }
578 
mpremip(bool local)579 void mpremip(bool local)
580 {
581     if(local) client::edittrigger(sel, EDIT_REMIP);
582     remip();
583     allchanged();
584 }
585 
586 ICOMMAND(0, remip, "", (), mpremip(true));
587 
588 const ivec cubecoords[8] = // verts of bounding cube
589 {
590 #define GENCUBEVERT(n, x, y, z) ivec(x, y, z),
591     GENCUBEVERTS(0, 8, 0, 8, 0, 8)
592 #undef GENCUBEVERT
593 };
594 
595 template<class T>
gencubevert(const cube & c,int i,T & v)596 static inline void gencubevert(const cube &c, int i, T &v)
597 {
598     switch(i)
599     {
600         default:
601 #define GENCUBEVERT(n, x, y, z) \
602         case n: \
603             v = T(edgeget(cubeedge(c, 0, y, z), x), \
604                   edgeget(cubeedge(c, 1, z, x), y), \
605                   edgeget(cubeedge(c, 2, x, y), z)); \
606             break;
607         GENCUBEVERTS(0, 1, 0, 1, 0, 1)
608 #undef GENCUBEVERT
609     }
610 }
611 
genfaceverts(const cube & c,int orient,ivec v[4])612 void genfaceverts(const cube &c, int orient, ivec v[4])
613 {
614     switch(orient)
615     {
616         default:
617 #define GENFACEORIENT(o, v0, v1, v2, v3) \
618         case o: v0 v1 v2 v3 break;
619 #define GENFACEVERT(o, n, x,y,z, xv,yv,zv) \
620             v[n] = ivec(edgeget(cubeedge(c, 0, y, z), x), \
621                         edgeget(cubeedge(c, 1, z, x), y), \
622                         edgeget(cubeedge(c, 2, x, y), z));
623         GENFACEVERTS(0, 1, 0, 1, 0, 1, , , , , , )
624     #undef GENFACEORIENT
625     #undef GENFACEVERT
626     }
627 }
628 
629 const ivec facecoords[6][4] =
630 {
631 #define GENFACEORIENT(o, v0, v1, v2, v3) \
632     { v0, v1, v2, v3 },
633 #define GENFACEVERT(o, n, x,y,z, xv,yv,zv) \
634         ivec(x,y,z)
635     GENFACEVERTS(0, 8, 0, 8, 0, 8, , , , , , )
636 #undef GENFACEORIENT
637 #undef GENFACEVERT
638 };
639 
640 const uchar fv[6][4] = // indexes for cubecoords, per each vert of a face orientation
641 {
642     { 2, 1, 6, 5 },
643     { 3, 4, 7, 0 },
644     { 4, 5, 6, 7 },
645     { 1, 2, 3, 0 },
646     { 6, 1, 0, 7 },
647     { 5, 4, 3, 2 },
648 };
649 
650 const uchar fvmasks[64] = // mask of verts used given a mask of visible face orientations
651 {
652     0x00, 0x66, 0x99, 0xFF, 0xF0, 0xF6, 0xF9, 0xFF,
653     0x0F, 0x6F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
654     0xC3, 0xE7, 0xDB, 0xFF, 0xF3, 0xF7, 0xFB, 0xFF,
655     0xCF, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
656     0x3C, 0x7E, 0xBD, 0xFF, 0xFC, 0xFE, 0xFD, 0xFF,
657     0x3F, 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
658     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
659     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
660 };
661 
662 const uchar faceedgesidx[6][4] = // ordered edges surrounding each orient
663 {//0..1 = row edges, 2..3 = column edges
664     { 4,  5,  8, 10 },
665     { 6,  7,  9, 11 },
666     { 8,  9,  0, 2 },
667     { 10, 11, 1, 3 },
668     { 0,  1,  4, 6 },
669     { 2,  3,  5, 7 },
670 };
671 
flataxisface(const cube & c,int orient)672 bool flataxisface(const cube &c, int orient)
673 {
674     uint face = c.faces[dimension(orient)];
675     if(dimcoord(orient)) face >>= 4;
676     return (face&0x0F0F0F0F) == 0x01010101*(face&0x0F);
677 }
678 
collideface(const cube & c,int orient)679 bool collideface(const cube &c, int orient)
680 {
681     if(flataxisface(c, orient))
682     {
683         uchar r1 = c.edges[faceedgesidx[orient][0]], r2 = c.edges[faceedgesidx[orient][1]];
684         if(uchar((r1>>4)|(r2&0xF0)) == uchar((r1&0x0F)|(r2<<4))) return false;
685         uchar c1 = c.edges[faceedgesidx[orient][2]], c2 = c.edges[faceedgesidx[orient][3]];
686         if(uchar((c1>>4)|(c2&0xF0)) == uchar((c1&0x0F)|(c2<<4))) return false;
687     }
688     return true;
689 }
690 
touchingface(const cube & c,int orient)691 bool touchingface(const cube &c, int orient)
692 {
693     uint face = c.faces[dimension(orient)];
694     return dimcoord(orient) ? (face&0xF0F0F0F0)==0x80808080 : (face&0x0F0F0F0F)==0;
695 }
696 
notouchingface(const cube & c,int orient)697 bool notouchingface(const cube &c, int orient)
698 {
699     uint face = c.faces[dimension(orient)];
700     return dimcoord(orient) ? (face&0x80808080)==0 : ((0x88888888-face)&0x08080808) == 0;
701 }
702 
faceconvexity(const ivec v[4])703 int faceconvexity(const ivec v[4])
704 {
705     ivec n;
706     n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
707     return ivec(v[0]).sub(v[3]).dot(n);
708     // 1 if convex, -1 if concave, 0 if flat
709 }
710 
faceconvexity(const vertinfo * verts,int numverts,int size)711 int faceconvexity(const vertinfo *verts, int numverts, int size)
712 {
713     if(numverts < 4) return 0;
714     ivec v0 = verts[0].getxyz(),
715          e1 = verts[1].getxyz().sub(v0),
716          e2 = verts[2].getxyz().sub(v0),
717          n;
718     if(size >= (8<<5))
719     {
720         if(size >= (8<<10)) n.cross(e1.shr(10), e2.shr(10));
721         else n.cross(e1, e2).shr(10);
722     }
723     else n.cross(e1, e2);
724     return verts[3].getxyz().sub(v0).dot(n);
725 }
726 
faceconvexity(const ivec v[4],int & vis)727 int faceconvexity(const ivec v[4], int &vis)
728 {
729     ivec e1, e2, e3, n;
730     n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
731     int convex = (e3 = v[0]).sub(v[3]).dot(n);
732     if(!convex)
733     {
734         if(ivec().cross(e3, e2).iszero()) { if(!n.iszero()) vis = 1; }
735         else if(n.iszero()) { vis = 2; }
736         return 0;
737     }
738     return convex;
739 }
740 
faceconvexity(const cube & c,int orient)741 int faceconvexity(const cube &c, int orient)
742 {
743     if(flataxisface(c, orient)) return 0;
744     ivec v[4];
745     genfaceverts(c, orient, v);
746     return faceconvexity(v);
747 }
748 
faceorder(const cube & c,int orient)749 int faceorder(const cube &c, int orient) // gets above 'fv' so that each face is convex
750 {
751     return faceconvexity(c, orient)<0 ? 1 : 0;
752 }
753 
faceedges(const cube & c,int orient,uchar edges[4])754 static inline void faceedges(const cube &c, int orient, uchar edges[4])
755 {
756     loopk(4) edges[k] = c.edges[faceedgesidx[orient][k]];
757 }
758 
faceedges(const cube & c,int orient)759 uint faceedges(const cube &c, int orient)
760 {
761     union { uchar edges[4]; uint face; } u;
762     faceedges(c, orient, u.edges);
763     return u.face;
764 }
765 
genfacevecs(const cube & cu,int orient,const ivec & pos,int size,bool solid,ivec2 * fvecs,const ivec * v=NULL)766 static inline int genfacevecs(const cube &cu, int orient, const ivec &pos, int size, bool solid, ivec2 *fvecs, const ivec *v = NULL)
767 {
768     int i = 0;
769     if(solid)
770     {
771         switch(orient)
772         {
773         #define GENFACEORIENT(orient, v0, v1, v2, v3) \
774             case orient: \
775             { \
776                 if(dimcoord(orient)) { v0 v1 v2 v3 } else { v3 v2 v1 v0 } \
777                 break; \
778             }
779         #define GENFACEVERT(orient, vert, xv,yv,zv, x,y,z) \
780             { ivec2 &f = fvecs[i]; x ((xv)<<3); y ((yv)<<3); z ((zv)<<3); i++; }
781             GENFACEVERTS(pos.x, pos.x+size, pos.y, pos.y+size, pos.z, pos.z+size, f.x = , f.x = , f.y = , f.y = , (void), (void))
782         #undef GENFACEVERT
783         }
784         return 4;
785     }
786     ivec buf[4];
787     if(!v) { genfaceverts(cu, orient, buf); v = buf; }
788     ivec2 prev(INT_MAX, INT_MAX);
789     switch(orient)
790     {
791     #define GENFACEVERT(orient, vert, sx,sy,sz, dx,dy,dz) \
792         { \
793             const ivec &e = v[vert]; \
794             ivec ef; \
795             ef.dx = e.sx; ef.dy = e.sy; ef.dz = e.sz; \
796             if(ef.z == dimcoord(orient)*8) \
797             { \
798                 ivec2 &f = fvecs[i]; \
799                 ivec pf; \
800                 pf.dx = pos.sx; pf.dy = pos.sy; pf.dz = pos.sz; \
801                 f = ivec2(ef.x*size + (pf.x<<3), ef.y*size + (pf.y<<3)); \
802                 if(f != prev) { prev = f; i++; } \
803             } \
804         }
805         GENFACEVERTS(x, x, y, y, z, z, x, x, y, y, z, z)
806     #undef GENFACEORIENT
807     #undef GENFACEVERT
808     }
809     if(fvecs[0] == prev) i--;
810     return i;
811 }
812 
clipfacevecy(const ivec2 & o,const ivec2 & dir,int cx,int cy,int size,ivec2 & r)813 static inline int clipfacevecy(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
814 {
815     if(dir.x >= 0)
816     {
817         if(cx <= o.x || cx >= o.x+dir.x) return 0;
818     }
819     else if(cx <= o.x+dir.x || cx >= o.x) return 0;
820 
821     int t = (o.y-cy) + (cx-o.x)*dir.y/dir.x;
822     if(t <= 0 || t >= size) return 0;
823 
824     r.x = cx;
825     r.y = cy + t;
826     return 1;
827 }
828 
clipfacevecx(const ivec2 & o,const ivec2 & dir,int cx,int cy,int size,ivec2 & r)829 static inline int clipfacevecx(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 &r)
830 {
831     if(dir.y >= 0)
832     {
833         if(cy <= o.y || cy >= o.y+dir.y) return 0;
834     }
835     else if(cy <= o.y+dir.y || cy >= o.y) return 0;
836 
837     int t = (o.x-cx) + (cy-o.y)*dir.x/dir.y;
838     if(t <= 0 || t >= size) return 0;
839 
840     r.x = cx + t;
841     r.y = cy;
842     return 1;
843 }
844 
clipfacevec(const ivec2 & o,const ivec2 & dir,int cx,int cy,int size,ivec2 * rvecs)845 static inline int clipfacevec(const ivec2 &o, const ivec2 &dir, int cx, int cy, int size, ivec2 *rvecs)
846 {
847     int r = 0;
848 
849     if(o.x >= cx && o.x <= cx+size &&
850        o.y >= cy && o.y <= cy+size &&
851        ((o.x != cx && o.x != cx+size) || (o.y != cy && o.y != cy+size)))
852     {
853         rvecs[0] = o;
854         r++;
855     }
856 
857     r += clipfacevecx(o, dir, cx, cy, size, rvecs[r]);
858     r += clipfacevecx(o, dir, cx, cy+size, size, rvecs[r]);
859     r += clipfacevecy(o, dir, cx, cy, size, rvecs[r]);
860     r += clipfacevecy(o, dir, cx+size, cy, size, rvecs[r]);
861 
862     ASSERT(r <= 2);
863     return r;
864 }
865 
insideface(const ivec2 * p,int nump,const ivec2 * o,int numo)866 static inline bool insideface(const ivec2 *p, int nump, const ivec2 *o, int numo)
867 {
868     int bounds = 0;
869     ivec2 prev = o[numo-1];
870     loopi(numo)
871     {
872         const ivec2 &cur = o[i];
873         ivec2 dir = ivec2(cur).sub(prev);
874         int offset = dir.cross(prev);
875         loopj(nump) if(dir.cross(p[j]) > offset) return false;
876         bounds++;
877         prev = cur;
878     }
879     return bounds>=3;
880 }
881 
clipfacevecs(const ivec2 * o,int numo,int cx,int cy,int size,ivec2 * rvecs)882 static inline int clipfacevecs(const ivec2 *o, int numo, int cx, int cy, int size, ivec2 *rvecs)
883 {
884     cx <<= 3;
885     cy <<= 3;
886     size <<= 3;
887 
888     int r = 0;
889     ivec2 prev = o[numo-1];
890     loopi(numo)
891     {
892         const ivec2 &cur = o[i];
893         r += clipfacevec(prev, ivec2(cur).sub(prev), cx, cy, size, &rvecs[r]);
894         prev = cur;
895     }
896     ivec2 corner[4] = {ivec2(cx, cy), ivec2(cx+size, cy), ivec2(cx+size, cy+size), ivec2(cx, cy+size)};
897     loopi(4) if(insideface(&corner[i], 1, o, numo)) rvecs[r++] = corner[i];
898     ASSERT(r <= 8);
899     return r;
900 }
901 
collapsedface(const cube & c,int orient)902 bool collapsedface(const cube &c, int orient)
903 {
904     int e0 = c.edges[faceedgesidx[orient][0]], e1 = c.edges[faceedgesidx[orient][1]],
905         e2 = c.edges[faceedgesidx[orient][2]], e3 = c.edges[faceedgesidx[orient][3]],
906         face = dimension(orient)*4,
907         f0 = c.edges[face+0], f1 = c.edges[face+1],
908         f2 = c.edges[face+2], f3 = c.edges[face+3];
909     if(dimcoord(orient)) { f0 >>= 4; f1 >>= 4; f2 >>= 4; f3 >>= 4; }
910     else { f0 &= 0xF; f1 &= 0xF; f2 &= 0xF; f3 &= 0xF; }
911     ivec v0(e0&0xF, e2&0xF, f0),
912          v1(e0>>4, e3&0xF, f1),
913          v2(e1>>4, e3>>4, f3),
914          v3(e1&0xF, e2>>4, f2);
915     return ivec().cross(v1.sub(v0), v2.sub(v0)).iszero() &&
916            ivec().cross(v2, v3.sub(v0)).iszero();
917 }
918 
occludesface(const cube & c,int orient,const ivec & o,int size,const ivec & vo,int vsize,ushort vmat,ushort nmat,ushort matmask,const ivec2 * vf,int numv)919 static inline bool occludesface(const cube &c, int orient, const ivec &o, int size, const ivec &vo, int vsize, ushort vmat, ushort nmat, ushort matmask, const ivec2 *vf, int numv)
920 {
921     int dim = dimension(orient);
922     if(!c.children)
923     {
924         if(c.material)
925         {
926             if(nmat != MAT_AIR && (c.material&matmask) == nmat)
927             {
928                 ivec2 nf[8];
929                 return clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, nf) < 3;
930             }
931             if(vmat != MAT_AIR && ((c.material&matmask) == vmat || (isliquid(vmat) && isclipped(c.material&MATF_VOLUME)))) return true;
932         }
933         if(isentirelysolid(c)) return true;
934         if(touchingface(c, orient) && faceedges(c, orient) == F_SOLID) return true;
935         ivec2 cf[8];
936         int numc = clipfacevecs(vf, numv, o[C[dim]], o[R[dim]], size, cf);
937         if(numc < 3) return true;
938         if(isempty(c) || notouchingface(c, orient)) return false;
939         ivec2 of[4];
940         int numo = genfacevecs(c, orient, o, size, false, of);
941         return numo >= 3 && insideface(cf, numc, of, numo);
942     }
943 
944     size >>= 1;
945     int coord = dimcoord(orient);
946     loopi(8) if(octacoord(dim, i) == coord)
947     {
948         if(!occludesface(c.children[i], orient, ivec(i, o, size), size, vo, vsize, vmat, nmat, matmask, vf, numv)) return false;
949     }
950 
951     return true;
952 }
953 
visibleface(const cube & c,int orient,const ivec & co,int size,ushort mat,ushort nmat,ushort matmask)954 bool visibleface(const cube &c, int orient, const ivec &co, int size, ushort mat, ushort nmat, ushort matmask)
955 {
956     if(mat != MAT_AIR)
957     {
958         if(mat != MAT_CLIP && faceedges(c, orient)==F_SOLID && touchingface(c, orient)) return false;
959     }
960     else
961     {
962         if(collapsedface(c, orient)) return false;
963         if(!touchingface(c, orient)) return true;
964     }
965 
966     ivec no;
967     int nsize;
968     const cube &o = neighbourcube(c, orient, co, size, no, nsize);
969 
970     int opp = opposite(orient);
971     if(nsize > size || (nsize == size && !o.children))
972     {
973         if(o.material)
974         {
975             if(nmat != MAT_AIR && (o.material&matmask) == nmat) return true;
976             if(mat != MAT_AIR && ((o.material&matmask) == mat || (isliquid(mat) && isclipped(o.material&MATF_VOLUME)))) return false;
977         }
978         if(isentirelysolid(o)) return false;
979         if(isempty(o) || notouchingface(o, opp)) return true;
980         if(touchingface(o, opp) && faceedges(o, opp) == F_SOLID) return false;
981 
982         ivec vo = ivec(co).mask(0xFFF);
983         no.mask(0xFFF);
984         ivec2 cf[4], of[4];
985         int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf),
986             numo = genfacevecs(o, opp, no, nsize, false, of);
987         return numo < 3 || !insideface(cf, numc, of, numo);
988     }
989 
990     ivec vo = ivec(co).mask(0xFFF);
991     no.mask(0xFFF);
992     ivec2 cf[4];
993     int numc = genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf);
994     return !occludesface(o, opp, no, nsize, vo, size, mat, nmat, matmask, cf, numc);
995 }
996 
classifyface(const cube & c,int orient,const ivec & co,int size)997 int classifyface(const cube &c, int orient, const ivec &co, int size)
998 {
999     int vismask = 2, forcevis = 0;
1000     bool solid = false;
1001     switch(c.material&MATF_CLIP)
1002     {
1003     case MAT_NOCLIP: vismask = 0; break;
1004     case MAT_CLIP: solid = true; break;
1005     }
1006     if(isempty(c) || collapsedface(c, orient))
1007     {
1008         if(!vismask) return 0;
1009     }
1010     else if(!touchingface(c, orient))
1011     {
1012         forcevis = 1;
1013         if(!solid)
1014         {
1015             if(vismask && collideface(c, orient)) forcevis |= 2;
1016             return forcevis;
1017         }
1018     }
1019     else vismask |= 1;
1020 
1021     ivec no;
1022     int nsize;
1023     const cube &o = neighbourcube(c, orient, co, size, no, nsize);
1024     if(&o==&c) return 0;
1025 
1026     int opp = opposite(orient);
1027     if(nsize > size || (nsize == size && !o.children))
1028     {
1029         if(o.material)
1030         {
1031             if((~c.material & o.material) & MAT_ALPHA) { forcevis |= vismask&1; vismask &= ~1; }
1032             switch(o.material&MATF_CLIP)
1033             {
1034                 case MAT_CLIP: vismask &= ~2; break;
1035                 case MAT_NOCLIP: forcevis |= vismask&2; vismask &= ~2; break;
1036             }
1037         }
1038         if(vismask && !isentirelysolid(o))
1039         {
1040             if(isempty(o) || notouchingface(o, opp)) forcevis |= vismask;
1041             else if(!touchingface(o, opp) || faceedges(o, opp) != F_SOLID)
1042             {
1043                 ivec vo = ivec(co).mask(0xFFF);
1044                 no.mask(0xFFF);
1045                 ivec2 cf[4], of[4];
1046                 int numo = genfacevecs(o, opp, no, nsize, false, of);
1047                 if(numo < 3) forcevis |= vismask;
1048                 else
1049                 {
1050                     int numc = 0;
1051                     if(vismask&2 && solid)
1052                     {
1053                         numc = genfacevecs(c, orient, vo, size, true, cf);
1054                         if(!insideface(cf, numc, of, numo)) forcevis |= 2;
1055                         vismask &= ~2;
1056                     }
1057                     if(vismask)
1058                     {
1059                         numc = genfacevecs(c, orient, vo, size, false, cf);
1060                         if(!insideface(cf, numc, of, numo)) forcevis |= vismask;
1061                     }
1062                 }
1063             }
1064         }
1065     }
1066     else
1067     {
1068         ivec vo = ivec(co).mask(0xFFF);
1069         no.mask(0xFFF);
1070         ivec2 cf[4];
1071         int numc = 0;
1072         if(vismask&1)
1073         {
1074             numc = genfacevecs(c, orient, vo, size, false, cf);
1075             if(!occludesface(o, opp, no, nsize, vo, size, MAT_AIR, (c.material&MAT_ALPHA)^MAT_ALPHA, MAT_ALPHA, cf, numc)) forcevis |= 1;
1076         }
1077         if(vismask&2)
1078         {
1079             if(!numc || solid) numc = genfacevecs(c, orient, vo, size, solid, cf);
1080             if(!occludesface(o, opp, no, nsize, vo, size, MAT_CLIP, MAT_NOCLIP, MATF_CLIP, cf, numc)) forcevis |= 2;
1081         }
1082     }
1083 
1084     if(forcevis&2 && !solid && !collideface(c, orient)) forcevis &= ~2;
1085     return forcevis;
1086 }
1087 
1088 // more expensive version that checks both triangles of a face independently
visibletris(const cube & c,int orient,const ivec & co,int size,ushort vmat,ushort nmat,ushort matmask)1089 int visibletris(const cube &c, int orient, const ivec &co, int size, ushort vmat, ushort nmat, ushort matmask)
1090 {
1091     int vis = 3, touching = 0xF;
1092     ivec v[4], e1, e2, e3, n;
1093     genfaceverts(c, orient, v);
1094     n.cross((e1 = v[1]).sub(v[0]), (e2 = v[2]).sub(v[0]));
1095     int convex = (e3 = v[0]).sub(v[3]).dot(n);
1096     if(!convex)
1097     {
1098         if(ivec().cross(e3, e2).iszero() || v[1] == v[3]) { if(n.iszero()) return 0; vis = 1; touching = 0xF&~(1<<3); }
1099         else if(n.iszero()) { vis = 2; touching = 0xF&~(1<<1); }
1100     }
1101 
1102     int dim = dimension(orient), coord = dimcoord(orient);
1103     if(v[0][dim] != coord*8) touching &= ~(1<<0);
1104     if(v[1][dim] != coord*8) touching &= ~(1<<1);
1105     if(v[2][dim] != coord*8) touching &= ~(1<<2);
1106     if(v[3][dim] != coord*8) touching &= ~(1<<3);
1107     static const int notouchmasks[2][16] = // mask of triangles not touching
1108     { // order 0: flat or convex
1109        // 0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15
1110         { 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 3, 3, 3, 1, 3, 0 },
1111       // order 1: concave
1112         { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 2, 0 },
1113     };
1114     int order = convex < 0 ? 1 : 0, notouch = notouchmasks[order][touching];
1115     if((vis&notouch)==vis) return vis;
1116 
1117     ivec no;
1118     int nsize;
1119     const cube &o = neighbourcube(c, orient, co, size, no, nsize);
1120 
1121     if((c.material&matmask) == nmat) nmat = MAT_AIR;
1122 
1123     ivec vo = ivec(co).mask(0xFFF);
1124     no.mask(0xFFF);
1125     ivec2 cf[4], of[4];
1126     int opp = opposite(orient), numo = 0, numc;
1127     if(nsize > size || (nsize == size && !o.children))
1128     {
1129         if(o.material)
1130         {
1131             if(vmat != MAT_AIR && (o.material&matmask) == vmat) return vis&notouch;
1132             if(nmat != MAT_AIR && (o.material&matmask) == nmat) return vis;
1133         }
1134         if(isempty(o) || notouchingface(o, opp)) return vis;
1135         if(isentirelysolid(o) || (touchingface(o, opp) && faceedges(o, opp) == F_SOLID)) return vis&notouch;
1136 
1137         numc = genfacevecs(c, orient, vo, size, false, cf, v);
1138         numo = genfacevecs(o, opp, no, nsize, false, of);
1139         if(numo < 3) return vis;
1140         if(insideface(cf, numc, of, numo)) return vis&notouch;
1141     }
1142     else
1143     {
1144         numc = genfacevecs(c, orient, vo, size, false, cf, v);
1145         if(occludesface(o, opp, no, nsize, vo, size, vmat, nmat, matmask, cf, numc)) return vis&notouch;
1146     }
1147     if(vis != 3 || notouch) return vis;
1148 
1149     static const int triverts[2][2][2][3] =
1150     { // order
1151         { // coord
1152             { { 1, 2, 3 }, { 0, 1, 3 } }, // verts
1153             { { 0, 1, 2 }, { 0, 2, 3 } }
1154         },
1155         { // coord
1156             { { 0, 1, 2 }, { 3, 0, 2 } }, // verts
1157             { { 1, 2, 3 }, { 1, 3, 0 } }
1158         }
1159     };
1160 
1161     do
1162     {
1163         loopi(2)
1164         {
1165             const int *verts = triverts[order][coord][i];
1166             ivec2 tf[3] = { cf[verts[0]], cf[verts[1]], cf[verts[2]] };
1167             if(numo > 0) { if(!insideface(tf, 3, of, numo)) continue; }
1168             else if(!occludesface(o, opp, no, nsize, vo, size, vmat, nmat, matmask, tf, 3)) continue;
1169             return vis & ~(1<<i);
1170         }
1171         vis |= 4;
1172     } while(++order <= 1);
1173 
1174     return 3;
1175 }
1176 
calcvert(const cube & c,const ivec & co,int size,ivec & v,int i,bool solid)1177 void calcvert(const cube &c, const ivec &co, int size, ivec &v, int i, bool solid)
1178 {
1179     if(solid) v = cubecoords[i]; else gencubevert(c, i, v);
1180     // avoid overflow
1181     if(size>=8) v.mul(size/8);
1182     else v.div(8/size);
1183     v.add(ivec(co).shl(3));
1184 }
1185 
calcvert(const cube & c,const ivec & co,int size,vec & v,int i,bool solid)1186 void calcvert(const cube &c, const ivec &co, int size, vec &v, int i, bool solid)
1187 {
1188     if(solid) v = vec(cubecoords[i]); else gencubevert(c, i, v);
1189     v.mul(size/8.0f).add(vec(co));
1190 }
1191 
genclipbounds(const cube & c,const ivec & co,int size,clipplanes & p)1192 void genclipbounds(const cube &c, const ivec &co, int size, clipplanes &p)
1193 {
1194     // generate tight bounding box
1195     calcvert(c, co, size, p.v[0], 0);
1196     vec mx = p.v[0], mn = p.v[0];
1197     for(int i = 1; i < 8; i++)
1198     {
1199         calcvert(c, co, size, p.v[i], i);
1200         mx.max(p.v[i]);
1201         mn.min(p.v[i]);
1202     }
1203 
1204     p.r = mx.sub(mn).mul(0.5f);
1205     p.o = mn.add(p.r);
1206 
1207     p.size = 0;
1208     p.visible = 0x80;
1209 }
1210 
genclipplane(const cube & c,int orient,vec * v,plane * clip)1211 int genclipplane(const cube &c, int orient, vec *v, plane *clip)
1212 {
1213     int planes = 0, convex = faceconvexity(c, orient), order = convex < 0 ? 1 : 0;
1214     const vec &v0 = v[fv[orient][order]], &v1 = v[fv[orient][order+1]], &v2 = v[fv[orient][order+2]], &v3 = v[fv[orient][(order+3)&3]];
1215     if(v0==v2) return 0;
1216     if(v0!=v1 && v1!=v2) clip[planes++].toplane(v0, v1, v2);
1217     if(v0!=v3 && v2!=v3 && (!planes || convex)) clip[planes++].toplane(v0, v2, v3);
1218     return planes;
1219 }
1220 
genclipplanes(const cube & c,const ivec & co,int size,clipplanes & p,bool collide,bool noclip)1221 void genclipplanes(const cube &c, const ivec &co, int size, clipplanes &p, bool collide, bool noclip)
1222 {
1223     p.visible &= ~0x80;
1224     if(collide || (c.visible&0xC0) == 0x40)
1225     {
1226         loopi(6) if(c.visible&(1<<i))
1227         {
1228             int vis;
1229             if(flataxisface(c, i)) p.visible |= 1<<i;
1230             else if((vis = visibletris(c, i, co, size, MAT_CLIP, MAT_NOCLIP, MATF_CLIP)))
1231             {
1232                 int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
1233                 const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
1234                 if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
1235                 if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
1236             }
1237         }
1238     }
1239     else if(c.visible&0x80)
1240     {
1241         ushort nmat = MAT_ALPHA, matmask = MAT_ALPHA;
1242         if(noclip) { nmat = MAT_NOCLIP; matmask = MATF_CLIP; }
1243         int vis;
1244         loopi(6) if((vis = visibletris(c, i, co, size, MAT_AIR, nmat, matmask)))
1245         {
1246             if(flataxisface(c, i)) p.visible |= 1<<i;
1247             else
1248             {
1249                 int convex = faceconvexity(c, i), order = vis&4 || convex < 0 ? 1 : 0;
1250                 const vec &v0 = p.v[fv[i][order]], &v1 = p.v[fv[i][order+1]], &v2 = p.v[fv[i][order+2]], &v3 = p.v[fv[i][(order+3)&3]];
1251                 if(vis&1) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v1, v2); }
1252                 if(vis&2 && (!(vis&1) || convex)) { p.side[p.size] = i; p.p[p.size++].toplane(v0, v2, v3); }
1253             }
1254         }
1255     }
1256 }
1257 
mergefacecmp(const facebounds & x,const facebounds & y)1258 static inline bool mergefacecmp(const facebounds &x, const facebounds &y)
1259 {
1260     if(x.v2 < y.v2) return true;
1261     if(x.v2 > y.v2) return false;
1262     if(x.u1 < y.u1) return true;
1263     if(x.u1 > y.u1) return false;
1264     return false;
1265 }
1266 
mergefacev(int orient,facebounds * m,int sz,facebounds & n)1267 static int mergefacev(int orient, facebounds *m, int sz, facebounds &n)
1268 {
1269     for(int i = sz-1; i >= 0; --i)
1270     {
1271         if(m[i].v2 < n.v1) break;
1272         if(m[i].v2 == n.v1 && m[i].u1 == n.u1 && m[i].u2 == n.u2)
1273         {
1274             n.v1 = m[i].v1;
1275             memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(facebounds));
1276             return 1;
1277         }
1278     }
1279     return 0;
1280 }
1281 
mergefaceu(int orient,facebounds & m,facebounds & n)1282 static int mergefaceu(int orient, facebounds &m, facebounds &n)
1283 {
1284     if(m.v1 == n.v1 && m.v2 == n.v2 && m.u2 == n.u1)
1285     {
1286         n.u1 = m.u1;
1287         return 1;
1288     }
1289     return 0;
1290 }
1291 
mergeface(int orient,facebounds * m,int sz,facebounds & n)1292 static int mergeface(int orient, facebounds *m, int sz, facebounds &n)
1293 {
1294     for(bool merged = false; sz; merged = true)
1295     {
1296         int vmerged = mergefacev(orient, m, sz, n);
1297         sz -= vmerged;
1298         if(!vmerged && merged) break;
1299         if(!sz) break;
1300         int umerged = mergefaceu(orient, m[sz-1], n);
1301         sz -= umerged;
1302         if(!umerged) break;
1303     }
1304     m[sz++] = n;
1305     return sz;
1306 }
1307 
mergefaces(int orient,facebounds * m,int sz)1308 int mergefaces(int orient, facebounds *m, int sz)
1309 {
1310     quicksort(m, sz, mergefacecmp);
1311 
1312     int nsz = 0;
1313     loopi(sz) nsz = mergeface(orient, m, nsz, m[i]);
1314     return nsz;
1315 }
1316 
1317 struct cfkey
1318 {
1319     uchar orient;
1320     ushort material, tex;
1321     ivec n;
1322     int offset;
1323 };
1324 
htcmp(const cfkey & x,const cfkey & y)1325 static inline bool htcmp(const cfkey &x, const cfkey &y)
1326 {
1327     return x.orient == y.orient && x.tex == y.tex && x.n == y.n && x.offset == y.offset && x.material==y.material;
1328 }
1329 
hthash(const cfkey & k)1330 static inline uint hthash(const cfkey &k)
1331 {
1332     return hthash(k.n)^k.offset^k.tex^k.orient^k.material;
1333 }
1334 
mincubeface(const cube & cu,int orient,const ivec & o,int size,const facebounds & orig,facebounds & cf,ushort nmat,ushort matmask)1335 void mincubeface(const cube &cu, int orient, const ivec &o, int size, const facebounds &orig, facebounds &cf, ushort nmat, ushort matmask)
1336 {
1337     int dim = dimension(orient);
1338     if(cu.children)
1339     {
1340         size >>= 1;
1341         int coord = dimcoord(orient);
1342         loopi(8) if(octacoord(dim, i) == coord)
1343             mincubeface(cu.children[i], orient, ivec(i, o, size), size, orig, cf, nmat, matmask);
1344         return;
1345     }
1346     int c = C[dim], r = R[dim];
1347     ushort uco = (o[c]&0xFFF)<<3, vco = (o[r]&0xFFF)<<3;
1348     ushort uc1 = uco, vc1 = vco, uc2 = ushort(size<<3)+uco, vc2 = ushort(size<<3)+vco;
1349     uc1 = max(uc1, orig.u1);
1350     uc2 = min(uc2, orig.u2);
1351     vc1 = max(vc1, orig.v1);
1352     vc2 = min(vc2, orig.v2);
1353     if(!isempty(cu) && touchingface(cu, orient) && !(nmat!=MAT_AIR && (cu.material&matmask)==nmat))
1354     {
1355         uchar r1 = cu.edges[faceedgesidx[orient][0]], r2 = cu.edges[faceedgesidx[orient][1]],
1356               c1 = cu.edges[faceedgesidx[orient][2]], c2 = cu.edges[faceedgesidx[orient][3]];
1357         ushort u1 = max(c1&0xF, c2&0xF)*size+uco, u2 = min(c1>>4, c2>>4)*size+uco,
1358                v1 = max(r1&0xF, r2&0xF)*size+vco, v2 = min(r1>>4, r2>>4)*size+vco;
1359         u1 = max(u1, orig.u1);
1360         u2 = min(u2, orig.u2);
1361         v1 = max(v1, orig.v1);
1362         v2 = min(v2, orig.v2);
1363         if(v2-v1==vc2-vc1)
1364         {
1365             if(u2-u1==uc2-uc1) return;
1366             if(u1==uc1) uc1 = u2;
1367             if(u2==uc2) uc2 = u1;
1368         }
1369         else if(u2-u1==uc2-uc1)
1370         {
1371             if(v1==vc1) vc1 = v2;
1372             if(v2==vc2) vc2 = v1;
1373         }
1374     }
1375     if(uc1==uc2 || vc1==vc2) return;
1376     cf.u1 = min(cf.u1, uc1);
1377     cf.u2 = max(cf.u2, uc2);
1378     cf.v1 = min(cf.v1, vc1);
1379     cf.v2 = max(cf.v2, vc2);
1380 }
1381 
mincubeface(const cube & cu,int orient,const ivec & co,int size,facebounds & orig)1382 bool mincubeface(const cube &cu, int orient, const ivec &co, int size, facebounds &orig)
1383 {
1384     ivec no;
1385     int nsize;
1386     const cube &nc = neighbourcube(cu, orient, co, size, no, nsize);
1387     facebounds mincf;
1388     mincf.u1 = orig.u2;
1389     mincf.u2 = orig.u1;
1390     mincf.v1 = orig.v2;
1391     mincf.v2 = orig.v1;
1392     mincubeface(nc, opposite(orient), no, nsize, orig, mincf, cu.material&MAT_ALPHA ? MAT_AIR : MAT_ALPHA, MAT_ALPHA);
1393     bool smaller = false;
1394     if(mincf.u1 > orig.u1) { orig.u1 = mincf.u1; smaller = true; }
1395     if(mincf.u2 < orig.u2) { orig.u2 = mincf.u2; smaller = true; }
1396     if(mincf.v1 > orig.v1) { orig.v1 = mincf.v1; smaller = true; }
1397     if(mincf.v2 < orig.v2) { orig.v2 = mincf.v2; smaller = true; }
1398     return smaller;
1399 }
1400 
1401 VAR(IDF_WORLD, maxmerge, 0, 6, 12);
1402 VAR(IDF_WORLD, minface, 0, 4, 12);
1403 
1404 struct pvert
1405 {
1406     ushort x, y;
1407 
pvertpvert1408     pvert() {}
pvertpvert1409     pvert(ushort x, ushort y) : x(x), y(y) {}
1410 
operator ==pvert1411     bool operator==(const pvert &o) const { return x == o.x && y == o.y; }
operator !=pvert1412     bool operator!=(const pvert &o) const { return x != o.x || y != o.y; }
1413 };
1414 
1415 struct pedge
1416 {
1417     pvert from, to;
1418 
pedgepedge1419     pedge() {}
pedgepedge1420     pedge(const pvert &from, const pvert &to) : from(from), to(to) {}
1421 
operator ==pedge1422     bool operator==(const pedge &o) const { return from == o.from && to == o.to; }
operator !=pedge1423     bool operator!=(const pedge &o) const { return from != o.from || to != o.to; }
1424 };
1425 
hthash(const pedge & x)1426 static inline uint hthash(const pedge &x) { return uint(x.from.x)^(uint(x.from.y)<<8); }
htcmp(const pedge & x,const pedge & y)1427 static inline bool htcmp(const pedge &x, const pedge &y) { return x == y; }
1428 
1429 struct poly
1430 {
1431     cube *c;
1432     int numverts;
1433     bool merged;
1434     pvert verts[MAXFACEVERTS];
1435 };
1436 
clippoly(poly & p,const facebounds & b)1437 bool clippoly(poly &p, const facebounds &b)
1438 {
1439     pvert verts1[MAXFACEVERTS+4], verts2[MAXFACEVERTS+4];
1440     int numverts1 = 0, numverts2 = 0, px = p.verts[p.numverts-1].x, py = p.verts[p.numverts-1].y;
1441     loopi(p.numverts)
1442     {
1443         int x = p.verts[i].x, y = p.verts[i].y;
1444         if(x < b.u1)
1445         {
1446             if(px > b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
1447             if(px > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
1448         }
1449         else if(x > b.u2)
1450         {
1451             if(px < b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
1452             if(px < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
1453         }
1454         else
1455         {
1456             if(px < b.u1)
1457             {
1458                 if(x > b.u1) verts1[numverts1++] = pvert(b.u1, y + ((y - py)*(b.u1 - x))/(x - px));
1459             }
1460             else if(px > b.u2 && x < b.u2) verts1[numverts1++] = pvert(b.u2, y + ((y - py)*(b.u2 - x))/(x - px));
1461             verts1[numverts1++] = pvert(x, y);
1462         }
1463         px = x;
1464         py = y;
1465     }
1466     if(numverts1 < 3) return false;
1467     px = verts1[numverts1-1].x;
1468     py = verts1[numverts1-1].y;
1469     loopi(numverts1)
1470     {
1471         int x = verts1[i].x, y = verts1[i].y;
1472         if(y < b.v1)
1473         {
1474             if(py > b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
1475             if(py > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
1476         }
1477         else if(y > b.v2)
1478         {
1479             if(py < b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
1480             if(py < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
1481         }
1482         else
1483         {
1484             if(py < b.v1)
1485             {
1486                 if(y > b.v1) verts2[numverts2++] = pvert(x + ((x - px)*(b.v1 - y))/(y - py), b.v1);
1487             }
1488             else if(py > b.v2 && y < b.v2) verts2[numverts2++] = pvert(x + ((x - px)*(b.v2 - y))/(y - py), b.v2);
1489             verts2[numverts2++] = pvert(x, y);
1490         }
1491         px = x;
1492         py = y;
1493     }
1494     if(numverts2 < 3) return false;
1495     if(numverts2 > MAXFACEVERTS) return false;
1496     memcpy(p.verts, verts2, numverts2*sizeof(pvert));
1497     p.numverts = numverts2;
1498     return true;
1499 }
1500 
genpoly(cube & cu,int orient,const ivec & o,int size,int vis,ivec & n,int & offset,poly & p)1501 bool genpoly(cube &cu, int orient, const ivec &o, int size, int vis, ivec &n, int &offset, poly &p)
1502 {
1503     int dim = dimension(orient), coord = dimcoord(orient);
1504     ivec v[4];
1505     genfaceverts(cu, orient, v);
1506     if(flataxisface(cu, orient))
1507     {
1508          n = ivec(0, 0, 0);
1509          n[dim] = coord ? 1 : -1;
1510     }
1511     else
1512     {
1513         if(faceconvexity(v)) return false;
1514         n.cross(ivec(v[1]).sub(v[0]), ivec(v[2]).sub(v[0]));
1515         if(n.iszero()) n.cross(ivec(v[2]).sub(v[0]), ivec(v[3]).sub(v[0]));
1516         reduceslope(n);
1517     }
1518 
1519     ivec po = ivec(o).mask(0xFFF).shl(3);
1520     loopk(4) v[k].mul(size).add(po);
1521     offset = -n.dot(v[3]);
1522 
1523     int r = R[dim], c = C[dim], order = vis&4 ? 1 : 0;
1524     p.numverts = 0;
1525     if(coord)
1526     {
1527         const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
1528         if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
1529         const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
1530         if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
1531     }
1532     else
1533     {
1534         if(vis&2) { const ivec &v3 = v[(order+3)&3]; p.verts[p.numverts++] = pvert(v3[c], v3[r]); }
1535         const ivec &v2 = v[order+2]; p.verts[p.numverts++] = pvert(v2[c], v2[r]);
1536         if(vis&1) { const ivec &v1 = v[order+1]; p.verts[p.numverts++] = pvert(v1[c], v1[r]); }
1537         const ivec &v0 = v[order]; p.verts[p.numverts++] = pvert(v0[c], v0[r]);
1538     }
1539 
1540     if(faceedges(cu, orient)!=F_SOLID)
1541     {
1542         int px = int(p.verts[p.numverts-2].x) - int(p.verts[p.numverts-3].x), py = int(p.verts[p.numverts-2].y) - int(p.verts[p.numverts-3].y),
1543             cx = int(p.verts[p.numverts-1].x) - int(p.verts[p.numverts-2].x), cy = int(p.verts[p.numverts-1].y) - int(p.verts[p.numverts-2].y),
1544             dir = px*cy - py*cx;
1545         if(dir > 0) return false;
1546         if(!dir) { if(p.numverts < 4) return false; p.verts[p.numverts-2] = p.verts[p.numverts-1]; p.numverts--; }
1547         px = cx; py = cy;
1548         cx = int(p.verts[0].x) - int(p.verts[p.numverts-1].x); cy = int(p.verts[0].y) - int(p.verts[p.numverts-1].y);
1549         dir = px*cy - py*cx;
1550         if(dir > 0) return false;
1551         if(!dir) { if(p.numverts < 4) return false; p.numverts--; }
1552         px = cx; py = cy;
1553         cx = int(p.verts[1].x) - int(p.verts[0].x); cy = int(p.verts[1].y) - int(p.verts[0].y);
1554         dir = px*cy - py*cx;
1555         if(dir > 0) return false;
1556         if(!dir) { if(p.numverts < 4) return false; p.verts[0] = p.verts[p.numverts-1]; p.numverts--; }
1557         px = cx; py = cy;
1558         cx = int(p.verts[2].x) - int(p.verts[1].x); cy = int(p.verts[2].y) - int(p.verts[1].y);
1559         dir = px*cy - py*cx;
1560         if(dir > 0) return false;
1561         if(!dir) { if(p.numverts < 4) return false; p.verts[1] = p.verts[2]; p.verts[2] = p.verts[3]; p.numverts--; }
1562     }
1563 
1564     p.c = &cu;
1565     p.merged = false;
1566 
1567     if(minface && size >= 1<<minface && touchingface(cu, orient))
1568     {
1569         facebounds b;
1570         b.u1 = b.u2 = p.verts[0].x;
1571         b.v1 = b.v2 = p.verts[0].y;
1572         for(int i = 1; i < p.numverts; i++)
1573         {
1574             const pvert &v = p.verts[i];
1575             b.u1 = min(b.u1, v.x);
1576             b.u2 = max(b.u2, v.x);
1577             b.v1 = min(b.v1, v.y);
1578             b.v2 = max(b.v2, v.y);
1579         }
1580         if(mincubeface(cu, orient, o, size, b) && clippoly(p, b))
1581             p.merged = true;
1582     }
1583 
1584     return true;
1585 }
1586 
1587 struct plink : pedge
1588 {
1589     int polys[2];
1590 
plinkplink1591     plink() { clear(); }
plinkplink1592     plink(const pedge &p) : pedge(p) { clear(); }
1593 
clearplink1594     void clear() { polys[0] = polys[1] = -1; }
1595 };
1596 
mergepolys(int orient,hashset<plink> & links,vector<plink * > & queue,int owner,poly & p,poly & q,const pedge & e)1597 bool mergepolys(int orient, hashset<plink> &links, vector<plink *> &queue, int owner, poly &p, poly &q, const pedge &e)
1598 {
1599     int pe = -1, qe = -1;
1600     loopi(p.numverts) if(p.verts[i] == e.from) { pe = i; break; }
1601     loopi(q.numverts) if(q.verts[i] == e.to) { qe = i; break; }
1602     if(pe < 0 || qe < 0) return false;
1603     if(p.verts[(pe+1)%p.numverts] != e.to || q.verts[(qe+1)%q.numverts] != e.from) return false;
1604     /*
1605      *  c----d
1606      *  |    |
1607      *  F----T
1608      *  |  P |
1609      *  b----a
1610      */
1611     pvert verts[2*MAXFACEVERTS];
1612     int numverts = 0, index = pe+2; // starts at A = T+1, ends at F = T+p.numverts
1613     loopi(p.numverts-1)
1614     {
1615         if(index >= p.numverts) index -= p.numverts;
1616         verts[numverts++] = p.verts[index++];
1617     }
1618     index = qe+2; // starts at C = T+2 = F+1, ends at T = T+q.numverts
1619     int px = int(verts[numverts-1].x) - int(verts[numverts-2].x), py = int(verts[numverts-1].y) - int(verts[numverts-2].y);
1620     loopi(q.numverts-1)
1621     {
1622         if(index >= q.numverts) index -= q.numverts;
1623         pvert &src = q.verts[index++];
1624         int cx = int(src.x) - int(verts[numverts-1].x), cy = int(src.y) - int(verts[numverts-1].y),
1625             dir = px*cy - py*cx;
1626         if(dir > 0) return false;
1627         if(!dir) numverts--;
1628         verts[numverts++] = src;
1629         px = cx;
1630         py = cy;
1631     }
1632     int cx = int(verts[0].x) - int(verts[numverts-1].x), cy = int(verts[0].y) - int(verts[numverts-1].y),
1633         dir = px*cy - py*cx;
1634     if(dir > 0) return false;
1635     if(!dir) numverts--;
1636 
1637     if(numverts > MAXFACEVERTS) return false;
1638 
1639     q.merged = true;
1640     q.numverts = 0;
1641 
1642     p.merged = true;
1643     p.numverts = numverts;
1644     memcpy(p.verts, verts, numverts*sizeof(pvert));
1645 
1646     int prev = p.numverts-1;
1647     loopj(p.numverts)
1648     {
1649         pedge e(p.verts[prev], p.verts[j]);
1650         int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
1651         if(order) swap(e.from, e.to);
1652         plink &l = links.access(e, e);
1653         bool shouldqueue = l.polys[order] < 0 && l.polys[order^1] >= 0;
1654         l.polys[order] = owner;
1655         if(shouldqueue) queue.add(&l);
1656         prev = j;
1657     }
1658 
1659     return true;
1660 }
1661 
addmerge(cube & cu,int orient,const ivec & co,const ivec & n,int offset,poly & p)1662 void addmerge(cube &cu, int orient, const ivec &co, const ivec &n, int offset, poly &p)
1663 {
1664     cu.merged |= 1<<orient;
1665     if(!p.numverts)
1666     {
1667         if(cu.ext) cu.ext->surfaces[orient] = ambientsurface;
1668         return;
1669     }
1670     surfaceinfo surf = brightsurface;
1671     vertinfo verts[MAXFACEVERTS];
1672     surf.numverts |= p.numverts;
1673     int dim = dimension(orient), coord = dimcoord(orient), c = C[dim], r = R[dim];
1674     loopk(p.numverts)
1675     {
1676         pvert &src = p.verts[coord ? k : p.numverts-1-k];
1677         vertinfo &dst = verts[k];
1678         ivec v;
1679         v[c] = src.x;
1680         v[r] = src.y;
1681         v[dim] = -(offset + n[c]*src.x + n[r]*src.y)/n[dim];
1682         dst.set(v);
1683     }
1684     if(cu.ext)
1685     {
1686         const surfaceinfo &oldsurf = cu.ext->surfaces[orient];
1687         int numverts = oldsurf.numverts&MAXFACEVERTS;
1688         if(numverts == p.numverts)
1689         {
1690             ivec v0 = verts[0].getxyz();
1691             const vertinfo *oldverts = cu.ext->verts() + oldsurf.verts;
1692             loopj(numverts) if(v0 == oldverts[j].getxyz())
1693             {
1694                 for(int k = 1; k < numverts; k++)
1695                 {
1696                     if(++j >= numverts) j = 0;
1697                     if(verts[k].getxyz() != oldverts[j].getxyz()) goto nomatch;
1698                 }
1699                 return;
1700             }
1701         nomatch:;
1702         }
1703     }
1704     setsurface(cu, orient, surf, verts, p.numverts);
1705 }
1706 
clearmerge(cube & c,int orient)1707 static inline void clearmerge(cube &c, int orient)
1708 {
1709     if(c.merged&(1<<orient))
1710     {
1711         c.merged &= ~(1<<orient);
1712         if(c.ext) c.ext->surfaces[orient] = brightsurface;
1713     }
1714 }
1715 
addmerges(int orient,const ivec & co,const ivec & n,int offset,vector<poly> & polys)1716 void addmerges(int orient, const ivec &co, const ivec &n, int offset, vector<poly> &polys)
1717 {
1718     loopv(polys)
1719     {
1720         poly &p = polys[i];
1721         if(p.merged) addmerge(*p.c, orient, co, n, offset, p);
1722         else clearmerge(*p.c, orient);
1723     }
1724 }
1725 
mergepolys(int orient,const ivec & co,const ivec & n,int offset,vector<poly> & polys)1726 void mergepolys(int orient, const ivec &co, const ivec &n, int offset, vector<poly> &polys)
1727 {
1728     if(polys.length() <= 1) { addmerges(orient, co, n, offset, polys); return; }
1729     hashset<plink> links(polys.length() <= 32 ? 128 : 1024);
1730     vector<plink *> queue;
1731     loopv(polys)
1732     {
1733         poly &p = polys[i];
1734         int prev = p.numverts-1;
1735         loopj(p.numverts)
1736         {
1737             pedge e(p.verts[prev], p.verts[j]);
1738             int order = e.from.x > e.to.x || (e.from.x == e.to.x && e.from.y > e.to.y) ? 1 : 0;
1739             if(order) swap(e.from, e.to);
1740             plink &l = links.access(e, e);
1741             l.polys[order] = i;
1742             if(l.polys[0] >= 0 && l.polys[1] >= 0) queue.add(&l);
1743             prev = j;
1744         }
1745     }
1746     vector<plink *> nextqueue;
1747     while(queue.length())
1748     {
1749         loopv(queue)
1750         {
1751             plink &l = *queue[i];
1752             if(l.polys[0] >= 0 && l.polys[1] >= 0)
1753                 mergepolys(orient, links, nextqueue, l.polys[0], polys[l.polys[0]], polys[l.polys[1]], l);
1754         }
1755         queue.setsize(0);
1756         queue.move(nextqueue);
1757     }
1758     addmerges(orient, co, n, offset, polys);
1759 }
1760 
1761 static int genmergeprogress = 0;
1762 
1763 struct cfpolys
1764 {
1765     vector<poly> polys;
1766 };
1767 
1768 static hashtable<cfkey, cfpolys> cpolys;
1769 
genmerges(cube * c=worldroot,const ivec & o=ivec (0,0,0),int size=worldsize>>1)1770 void genmerges(cube *c = worldroot, const ivec &o = ivec(0, 0, 0), int size = worldsize>>1)
1771 {
1772     if((genmergeprogress++&0xFFF)==0) progress(float(genmergeprogress)/allocnodes, "Merging surfaces...");
1773     neighbourstack[++neighbourdepth] = c;
1774     loopi(8)
1775     {
1776         ivec co(i, o, size);
1777         int vis;
1778         if(c[i].children) genmerges(c[i].children, co, size>>1);
1779         else if(!isempty(c[i])) loopj(6) if((vis = visibletris(c[i], j, co, size)))
1780         {
1781             cfkey k;
1782             poly p;
1783             if(size < 1<<maxmerge && c != worldroot)
1784             {
1785                 if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p))
1786                 {
1787                     k.orient = j;
1788                     k.tex = c[i].texture[j];
1789                     k.material = c[i].material&MAT_ALPHA;
1790                     cpolys[k].polys.add(p);
1791                     continue;
1792                 }
1793             }
1794             else if(minface && size >= 1<<minface && touchingface(c[i], j))
1795             {
1796                 if(genpoly(c[i], j, co, size, vis, k.n, k.offset, p) && p.merged)
1797                 {
1798                     addmerge(c[i], j, co, k.n, k.offset, p);
1799                     continue;
1800                 }
1801             }
1802             clearmerge(c[i], j);
1803         }
1804         if((size == 1<<maxmerge || c == worldroot) && cpolys.numelems)
1805         {
1806             enumeratekt(cpolys, cfkey, key, cfpolys, val,
1807             {
1808                 mergepolys(key.orient, co, key.n, key.offset, val.polys);
1809             });
1810             cpolys.clear();
1811         }
1812     }
1813     --neighbourdepth;
1814 }
1815 
calcmergedsize(int orient,const ivec & co,int size,const vertinfo * verts,int numverts)1816 int calcmergedsize(int orient, const ivec &co, int size, const vertinfo *verts, int numverts)
1817 {
1818     ushort x1 = verts[0].x, y1 = verts[0].y, z1 = verts[0].z,
1819            x2 = x1, y2 = y1, z2 = z1;
1820     for(int i = 1; i < numverts; i++)
1821     {
1822         const vertinfo &v = verts[i];
1823         x1 = min(x1, v.x);
1824         x2 = max(x2, v.x);
1825         y1 = min(y1, v.y);
1826         y2 = max(y2, v.y);
1827         z1 = min(z1, v.z);
1828         z2 = max(z2, v.z);
1829     }
1830     int bits = 0;
1831     while(1<<bits < size) ++bits;
1832     bits += 3;
1833     ivec mo(co);
1834     mo.mask(0xFFF);
1835     mo.shl(3);
1836     while(bits<15)
1837     {
1838         mo.mask(~((1<<bits)-1));
1839         if(mo.x <= x1 && mo.x + (1<<bits) >= x2 &&
1840            mo.y <= y1 && mo.y + (1<<bits) >= y2 &&
1841            mo.z <= z1 && mo.z + (1<<bits) >= z2)
1842             break;
1843         bits++;
1844     }
1845     return bits-3;
1846 }
1847 
invalidatemerges(cube & c)1848 static void invalidatemerges(cube &c)
1849 {
1850     if(c.merged)
1851     {
1852         brightencube(c);
1853         c.merged = 0;
1854     }
1855     if(c.ext)
1856     {
1857         if(c.ext->va)
1858         {
1859             if(!(c.ext->va->hasmerges&(MERGE_PART | MERGE_ORIGIN))) return;
1860             destroyva(c.ext->va);
1861             c.ext->va = NULL;
1862         }
1863         if(c.ext->tjoints >= 0) c.ext->tjoints = -1;
1864     }
1865     if(c.children) loopi(8) invalidatemerges(c.children[i]);
1866 }
1867 
1868 static int invalidatedmerges = 0;
1869 
invalidatemerges(cube & c,const ivec & co,int size,bool msg)1870 void invalidatemerges(cube &c, const ivec &co, int size, bool msg)
1871 {
1872     if(msg && invalidatedmerges!=totalmillis)
1873     {
1874         progress(0, "Invalidating merged surfaces...");
1875         invalidatedmerges = totalmillis;
1876     }
1877     invalidatemerges(c);
1878 }
1879 
calcmerges()1880 void calcmerges()
1881 {
1882     genmergeprogress = 0;
1883     genmerges();
1884 }
1885