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¬ouch)==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¬ouch;
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¬ouch;
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¬ouch;
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