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