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