1 // core world management routines
2
3 #include "engine.h"
4
5 cube *worldroot = newcubes(F_SOLID);
6 int allocnodes = 0;
7
newcubeext(cube & c)8 cubeext *newcubeext(cube &c)
9 {
10 if(c.ext) return c.ext;
11 c.ext = new cubeext;
12 c.ext->material = MAT_AIR;
13 c.ext->visible = 0;
14 c.ext->merged = 0;
15 c.ext->mergeorigin = 0;
16 c.ext->va = NULL;
17 c.ext->clip = NULL;
18 c.ext->surfaces = NULL;
19 c.ext->normals = NULL;
20 c.ext->ents = NULL;
21 c.ext->merges = NULL;
22 c.ext->tjoints = -1;
23 return c.ext;
24 }
25
newcubes(uint face)26 cube *newcubes(uint face)
27 {
28 cube *c = new cube[8];
29 loopi(8)
30 {
31 c->children = NULL;
32 c->ext = NULL;
33 setfaces(*c, face);
34 loopl(6)
35 {
36 c->texture[l] = 2+l;
37 }
38 c++;
39 }
40 allocnodes++;
41 return c-8;
42 }
43
familysize(cube & c)44 int familysize(cube &c)
45 {
46 int size = 1;
47 if(c.children) loopi(8) size += familysize(c.children[i]);
48 return size;
49 }
50
freeocta(cube * c)51 void freeocta(cube *c)
52 {
53 if(!c) return;
54 loopi(8) discardchildren(c[i]);
55 delete[] c;
56 allocnodes--;
57 }
58
freecubeext(cube & c)59 void freecubeext(cube &c)
60 {
61 DELETEP(c.ext);
62 }
63
discardchildren(cube & c)64 void discardchildren(cube &c)
65 {
66 if(c.ext)
67 {
68 if(c.ext->va) destroyva(c.ext->va);
69 c.ext->va = NULL;
70 c.ext->merged = 0;
71 c.ext->tjoints = -1;
72 freesurfaces(c);
73 freenormals(c);
74 freeclipplanes(c);
75 freeoctaentities(c);
76 freemergeinfo(c);
77 freecubeext(c);
78 }
79 if(c.children)
80 {
81 freeocta(c.children);
82 c.children = NULL;
83 }
84 }
85
getcubevector(cube & c,int d,int x,int y,int z,ivec & p)86 void getcubevector(cube &c, int d, int x, int y, int z, ivec &p)
87 {
88 ivec v(d, x, y, z);
89
90 loopi(3)
91 p[i] = edgeget(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]]);
92 }
93
setcubevector(cube & c,int d,int x,int y,int z,ivec & p)94 void setcubevector(cube &c, int d, int x, int y, int z, ivec &p)
95 {
96 ivec v(d, x, y, z);
97
98 loopi(3)
99 edgeset(cubeedge(c, i, v[R[i]], v[C[i]]), v[D[i]], p[i]);
100 }
101
optiface(uchar * p,cube & c)102 void optiface(uchar *p, cube &c)
103 {
104 loopi(4) if(edgeget(p[i], 0)!=edgeget(p[i], 1)) return;
105 emptyfaces(c);
106 }
107
printcube()108 void printcube()
109 {
110 cube &c = lookupcube(lu.x, lu.y, lu.z, 0); // assume this is cube being pointed at
111 conoutf("\fa= %p = (%d, %d, %d) @ %d", &c, lu.x, lu.y, lu.z, lusize);
112 conoutf("\fa x %.8x", c.faces[0]);
113 conoutf("\fa y %.8x", c.faces[1]);
114 conoutf("\fa z %.8x", c.faces[2]);
115 }
116
117 COMMAND(printcube, "");
118
isvalidcube(cube & c)119 bool isvalidcube(cube &c)
120 {
121 clipplanes p;
122 genclipplanes(c, 0, 0, 0, 256, p);
123 loopi(8) // test that cube is convex
124 {
125 vvec v;
126 calcvert(c, 0, 0, 0, 256, v, i);
127 if(!pointincube(p, v.tovec()))
128 return false;
129 }
130 return true;
131 }
132
validatec(cube * c,int size)133 void validatec(cube *c, int size)
134 {
135 loopi(8)
136 {
137 if(c[i].children)
138 {
139 if(size<=(8>>VVEC_FRAC))
140 {
141 solidfaces(c[i]);
142 discardchildren(c[i]);
143 }
144 else validatec(c[i].children, size>>1);
145 }
146 else
147 {
148 loopj(3) optiface((uchar *)&(c[i].faces[j]), c[i]);
149 loopj(12)
150 if(edgeget(c[i].edges[j], 1)>8 ||
151 edgeget(c[i].edges[j], 1)<edgeget(c[i].edges[j], 0))
152 emptyfaces(c[i]);
153 }
154 }
155 }
156
157 ivec lu;
158 int lusize;
lookupcube(int tx,int ty,int tz,int tsize)159 cube &lookupcube(int tx, int ty, int tz, int tsize)
160 {
161 int size = hdr.worldsize;
162 int x = 0, y = 0, z = 0;
163 cube *c = worldroot;
164 for(;;)
165 {
166 size >>= 1;
167 ASSERT(size);
168 if(tz>=z+size) { z += size; c += 4; }
169 if(ty>=y+size) { y += size; c += 2; }
170 if(tx>=x+size) { x += size; c += 1; }
171 //if(tsize==size) break;
172 if(abs(tsize)>=size) break;
173 if(c->children==NULL)
174 {
175 //if(!tsize) break;
176 if(tsize<=0) break;
177 subdividecube(*c);
178 }
179 c = c->children;
180 }
181 lu.x = x;
182 lu.y = y;
183 lu.z = z;
184 lusize = size;
185 return *c;
186 }
187
neighbourcube(int x,int y,int z,int size,int rsize,int orient)188 cube &neighbourcube(int x, int y, int z, int size, int rsize, int orient)
189 {
190 switch(orient)
191 {
192 case O_BOTTOM: z -= size; break;
193 case O_TOP: z += size; break;
194 case O_BACK: y -= size; break;
195 case O_FRONT: y += size; break;
196 case O_LEFT: x -= size; break;
197 case O_RIGHT: x += size; break;
198 }
199 return lookupcube(x, y, z, rsize);
200 }
201
lookupmaterial(const vec & v)202 int lookupmaterial(const vec &v)
203 {
204 ivec o(v);
205 if(!insideworld(o)) return MAT_AIR;
206 int scale = worldscale-1;
207 cube *c = &worldroot[octastep(o.x, o.y, o.z, scale)];
208 while(c->children)
209 {
210 scale--;
211 c = &c->children[octastep(o.x, o.y, o.z, scale)];
212 }
213 return c->ext ? c->ext->material : MAT_AIR;
214 }
215
216 ////////// (re)mip //////////
217
getmippedtexture(cube & p,int orient)218 int getmippedtexture(cube &p, int orient)
219 {
220 cube *c = p.children;
221 int d = dimension(orient);
222 int dc = dimcoord(orient);
223 int tex[4] = {-1,-1,-1,-1};
224 loop(x,2) loop(y,2)
225 {
226 int n = octaindex(d, x, y, dc);
227 if(isempty(c[n]))
228 n = oppositeocta(d, n);
229 if(isempty(c[n]))
230 continue;
231
232 loopk(3)
233 if(tex[k] == c[n].texture[orient])
234 return tex[k];
235
236 if(c[n].texture[orient] > 0) // assume 0 is sky. favour non-sky tex
237 tex[x*2+y] = c[n].texture[orient];
238 }
239
240 loopk(4)
241 if(tex[k]>0) return tex[k];
242
243 return p.texture[orient];
244 }
245
forcemip(cube & c)246 void forcemip(cube &c)
247 {
248 cube *ch = c.children;
249 emptyfaces(c);
250
251 loopi(8) loopj(8)
252 {
253 int n = i^(j==3 ? 4 : (j==4 ? 3 : j));
254 if(!isempty(ch[n])) // breadth first search for cube near vert
255 {
256 ivec v, p(i);
257 getcubevector(ch[n], 2, p.x, p.y, p.z, v);
258
259 loopk(3) // adjust vert to parent size
260 {
261 if(octacoord(k, n) == 1)
262 v[k] += 8;
263 v[k] >>= 1;
264 }
265
266 setcubevector(c, 2, p.x, p.y, p.z, v);
267 break;
268 }
269 }
270
271 loopj(6)
272 c.texture[j] = getmippedtexture(c, j);
273 }
274
midedge(const ivec & a,const ivec & b,int xd,int yd,bool & perfect)275 int midedge(const ivec &a, const ivec &b, int xd, int yd, bool &perfect)
276 {
277 int ax = a[xd], bx = b[xd];
278 int ay = a[yd], by = b[yd];
279 if(ay==by) return ay;
280 if(ax==bx) { perfect = false; return ay; }
281 bool crossx = (ax<8 && bx>8) || (ax>8 && bx<8);
282 bool crossy = (ay<8 && by>8) || (ay>8 && by<8);
283 if(crossy && !crossx) { midedge(a,b,yd,xd,perfect); return 8; } // to test perfection
284 if(ax<=8 && bx<=8) return ax>bx ? ay : by;
285 if(ax>=8 && bx>=8) return ax<bx ? ay : by;
286 int risex = (by-ay)*(8-ax)*256;
287 int s = risex/(bx-ax);
288 int y = s/256 + ay;
289 if(((abs(s)&0xFF)!=0) || // ie: rounding error
290 (crossy && y!=8) ||
291 (y<0 || y>16)) perfect = false;
292 return crossy ? 8 : min(max(y, 0), 16);
293 }
294
subdividecube(cube & c,bool fullcheck,bool brighten)295 bool subdividecube(cube &c, bool fullcheck, bool brighten)
296 {
297 if(c.children) return true;
298 if(isempty(c) || isentirelysolid(c))
299 {
300 int mat = c.ext ? c.ext->material : MAT_AIR;
301 c.children = newcubes(isempty(c) ? F_EMPTY : F_SOLID);
302 loopi(8)
303 {
304 loopl(6) c.children[i].texture[l] = c.texture[l];
305 if(mat!=MAT_AIR) ext(c.children[i]).material = mat;
306 if(brighten && !isempty(c)) brightencube(c.children[i]);
307 }
308 return true;
309 }
310 cube *ch = c.children = newcubes(F_SOLID);
311 bool perfect = true, p1, p2;
312 int mat = c.ext ? c.ext->material : MAT_AIR;
313 ivec v[8];
314 loopi(8)
315 {
316 ivec p(i);
317 getcubevector(c, 2, p.x, p.y, p.z, v[i]);
318 v[i].mul(2);
319 if(mat!=MAT_AIR) ext(ch[i]).material = mat;
320 }
321
322 loopj(6)
323 {
324 int d = dimension(j);
325 int z = dimcoord(j);
326 int e[3][3];
327 ivec *v1[2][2];
328 loop(y, 2) loop(x, 2)
329 v1[x][y] = v+octaindex(d, x, y, z);
330
331 loop(y, 3) loop(x, 3) // gen edges
332 {
333 if(x==1 && y==1) // center
334 {
335 int c1 = midedge(*v1[0][0], *v1[1][1], R[d], d, p1 = perfect);
336 int c2 = midedge(*v1[0][1], *v1[1][0], R[d], d, p2 = perfect);
337 e[x][y] = z ? max(c1,c2) : min(c1,c2);
338 perfect = e[x][y]==c1 ? p1 : p2;
339 }
340 else if(((x+y)&1)==0) // corner
341 e[x][y] = (*v1[x>>1][y>>1])[d];
342 else // edge
343 {
344 int a = min(x, y), b = x&1;
345 e[x][y] = midedge(*v1[a][a], *v1[a^b][a^(1-b)], x==1?R[d]:C[d], d, perfect);
346 }
347 }
348
349 loopi(8)
350 {
351 ivec o(i);
352 ch[i].texture[j] = c.texture[j];
353 loop(y, 2) loop(x, 2) // assign child edges
354 {
355 int ce = e[x+o[R[d]]][y+o[C[d]]];
356 if(o[D[d]]) ce -= 8;
357 ce = min(max(ce, 0), 8);
358 uchar &f = cubeedge(ch[i], d, x, y);
359 edgeset(f, z, ce);
360 }
361 }
362 }
363
364 validatec(ch, hdr.worldsize);
365 if(fullcheck) loopi(8) if(!isvalidcube(ch[i])) // not so good...
366 {
367 emptyfaces(ch[i]);
368 perfect=false;
369 }
370 if(brighten) loopi(8) if(!isempty(ch[i])) brightencube(ch[i]);
371 return perfect;
372 }
373
crushededge(uchar e,int dc)374 bool crushededge(uchar e, int dc) { return dc ? e==0 : e==0x88; }
375
visibleorient(cube & c,int orient)376 int visibleorient(cube &c, int orient)
377 {
378 loopi(2) loopj(2)
379 {
380 int a = faceedgesidx[orient][i*2 + 0];
381 int b = faceedgesidx[orient][i*2 + 1];
382 if(crushededge(c.edges[a],j) &&
383 crushededge(c.edges[b],j) &&
384 touchingface(c, orient)) return ((a>>2)<<1) + j;
385 }
386 return orient;
387 }
388
389 VAR(mipvis, 0, 0, 1);
390
391 static int remipprogress = 0, remiptotal = 0;
392
remip(cube & c,int x,int y,int z,int size)393 bool remip(cube &c, int x, int y, int z, int size)
394 {
395 if(c.ext)
396 {
397 c.ext->merged = 0;
398 if(c.ext->merges) freemergeinfo(c);
399 }
400
401 cube *ch = c.children;
402 if(!ch)
403 {
404 if(size<<1 <= VVEC_INT_MASK+1) return true;
405 subdividecube(c);
406 ch = c.children;
407 }
408 else if((remipprogress++&0x7FF)==1) progress(float(remipprogress)/remiptotal, "remipping...");
409
410 bool perfect = true;
411 loopi(8)
412 {
413 ivec o(i, x, y, z, size);
414 if(!remip(ch[i], o.x, o.y, o.z, size>>1)) perfect = false;
415 }
416
417 solidfaces(c); // so texmip is more consistent
418 loopj(6)
419 c.texture[j] = getmippedtexture(c, j); // parents get child texs regardless
420
421 if(!perfect) return false;
422 if(size<<1 > VVEC_INT_MASK+1) return false;
423
424 uchar mat = MAT_AIR;
425 loopi(8)
426 {
427 mat = ch[i].ext ? ch[i].ext->material : MAT_AIR;
428 if((mat&MATF_CLIP) == MAT_NOCLIP)
429 {
430 if(i > 0) return false;
431 while(++i < 8) if(!ch[i].ext || ch[i].ext->material != mat) return false;
432 break;
433 }
434 else if(!isentirelysolid(ch[i]))
435 {
436 while(++i < 8)
437 {
438 uchar omat = ch[i].ext ? ch[i].ext->material : MAT_AIR;
439 if(isentirelysolid(ch[i]) ? (omat&MATF_CLIP) == MAT_NOCLIP : mat != omat) return false;
440 }
441 break;
442 }
443 }
444
445 cube n = c;
446 forcemip(n);
447 n.children = NULL;
448 if(!subdividecube(n, false, false))
449 { freeocta(n.children); return false; }
450
451 cube *nh = n.children;
452 uchar vis[6] = {0, 0, 0, 0, 0, 0};
453 loopi(8)
454 {
455 if(ch[i].faces[0] != nh[i].faces[0] ||
456 ch[i].faces[1] != nh[i].faces[1] ||
457 ch[i].faces[2] != nh[i].faces[2])
458 { freeocta(nh); return false; }
459
460 if(isempty(ch[i]) && isempty(nh[i])) continue;
461
462 ivec o(i, x, y, z, size);
463 loop(orient, 6)
464 if(visibleface(ch[i], orient, o.x, o.y, o.z, size))
465 {
466 if(ch[i].texture[orient] != n.texture[orient]) { freeocta(nh); return false; }
467 vis[orient] |= 1<<i;
468 }
469 }
470 if(mipvis) loop(orient, 6)
471 {
472 int mask = 0;
473 loop(x, 2) loop(y, 2) mask |= 1<<octaindex(dimension(orient), x, y, dimcoord(orient));
474 if(vis[orient]&mask && (vis[orient]&mask)!=mask) { freeocta(nh); return false; }
475 }
476
477 freeocta(nh);
478 discardchildren(c);
479 loopi(3) c.faces[i] = n.faces[i];
480 if(mat!=MAT_AIR) ext(c).material = mat;
481 brightencube(c);
482 return true;
483 }
484
mpremip(bool local)485 void mpremip(bool local)
486 {
487 if(local) client::edittrigger(sel, EDIT_REMIP);
488 remipprogress = 1;
489 remiptotal = allocnodes;
490 loopi(8)
491 {
492 ivec o(i, 0, 0, 0, hdr.worldsize>>1);
493 remip(worldroot[i], o.x, o.y, o.z, hdr.worldsize>>2);
494 }
495 calcmerges();
496 if(!local) allchanged();
497 }
498
remip_()499 void remip_()
500 {
501 mpremip(true);
502 allchanged();
503 }
504
505 COMMANDN(remip, remip_, "");
506
edgelookup(cube & c,const ivec & p,int dim)507 uchar &edgelookup(cube &c, const ivec &p, int dim)
508 {
509 return c.edges[dim*4 +(p[C[dim]]>>3)*2 +(p[R[dim]]>>3)];
510 }
511
edgeval(cube & c,const ivec & p,int dim,int coord)512 int edgeval(cube &c, const ivec &p, int dim, int coord)
513 {
514 return edgeget(edgelookup(c,p,dim), coord);
515 }
516
genvertp(cube & c,ivec & p1,ivec & p2,ivec & p3,plane & pl)517 void genvertp(cube &c, ivec &p1, ivec &p2, ivec &p3, plane &pl)
518 {
519 int dim = 0;
520 if(p1.y==p2.y && p2.y==p3.y) dim = 1;
521 else if(p1.z==p2.z && p2.z==p3.z) dim = 2;
522
523 int coord = p1[dim];
524
525 ivec v1(p1), v2(p2), v3(p3);
526 v1[D[dim]] = edgeval(c, p1, dim, coord);
527 v2[D[dim]] = edgeval(c, p2, dim, coord);
528 v3[D[dim]] = edgeval(c, p3, dim, coord);
529
530 pl.toplane(v1.tovec(), v2.tovec(), v3.tovec());
531 }
532
threeplaneintersect(plane & pl1,plane & pl2,plane & pl3,vec & dest)533 bool threeplaneintersect(plane &pl1, plane &pl2, plane &pl3, vec &dest)
534 {
535 vec &t1 = dest, t2, t3, t4;
536 t1.cross(pl1, pl2); t4 = t1; t1.mul(pl3.offset);
537 t2.cross(pl3, pl1); t2.mul(pl2.offset);
538 t3.cross(pl2, pl3); t3.mul(pl1.offset);
539 t1.add(t2);
540 t1.add(t3);
541 t1.mul(-1);
542 float d = t4.dot(pl3);
543 if(d==0) return false;
544 t1.div(d);
545 return true;
546 }
547
genedgespanvert(ivec & p,cube & c,vec & v)548 void genedgespanvert(ivec &p, cube &c, vec &v)
549 {
550 ivec p1(8-p.x, p.y, p.z);
551 ivec p2(p.x, 8-p.y, p.z);
552 ivec p3(p.x, p.y, 8-p.z);
553
554 cube s;
555 solidfaces(s);
556
557 plane plane1, plane2, plane3;
558 genvertp(c, p, p1, p2, plane1);
559 genvertp(c, p, p2, p3, plane2);
560 genvertp(c, p, p3, p1, plane3);
561 if(plane1==plane2) genvertp(s, p, p1, p2, plane1);
562 if(plane1==plane3) genvertp(s, p, p1, p2, plane1);
563 if(plane2==plane3) genvertp(s, p, p2, p3, plane2);
564
565 ASSERT(threeplaneintersect(plane1, plane2, plane3, v));
566 //ASSERT(v.x>=0 && v.x<=8);
567 //ASSERT(v.y>=0 && v.y<=8);
568 //ASSERT(v.z>=0 && v.z<=8);
569 v.x = max(0.0f, min(8.0f, v.x));
570 v.y = max(0.0f, min(8.0f, v.y));
571 v.z = max(0.0f, min(8.0f, v.z));
572 }
573
edgespan2vectorcube(cube & c)574 void edgespan2vectorcube(cube &c)
575 {
576 vec v;
577
578 if(c.children) loopi(8) edgespan2vectorcube(c.children[i]);
579
580 if(isentirelysolid(c) || isempty(c)) return;
581
582 cube n = c;
583
584 loop(x,2) loop(y,2) loop(z,2)
585 {
586 ivec p(8*x, 8*y, 8*z);
587 genedgespanvert(p, c, v);
588
589 edgeset(cubeedge(n, 0, y, z), x, int(v.x+0.49f));
590 edgeset(cubeedge(n, 1, z, x), y, int(v.y+0.49f));
591 edgeset(cubeedge(n, 2, x, y), z, int(v.z+0.49f));
592 }
593
594 c = n;
595 }
596
converttovectorworld()597 void converttovectorworld()
598 {
599 if(verbose) conoutf("\frWARNING: old map, use savecurrentmap");
600 loopi(8) edgespan2vectorcube(worldroot[i]);
601 }
602
genvectorvert(const ivec & p,cube & c,ivec & v)603 void genvectorvert(const ivec &p, cube &c, ivec &v)
604 {
605 v.x = edgeval(c, p, 0, p.x);
606 v.y = edgeval(c, p, 1, p.y);
607 v.z = edgeval(c, p, 2, p.z);
608 }
609
610 const ivec cubecoords[8] = // verts of bounding cube
611 {
612 ivec(8, 8, 0),
613 ivec(0, 8, 0),
614 ivec(0, 8, 8),
615 ivec(8, 8, 8),
616 ivec(8, 0, 8),
617 ivec(0, 0, 8),
618 ivec(0, 0, 0),
619 ivec(8, 0, 0),
620 };
621
622 const ushort fv[6][4] = // indexes for cubecoords, per each vert of a face orientation
623 {
624 { 2, 1, 6, 5 },
625 { 3, 4, 7, 0 },
626 { 4, 5, 6, 7 },
627 { 1, 2, 3, 0 },
628 { 6, 1, 0, 7 },
629 { 5, 4, 3, 2 },
630 };
631
632 const uchar fvmasks[64] = // mask of verts used given a mask of visible face orientations
633 {
634 0x00, 0x66, 0x99, 0xFF, 0xF0, 0xF6, 0xF9, 0xFF,
635 0x0F, 0x6F, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
636 0xC3, 0xE7, 0xDB, 0xFF, 0xF3, 0xF7, 0xFB, 0xFF,
637 0xCF, 0xEF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
638 0x3C, 0x7E, 0xBD, 0xFF, 0xFC, 0xFE, 0xFD, 0xFF,
639 0x3F, 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
640 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
641 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
642 };
643
644 const uchar faceedgesidx[6][4] = // ordered edges surrounding each orient
645 {//1st face,2nd face
646 { 4, 5, 8, 10 },
647 { 6, 7, 9, 11 },
648 { 0, 2, 8, 9 },
649 { 1, 3, 10,11 },
650 { 0, 1, 4, 6 },
651 { 2, 3, 5, 7 },
652 };
653
654 const uchar faceedgesrcidx[6][4] =
655 {//0..1 = row edges, 2..3 = column edges
656 { 4, 5, 8, 10 },
657 { 6, 7, 9, 11 },
658 { 8, 9, 0, 2 },
659 { 10, 11, 1, 3 },
660 { 0, 1, 4, 6 },
661 { 2, 3, 5, 7 },
662 };
663
flataxisface(cube & c,int orient)664 bool flataxisface(cube &c, int orient)
665 {
666 uint face = c.faces[dimension(orient)];
667 if(dimcoord(orient)) face >>= 4;
668 face &= 0x0F0F0F0F;
669 return face == 0x01010101*(face&0x0F);
670 }
671
touchingface(cube & c,int orient)672 bool touchingface(cube &c, int orient)
673 {
674 uint face = c.faces[dimension(orient)];
675 return dimcoord(orient) ? (face&0xF0F0F0F0)==0x80808080 : (face&0x0F0F0F0F)==0;
676 }
677
faceconvexity(cube & c,int orient)678 int faceconvexity(cube &c, int orient)
679 {
680 /* // fast approximation
681 vec v[4];
682 int d = dimension(orient);
683 loopi(4) vertrepl(c, *(ivec *)cubecoords[fv[orient][i]], v[i], d, dimcoord(orient));
684 int n = (int)(v[0][d] - v[1][d] + v[2][d] - v[3][d]);
685 if (!dimcoord(orient)) n *= -1;
686 return n; // returns +ve if convex when tris are verts 012, 023. -ve for concave.
687 */
688 // slow perfect
689 ivec v[4];
690
691 if(flataxisface(c, orient)) return 0;
692
693 loopi(4) genvectorvert(cubecoords[fv[orient][i]], c, v[i]);
694
695 ivec n;
696 n.cross(v[1].sub(v[0]), v[2].sub(v[0]));
697 int x = n.dot(v[0]), y = n.dot(v[3]);
698 if(x < y) return -1; // concave
699 else if(x > y) return 1; // convex
700 else return 0; // flat
701 }
702
faceorder(cube & c,int orient)703 int faceorder(cube &c, int orient)
704 {
705 /*
706 uchar *edges = &c.edges[4*dimension(orient)];
707 uchar h[4];
708 loopi(4) h[i] = dimcoord(orient) ? edges[i]>>4 : 8-(edges[i]&0xF);
709 if(h[0]+h[3]<h[1]+h[2]) return 1;
710 else return 0;
711 */
712 return faceconvexity(c, orient)<0 ? 1 : 0;
713 }
714
faceverts(cube & c,int orient,int vert)715 int faceverts(cube &c, int orient, int vert) // gets above 'fv' so that each face is convex
716 {
717 return fv[orient][(vert + faceorder(c, orient))&3];
718 }
719
faceedges(const cube & c,int orient,uchar edges[4])720 static inline void faceedges(const cube &c, int orient, uchar edges[4])
721 {
722 loopk(4) edges[k] = c.edges[faceedgesidx[orient][k]];
723 }
724
faceedges(cube & c,int orient)725 uint faceedges(cube &c, int orient)
726 {
727 union { uchar edges[4]; uint face; } u;
728 faceedges(c, orient, u.edges);
729 return u.face;
730 }
731
732 struct facevec
733 {
734 int x, y;
735
facevecfacevec736 facevec() {}
facevecfacevec737 facevec(int x, int y) : x(x), y(y) {}
738
operator ==facevec739 bool operator==(const facevec &f) const { return x == f.x && y == f.y; }
740 };
741
genfacevecs(cube & c,int orient,const ivec & pos,int size,bool solid,facevec * fvecs)742 static inline void genfacevecs(cube &c, int orient, const ivec &pos, int size, bool solid, facevec *fvecs)
743 {
744 int dim = dimension(orient), coord = dimcoord(orient);
745 const ushort *fvo = fv[orient];
746 loopi(4)
747 {
748 const ivec &cc = cubecoords[fvo[i]];
749 facevec &f = fvecs[coord ? i : 3 - i];
750 int x, y;
751 if(solid)
752 {
753 x = cc[C[dim]];
754 y = cc[R[dim]];
755 }
756 else
757 {
758 x = edgeval(c, cc, C[dim], cc[C[dim]]);
759 y = edgeval(c, cc, R[dim], cc[R[dim]]);
760 }
761 f.x = x*size/(8>>VVEC_FRAC)+(pos[C[dim]]<<VVEC_FRAC);
762 f.y = y*size/(8>>VVEC_FRAC)+(pos[R[dim]]<<VVEC_FRAC);
763 }
764 }
765
genoppositefacevecs(cube & c,int orient,const ivec & pos,int size,facevec * fvecs)766 static inline int genoppositefacevecs(cube &c, int orient, const ivec &pos, int size, facevec *fvecs)
767 {
768 int dim = dimension(orient), coord = dimcoord(orient), touching = 0;
769 const ushort *fvo = fv[orient];
770 loopi(4)
771 {
772 const ivec &cc = cubecoords[fvo[coord ? i : 3 - i]];
773 if(edgeval(c, cc, dim, cc[dim]) == coord*8)
774 {
775 int x = edgeval(c, cc, C[dim], cc[C[dim]]),
776 y = edgeval(c, cc, R[dim], cc[R[dim]]);
777 facevec &f = fvecs[touching++];
778 f.x = x*size/(8>>VVEC_FRAC)+(pos[C[dim]]<<VVEC_FRAC);
779 f.y = y*size/(8>>VVEC_FRAC)+(pos[R[dim]]<<VVEC_FRAC);
780 }
781 }
782 return touching;
783 }
784
clipfacevecy(const facevec & o,const facevec & dir,int cx,int cy,int size,facevec & r)785 static inline int clipfacevecy(const facevec &o, const facevec &dir, int cx, int cy, int size, facevec &r)
786 {
787 if(dir.x >= 0)
788 {
789 if(cx <= o.x || cx >= o.x+dir.x) return 0;
790 }
791 else if(cx <= o.x+dir.x || cx >= o.x) return 0;
792
793 int t = (o.y-cy) + (cx-o.x)*dir.y/dir.x;
794 if(t <= 0 || t >= size) return 0;
795
796 r.x = cx;
797 r.y = cy + t;
798 return 1;
799 }
800
clipfacevecx(const facevec & o,const facevec & dir,int cx,int cy,int size,facevec & r)801 static inline int clipfacevecx(const facevec &o, const facevec &dir, int cx, int cy, int size, facevec &r)
802 {
803 if(dir.y >= 0)
804 {
805 if(cy <= o.y || cy >= o.y+dir.y) return 0;
806 }
807 else if(cy <= o.y+dir.y || cy >= o.y) return 0;
808
809 int t = (o.x-cx) + (cy-o.y)*dir.x/dir.y;
810 if(t <= 0 || t >= size) return 0;
811
812 r.x = cx + t;
813 r.y = cy;
814 return 1;
815 }
816
clipfacevec(const facevec & o,const facevec & dir,int cx,int cy,int size,facevec * rvecs)817 static inline int clipfacevec(const facevec &o, const facevec &dir, int cx, int cy, int size, facevec *rvecs)
818 {
819 int r = 0;
820
821 if(o.x >= cx && o.x <= cx+size &&
822 o.y >= cy && o.y <= cy+size &&
823 ((o.x != cx && o.x != cx+size) || (o.y != cy && o.y != cy+size)))
824 {
825 rvecs[0].x = o.x;
826 rvecs[0].y = o.y;
827 r++;
828 }
829
830 r += clipfacevecx(o, dir, cx, cy, size, rvecs[r]);
831 r += clipfacevecx(o, dir, cx, cy+size, size, rvecs[r]);
832 r += clipfacevecy(o, dir, cx, cy, size, rvecs[r]);
833 r += clipfacevecy(o, dir, cx+size, cy, size, rvecs[r]);
834
835 ASSERT(r <= 2);
836 return r;
837 }
838
insideface(const facevec * p,int nump,const facevec * o,int numo)839 static inline bool insideface(const facevec *p, int nump, const facevec *o, int numo)
840 {
841 int bounds = 0;
842 loopi(numo)
843 {
844 const facevec &cur = o[i], &next = o[i+1 < numo ? i+1 : 0];
845 if(cur == next) continue;
846 facevec dir(next.x-cur.x, next.y-cur.y);
847 int offset = dir.x*cur.y - dir.y*cur.x;
848 loopj(nump) if(dir.x*p[j].y - dir.y*p[j].x > offset) return false;
849 bounds++;
850 }
851 return bounds>=3;
852 }
853
clipfacevecs(const facevec * o,int cx,int cy,int size,facevec * rvecs)854 static inline int clipfacevecs(const facevec *o, int cx, int cy, int size, facevec *rvecs)
855 {
856 cx <<= VVEC_FRAC;
857 cy <<= VVEC_FRAC;
858 size <<= VVEC_FRAC;
859
860 int r = 0;
861 loopi(4)
862 {
863 const facevec &cur = o[i], &next = o[(i+1)%4];
864 if(cur == next) continue;
865 facevec dir(next.x-cur.x, next.y-cur.y);
866 r += clipfacevec(cur, dir, cx, cy, size, &rvecs[r]);
867 }
868 facevec corner[4] = {facevec(cx, cy), facevec(cx+size, cy), facevec(cx+size, cy+size), facevec(cx, cy+size)};
869 loopi(4) if(insideface(&corner[i], 1, o, 4)) rvecs[r++] = corner[i];
870 ASSERT(r <= 8);
871 return r;
872 }
873
collapsedface(uint cfe)874 bool collapsedface(uint cfe)
875 {
876 return ((cfe >> 4) & 0x0F0F) == (cfe & 0x0F0F) ||
877 ((cfe >> 20) & 0x0F0F) == ((cfe >> 16) & 0x0F0F);
878 }
879
occludesface(cube & c,int orient,const ivec & o,int size,const ivec & vo,int vsize,uchar vmat,uchar nmat,uchar matmask,const facevec * vf)880 static inline bool occludesface(cube &c, int orient, const ivec &o, int size, const ivec &vo, int vsize, uchar vmat, uchar nmat, uchar matmask, const facevec *vf)
881 {
882 int dim = dimension(orient);
883 if(!c.children)
884 {
885 if(nmat != MAT_AIR && c.ext && (c.ext->material&matmask) == nmat)
886 {
887 facevec nf[8];
888 return clipfacevecs(vf, o[C[dim]], o[R[dim]], size, nf) < 3;
889 }
890 if(isentirelysolid(c)) return true;
891 if(vmat != MAT_AIR && c.ext && ((c.ext->material&matmask) == vmat || (isliquid(vmat) && isclipped(c.ext->material&MATF_VOLUME)))) return true;
892 if(touchingface(c, orient) && faceedges(c, orient) == F_SOLID) return true;
893 facevec cf[8];
894 int numc = clipfacevecs(vf, o[C[dim]], o[R[dim]], size, cf);
895 if(numc < 3) return true;
896 if(isempty(c)) return false;
897 facevec of[4];
898 int numo = genoppositefacevecs(c, orient, o, size, of);
899 return numo >= 3 && insideface(cf, numc, of, numo);
900 }
901
902 size >>= 1;
903 int coord = dimcoord(orient);
904 loopi(8) if(octacoord(dim, i) == coord)
905 {
906 if(!occludesface(c.children[i], orient, ivec(i, o.x, o.y, o.z, size), size, vo, vsize, vmat, nmat, matmask, vf)) return false;
907 }
908
909 return true;
910 }
911
visibleface(cube & c,int orient,int x,int y,int z,int size,uchar mat,uchar nmat,uchar matmask)912 bool visibleface(cube &c, int orient, int x, int y, int z, int size, uchar mat, uchar nmat, uchar matmask)
913 {
914 uint cfe = faceedges(c, orient);
915 if(mat != MAT_AIR)
916 {
917 if(cfe==F_SOLID && touchingface(c, orient)) return false;
918 }
919 else
920 {
921 if(!touchingface(c, orient)) return true;
922 if(collapsedface(cfe)) return false;
923 }
924
925 cube &o = neighbourcube(x, y, z, size, -size, orient);
926 if(&o==&c) return false;
927
928 if(lusize > size || (lusize == size && !o.children))
929 {
930 if(nmat != MAT_AIR && o.ext && (o.ext->material&matmask) == nmat) return true;
931 if(isentirelysolid(o)) return false;
932 if(mat != MAT_AIR && o.ext && ((o.ext->material&matmask) == mat || (isliquid(mat) && (o.ext->material&MATF_VOLUME) == MAT_GLASS))) return false;
933 if(isempty(o)) return true;
934 if(touchingface(o, opposite(orient)) && faceedges(o, opposite(orient)) == F_SOLID) return false;
935
936 ivec vo(x, y, z);
937 vo.mask(VVEC_INT_MASK);
938 lu.mask(VVEC_INT_MASK);
939 facevec cf[4], of[4];
940 genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf);
941 int numo = genoppositefacevecs(o, opposite(orient), lu, lusize, of);
942 return numo < 3 || !insideface(cf, 4, of, numo);
943 }
944
945 ivec vo(x, y, z);
946 vo.mask(VVEC_INT_MASK);
947 lu.mask(VVEC_INT_MASK);
948 facevec cf[4];
949 genfacevecs(c, orient, vo, size, mat != MAT_AIR, cf);
950 return !occludesface(o, opposite(orient), lu, lusize, vo, size, mat, nmat, matmask, cf);
951 }
952
953 // more expensive version that checks both triangles of a face independently
visibletris(cube & c,int orient,int x,int y,int z,int size)954 int visibletris(cube &c, int orient, int x, int y, int z, int size)
955 {
956 int dim = dimension(orient), coord = dimcoord(orient);
957 uint face = c.faces[dim];
958 if(coord) face = (face&0xF0F0F0F0)^0x80808080;
959 else face &= 0x0F0F0F0F;
960
961 int notouch = 0;
962 if(face&0xFF) notouch++;
963 if(face&0xFF00) notouch++;
964 if(face&0xFF0000) notouch++;
965 if(face&0xFF000000) notouch++;
966 if(notouch>=2) return 3;
967
968 if(collapsedface(faceedges(c, orient))) return 0;
969
970 cube &o = neighbourcube(x, y, z, size, -size, orient);
971 if(&o==&c) return 0;
972
973 ivec vo(x, y, z);
974 vo.mask(VVEC_INT_MASK);
975 lu.mask(VVEC_INT_MASK);
976 facevec cf[4], of[4];
977 int opp = opposite(orient), numo = 0;
978 if(lusize > size || (lusize == size && !o.children))
979 {
980 if(isempty(o)) return 3;
981 if(!notouch && (isentirelysolid(o) || (touchingface(o, opp) && faceedges(o, opp) == F_SOLID))) return 0;
982
983 genfacevecs(c, orient, vo, size, false, cf);
984 numo = genoppositefacevecs(o, opp, lu, lusize, of);
985 if(numo < 3) return 3;
986 if(!notouch && insideface(cf, 4, of, numo)) return 0;
987 }
988 else
989 {
990 genfacevecs(c, orient, vo, size, false, cf);
991 if(!notouch && occludesface(o, opp, lu, lusize, vo, size, MAT_AIR, MAT_AIR, MATF_VOLUME, cf)) return 0;
992 }
993
994 static const int trimasks[2][2] = { { 0x7, 0xD }, { 0xE, 0xB } };
995 static const int triverts[2][2][2][3] =
996 { // order
997 { // coord
998 { { 1, 2, 3 }, { 0, 1, 3 } }, // verts
999 { { 0, 1, 2 }, { 0, 2, 3 } }
1000 },
1001 { // coord
1002 { { 0, 1, 2 }, { 3, 0, 2 } }, // verts
1003 { { 1, 2, 3 }, { 1, 3, 0 } }
1004 }
1005 };
1006
1007 int convex = faceconvexity(c, orient),
1008 order = convex < 0 ? 1 : 0,
1009 vis = 3,
1010 touching = 0;
1011 loopi(4)
1012 {
1013 const ivec &cc = cubecoords[fv[orient][i]];
1014 if(edgeval(c, cc, dim, cc[dim]) == coord*8) touching |= 1<<i;
1015 }
1016 facevec tf[4];
1017
1018 for(;;)
1019 {
1020 loopi(2) if((touching&trimasks[order][i])==trimasks[order][i])
1021 {
1022 const int *verts = triverts[order][coord][i];
1023 int v1 = verts[0], v2 = verts[1], v3 = verts[2];
1024 if(cf[v1]==cf[v2] || cf[v2]==cf[v3] || cf[v3]==cf[v1]) { notouch = 1; continue; }
1025 tf[0] = cf[v1]; tf[1] = cf[v2]; tf[2] = cf[v3];
1026 if(!numo)
1027 {
1028 tf[3] = cf[v3];
1029 if(!occludesface(o, opp, lu, lusize, vo, size, MAT_AIR, MAT_AIR, MATF_VOLUME, tf)) continue;
1030 }
1031 else if(!insideface(tf, 3, of, numo)) continue;
1032 return vis & ~(1<<i);
1033 }
1034 if(notouch || vis&4) break;
1035 if(c.ext && c.ext->surfaces) // compat for old lightmaps that can't be reordered
1036 {
1037 const uchar *tc = c.ext->surfaces[orient].texcoords;
1038 if((tc[0]!=tc[6] || tc[1]!=tc[7]) && (tc[0]!=tc[2] || tc[1]!=tc[3])) break;
1039 }
1040 vis |= 4;
1041 order++;
1042 }
1043
1044 return 3;
1045 }
1046
calcvert(cube & c,int x,int y,int z,int size,vvec & v,int i,bool solid)1047 void calcvert(cube &c, int x, int y, int z, int size, vvec &v, int i, bool solid)
1048 {
1049 ivec vv;
1050 if(solid || isentirelysolid(c)) vv = cubecoords[i];
1051 else genvectorvert(cubecoords[i], c, vv);
1052 v = vvec(vv.v);
1053 // avoid overflow
1054 if(size>=8) v.mul(size/8);
1055 else v.div(8/size);
1056 v.add(vvec(x, y, z));
1057 }
1058
calcvert(cube & c,int x,int y,int z,int size,vec & v,int i,bool solid)1059 void calcvert(cube &c, int x, int y, int z, int size, vec &v, int i, bool solid)
1060 {
1061 ivec vv;
1062 if(solid || isentirelysolid(c)) vv = cubecoords[i];
1063 else genvectorvert(cubecoords[i], c, vv);
1064 v = vv.tovec().mul(size/8.0f).add(vec(x, y, z));
1065 }
1066
calcverts(cube & c,int x,int y,int z,int size,vvec * verts,bool * usefaces)1067 int calcverts(cube &c, int x, int y, int z, int size, vvec *verts, bool *usefaces)
1068 {
1069 int vertused = 0;
1070 loopi(6) if((usefaces[i] = visibleface(c, i, x, y, z, size))) vertused |= fvmasks[1<<i];
1071 //loopk(4) vertused |= 1<<faceverts(c,i,k);
1072 loopi(8) if(vertused&(1<<i)) calcvert(c, x, y, z, size, verts[i], i);
1073 return vertused;
1074 }
1075
genclipplane(cube & c,int orient,vec * v,plane * clip)1076 int genclipplane(cube &c, int orient, vec *v, plane *clip)
1077 {
1078 int planes = 0;
1079 vec p[4];
1080 loopk(4) p[k] = v[faceverts(c,orient,k)];
1081
1082 if(p[0]==p[2]) return 0;
1083 if(p[0]!=p[1] && p[1]!=p[2]) clip[planes++].toplane(p[0], p[1], p[2]);
1084 if(p[0]!=p[3] && p[2]!=p[3] && (!planes || faceconvexity(c, orient))) clip[planes++].toplane(p[0], p[2], p[3]);
1085 return planes;
1086 }
1087
genclipplanes(cube & c,int x,int y,int z,int size,clipplanes & p)1088 void genclipplanes(cube &c, int x, int y, int z, int size, clipplanes &p)
1089 {
1090 bool usefaces[6];
1091 vvec sv[8];
1092 vec mx(x, y, z), mn(x+size, y+size, z+size);
1093 int vertused = calcverts(c, x, y, z, size, sv, usefaces);
1094
1095 loopi(8)
1096 {
1097 if(!(vertused&(1<<i))) // need all verts for proper box
1098 calcvert(c, x, y, z, size, sv[i], i);
1099
1100 p.v[i] = sv[i].tovec(x, y, z);
1101
1102 loopj(3) // generate tight bounding box
1103 {
1104 mn[j] = min(mn[j], p.v[i].v[j]);
1105 mx[j] = max(mx[j], p.v[i].v[j]);
1106 }
1107 }
1108
1109 p.r = mx; // radius of box
1110 p.r.sub(mn);
1111 p.r.mul(0.5f);
1112 p.o = mn; // center of box
1113 p.o.add(p.r);
1114
1115 p.size = 0;
1116 p.visible = c.ext ? c.ext->visible : 0;
1117 loopi(6) if(usefaces[i] && !touchingface(c, i)) // generate actual clipping planes
1118 {
1119 if(flataxisface(c, i)) p.visible |= 1<<i;
1120 else p.size += genclipplane(c, i, p.v, &p.p[p.size]);
1121 }
1122 }
1123
mergefacecmp(const cubeface * x,const cubeface * y)1124 static int mergefacecmp(const cubeface *x, const cubeface *y)
1125 {
1126 if(x->v2 < y->v2) return -1;
1127 if(x->v2 > y->v2) return 1;
1128 if(x->u1 < y->u1) return -1;
1129 if(x->u1 > y->u1) return 1;
1130 return 0;
1131 }
1132
mergefacev(int orient,cubeface * m,int sz,cubeface & n)1133 static int mergefacev(int orient, cubeface *m, int sz, cubeface &n)
1134 {
1135 for(int i = sz-1; i >= 0; --i)
1136 {
1137 if(m[i].v2 < n.v1) break;
1138 if(m[i].v2 == n.v1 && m[i].u1 == n.u1 && m[i].u2 == n.u2)
1139 {
1140 if(m[i].c) ext(*m[i].c).merged |= 1<<orient;
1141 if(n.c) ext(*n.c).merged |= 1<<orient;
1142 n.v1 = m[i].v1;
1143 memmove(&m[i], &m[i+1], (sz - (i+1)) * sizeof(cubeface));
1144 return 1;
1145 }
1146 }
1147 return 0;
1148 }
1149
mergefaceu(int orient,cubeface & m,cubeface & n)1150 static int mergefaceu(int orient, cubeface &m, cubeface &n)
1151 {
1152 if(m.v1 == n.v1 && m.v2 == n.v2 && m.u2 == n.u1)
1153 {
1154 if(m.c) ext(*m.c).merged |= 1<<orient;
1155 if(n.c) ext(*n.c).merged |= 1<<orient;
1156 n.u1 = m.u1;
1157 return 1;
1158 }
1159 return 0;
1160 }
1161
mergeface(int orient,cubeface * m,int sz,cubeface & n)1162 static int mergeface(int orient, cubeface *m, int sz, cubeface &n)
1163 {
1164 for(bool merged = false; sz; merged = true)
1165 {
1166 int vmerged = mergefacev(orient, m, sz, n);
1167 sz -= vmerged;
1168 if(!vmerged && merged) break;
1169 if(!sz) break;
1170 int umerged = mergefaceu(orient, m[sz-1], n);
1171 sz -= umerged;
1172 if(!umerged) break;
1173 }
1174 m[sz++] = n;
1175 return sz;
1176 }
1177
mergefaces(int orient,cubeface * m,int sz)1178 int mergefaces(int orient, cubeface *m, int sz)
1179 {
1180 quicksort(m, sz, mergefacecmp);
1181
1182 int nsz = 0;
1183 loopi(sz) nsz = mergeface(orient, m, nsz, m[i]);
1184 return nsz;
1185 }
1186
1187 struct cfkey
1188 {
1189 uchar orient;
1190 ushort tex;
1191 ivec n;
1192 int offset;
1193 };
1194
htcmp(const cfkey & x,const cfkey & y)1195 static inline bool htcmp(const cfkey &x, const cfkey &y)
1196 {
1197 return x.orient == y.orient && x.tex == y.tex && x.n == y.n && x.offset == y.offset;
1198 }
1199
hthash(const cfkey & k)1200 static inline uint hthash(const cfkey &k)
1201 {
1202 return hthash(k.n)^k.offset^k.tex^k.orient;
1203 }
1204
1205 struct cfval
1206 {
1207 vector<cubeface> faces;
1208 };
1209
1210 static hashtable<cfkey, cfval> cfaces;
1211
mincubeface(cube & cu,int orient,const ivec & o,int size,const mergeinfo & orig,mergeinfo & cf)1212 void mincubeface(cube &cu, int orient, const ivec &o, int size, const mergeinfo &orig, mergeinfo &cf)
1213 {
1214 int dim = dimension(orient);
1215 if(cu.children)
1216 {
1217 size >>= 1;
1218 int coord = dimcoord(orient);
1219 loopi(8) if(octacoord(dim, i) == coord)
1220 mincubeface(cu.children[i], orient, ivec(i, o.x, o.y, o.z, size), size, orig, cf);
1221 return;
1222 }
1223 int c = C[dim], r = R[dim];
1224 ushort uco = ushort((o[c]&VVEC_INT_MASK)<<VVEC_FRAC), vco = ushort((o[r]&VVEC_INT_MASK)<<VVEC_FRAC);
1225 ushort uc1 = uco, vc1 = vco, uc2 = ushort(size<<VVEC_FRAC)+uco, vc2 = ushort(size<<VVEC_FRAC)+vco;
1226 uc1 = max(uc1, orig.u1);
1227 uc2 = min(uc2, orig.u2);
1228 vc1 = max(vc1, orig.v1);
1229 vc2 = min(vc2, orig.v2);
1230 if(!isempty(cu) && touchingface(cu, orient))
1231 {
1232 uchar r1 = cu.edges[faceedgesrcidx[orient][0]], r2 = cu.edges[faceedgesrcidx[orient][1]],
1233 c1 = cu.edges[faceedgesrcidx[orient][2]], c2 = cu.edges[faceedgesrcidx[orient][3]];
1234 ushort scale = ushort(size/(8>>VVEC_FRAC)),
1235 u1 = max(c1&0xF, c2&0xF)*scale+uco, u2 = min(c1>>4, c2>>4)*scale+uco,
1236 v1 = max(r1&0xF, r2&0xF)*scale+vco, v2 = min(r1>>4, r2>>4)*scale+vco;
1237 u1 = max(u1, orig.u1);
1238 u2 = min(u2, orig.u2);
1239 v1 = max(v1, orig.v1);
1240 v2 = min(v2, orig.v2);
1241 if(v2-v1==vc2-vc1)
1242 {
1243 if(u2-u1==uc2-uc1) return;
1244 if(u1==uc1) uc1 = u2;
1245 if(u2==uc2) uc2 = u1;
1246 }
1247 else if(u2-u1==uc2-uc1)
1248 {
1249 if(v1==vc1) vc1 = v2;
1250 if(v2==vc2) vc2 = v1;
1251 }
1252 }
1253 if(uc1==uc2 || vc1==vc2) return;
1254 cf.u1 = min(cf.u1, uc1);
1255 cf.u2 = max(cf.u2, uc2);
1256 cf.v1 = min(cf.v1, vc1);
1257 cf.v2 = max(cf.v2, vc2);
1258 }
1259
mincubeface(cube & cu,int orient,const ivec & co,int size,mergeinfo & orig)1260 bool mincubeface(cube &cu, int orient, const ivec &co, int size, mergeinfo &orig)
1261 {
1262 cube &nc = neighbourcube(co.x, co.y, co.z, size, -size, orient);
1263 mergeinfo mincf;
1264 mincf.u1 = orig.u2;
1265 mincf.u2 = orig.u1;
1266 mincf.v1 = orig.v2;
1267 mincf.v2 = orig.v1;
1268 mincubeface(nc, opposite(orient), lu, lusize, orig, mincf);
1269 bool smaller = false;
1270 if(mincf.u1 > orig.u1) { orig.u1 = mincf.u1; smaller = true; }
1271 if(mincf.u2 < orig.u2) { orig.u2 = mincf.u2; smaller = true; }
1272 if(mincf.v1 > orig.v1) { orig.v1 = mincf.v1; smaller = true; }
1273 if(mincf.v2 < orig.v2) { orig.v2 = mincf.v2; smaller = true; }
1274 return smaller;
1275 }
1276
1277 VAR(minface, 0, 1, 1);
1278
gencubeface(cube & cu,int orient,const ivec & co,int size,ivec & n,int & offset,cubeface & cf)1279 bool gencubeface(cube &cu, int orient, const ivec &co, int size, ivec &n, int &offset, cubeface &cf)
1280 {
1281 uchar cfe[4];
1282 faceedges(cu, orient, cfe);
1283 if(cfe[0]!=cfe[1] || cfe[2]!=cfe[3] || (cfe[0]>>4)==(cfe[0]&0xF) || (cfe[2]>>4)==(cfe[2]&0xF)) return false;
1284 if(faceconvexity(cu, orient)) return false;
1285
1286 cf.c = &cu;
1287
1288 ivec v[4];
1289 loopi(4) genvectorvert(cubecoords[fv[orient][i]], cu, v[i]);
1290
1291 int scale = size/(8>>VVEC_FRAC);
1292 v[3].mul(scale);
1293 int dim = dimension(orient), c = C[dim], r = R[dim];
1294 cf.u1 = cf.u2 = ushort(v[3][c]);
1295 cf.v1 = cf.v2 = ushort(v[3][r]);
1296
1297 loopi(3)
1298 {
1299 ushort uc = ushort(v[i][c]*scale), vc = ushort(v[i][r]*scale);
1300 cf.u1 = min(cf.u1, uc);
1301 cf.u2 = max(cf.u2, uc);
1302 cf.v1 = min(cf.v1, vc);
1303 cf.v2 = max(cf.v2, vc);
1304 }
1305
1306 ivec vo(co);
1307 vo.mask(VVEC_INT_MASK);
1308 vo.mul(1<<VVEC_FRAC);
1309
1310 ushort uco = ushort(vo[c]), vco = ushort(vo[r]);
1311 cf.u1 += uco;
1312 cf.u2 += uco;
1313 cf.v1 += vco;
1314 cf.v2 += vco;
1315
1316 v[1].sub(v[0]);
1317 v[2].sub(v[0]);
1318 n.cross(v[1], v[2]);
1319
1320 // reduce the normal as much as possible without resorting to floating point
1321 int mindim = -1, minval = 64;
1322 loopi(3) if(n[i])
1323 {
1324 int val = abs(n[i]);
1325 if(mindim < 0 || val < minval)
1326 {
1327 mindim = i;
1328 minval = val;
1329 }
1330 }
1331 if(!(n[R[mindim]]%minval) && !(n[C[mindim]]%minval))
1332 {
1333 n[mindim] /= minval;
1334 n[R[mindim]] /= minval;
1335 n[C[mindim]] /= minval;
1336 }
1337 while((n[0]&1)==0 && (n[1]&1)==0 && (n[2]&1)==0)
1338 {
1339 n[0] >>= 1;
1340 n[1] >>= 1;
1341 n[2] >>= 1;
1342 }
1343
1344 v[3].add(vo);
1345 offset = -n.dot(v[3]);
1346
1347 if(minface && touchingface(cu, orient) && mincubeface(cu, orient, co, size, cf))
1348 {
1349 ext(cu).merged |= 1<<orient;
1350 }
1351
1352 return true;
1353 }
1354
addmergeinfo(cube & c,int orient,cubeface & cf)1355 void addmergeinfo(cube &c, int orient, cubeface &cf)
1356 {
1357 if(!c.ext) newcubeext(c);
1358 int index = 0;
1359 loopi(orient) if(c.ext->mergeorigin&(1<<i)) index++;
1360 int total = index;
1361 loopi(6-orient-1) if(c.ext->mergeorigin&(1<<(i+orient+1))) total++;
1362 mergeinfo *m = new mergeinfo[total+1];
1363 if(index) memcpy(m, c.ext->merges, index*sizeof(mergeinfo));
1364 if(total>index) memcpy(&m[index+1], &c.ext->merges[index], (total-index)*sizeof(mergeinfo));
1365 if(c.ext->merges) delete[] c.ext->merges;
1366 c.ext->merges = m;
1367 m += index;
1368 c.ext->mergeorigin |= 1<<orient;
1369 m->u1 = cf.u1;
1370 m->u2 = cf.u2;
1371 m->v1 = cf.v1;
1372 m->v2 = cf.v2;
1373 }
1374
freemergeinfo(cube & c)1375 void freemergeinfo(cube &c)
1376 {
1377 if(!c.ext) return;
1378 c.ext->mergeorigin = 0;
1379 DELETEA(c.ext->merges);
1380 }
1381
1382 VAR(maxmerge, 0, 6, VVEC_INT-1);
1383
1384 static int genmergeprogress = 0;
1385
genmergeinfo(cube * c=worldroot,const ivec & o=ivec (0,0,0),int size=hdr.worldsize>>1)1386 void genmergeinfo(cube *c = worldroot, const ivec &o = ivec(0, 0, 0), int size = hdr.worldsize>>1)
1387 {
1388 if((genmergeprogress++&0x7FF)==0) progress(float(genmergeprogress)/allocnodes, "merging surfaces...");
1389 loopi(8)
1390 {
1391 ivec co(i, o.x, o.y, o.z, size);
1392 if(c[i].ext)
1393 {
1394 if(c[i].ext->merges) freemergeinfo(c[i]);
1395 c[i].ext->merged = 0;
1396 }
1397 if(c[i].children) genmergeinfo(c[i].children, co, size>>1);
1398 else if(!isempty(c[i])) loopj(6) if(visibleface(c[i], j, co.x, co.y, co.z, size))
1399 {
1400 cfkey k;
1401 cubeface cf;
1402 if(gencubeface(c[i], j, co, size, k.n, k.offset, cf))
1403 {
1404 if(size >= 1<<maxmerge || c == worldroot)
1405 {
1406 if(c[i].ext && c[i].ext->merged&(1<<j)) addmergeinfo(c[i], j, cf);
1407 continue;
1408 }
1409 k.orient = j;
1410 k.tex = c[i].texture[j];
1411 cfaces[k].faces.add(cf);
1412 }
1413 }
1414 if((size == 1<<maxmerge || c == worldroot) && cfaces.numelems)
1415 {
1416 ASSERT(size <= 1<<maxmerge);
1417 enumeratekt(cfaces, cfkey, key, cfval, val,
1418 val.faces.setsize(mergefaces(key.orient, val.faces.getbuf(), val.faces.length()));
1419 loopvj(val.faces) if(val.faces[j].c->ext && val.faces[j].c->ext->merged&(1<<key.orient))
1420 {
1421 addmergeinfo(*val.faces[j].c, key.orient, val.faces[j]);
1422 }
1423 );
1424 cfaces.clear();
1425 }
1426
1427 }
1428 }
1429
genmergedverts(cube & cu,int orient,const ivec & co,int size,const mergeinfo & m,vvec * vv,plane * p)1430 void genmergedverts(cube &cu, int orient, const ivec &co, int size, const mergeinfo &m, vvec *vv, plane *p)
1431 {
1432 ivec vo(co);
1433 vo.mask(VVEC_INT_MASK);
1434 vo.mul(1<<VVEC_FRAC);
1435
1436 ivec v[3], n;
1437 loopi(3) genvectorvert(cubecoords[faceverts(cu, orient, i)], cu, v[i]);
1438 v[1].sub(v[0]);
1439 v[2].sub(v[0]);
1440 n.cross(v[1], v[2]);
1441
1442 ASSERT(size >= 8>>VVEC_FRAC);
1443 v[0].mul(size/(8>>VVEC_FRAC));
1444 v[0].add(vo);
1445 int offset = -n.dot(v[0]);
1446
1447 int dim = dimension(orient), c = C[dim], r = R[dim];
1448 loopi(4)
1449 {
1450 const ivec &coords = cubecoords[fv[orient][i]];
1451 int cc = coords[c] ? m.u2 : m.u1,
1452 rc = coords[r] ? m.v2 : m.v1,
1453 dc = -(offset + n[c]*cc + n[r]*rc)/n[dim];
1454 vv[i][c] = ushort(cc);
1455 vv[i][r] = ushort(rc);
1456 vv[i][dim] = ushort(dc);
1457 }
1458
1459 if(p)
1460 {
1461 ivec po(co);
1462 po.mask(~VVEC_INT_MASK);
1463 vec pn(n.tovec());
1464 float mag = pn.magnitude();
1465 pn.div(mag);
1466 *p = plane(pn, (offset-(n.dot(po)<<VVEC_FRAC))/(mag*(1<<VVEC_FRAC)));
1467 }
1468 }
1469
calcmergedsize(int orient,const ivec & co,int size,const mergeinfo & m,const vvec * vv)1470 int calcmergedsize(int orient, const ivec &co, int size, const mergeinfo &m, const vvec *vv)
1471 {
1472 int dim = dimension(orient), c = C[dim], r = R[dim];
1473 ushort d1 = vv[3][dim], d2 = d1;
1474 loopi(3)
1475 {
1476 d1 = min(d1, vv[i][dim]);
1477 d2 = max(d2, vv[i][dim]);
1478 }
1479 int bits = 0;
1480 while(1<<bits < size) ++bits;
1481 bits += VVEC_FRAC;
1482 ivec mo(co);
1483 mo.mask(VVEC_INT_MASK);
1484 mo.mul(1<<VVEC_FRAC);
1485 while(bits<VVEC_INT+VVEC_FRAC-1)
1486 {
1487 mo.mask(~((1<<bits)-1));
1488 if(mo[dim] <= d1 && mo[dim] + (1<<bits) >= d2 &&
1489 mo[c] <= m.u1 && mo[c] + (1<<bits) >= m.u2 &&
1490 mo[r] <= m.v1 && mo[r] + (1<<bits) >= m.v2)
1491 break;
1492 bits++;
1493 }
1494 return bits-VVEC_FRAC;
1495 }
1496
invalidatemerges(cube & c)1497 static void invalidatemerges(cube &c)
1498 {
1499 if(c.ext)
1500 {
1501 if(c.ext->va)
1502 {
1503 if(!(c.ext->va->hasmerges&(MERGE_PART | MERGE_ORIGIN))) return;
1504 destroyva(c.ext->va);
1505 c.ext->va = NULL;
1506 }
1507 if(c.ext->merged)
1508 {
1509 brightencube(c);
1510 c.ext->merged = 0;
1511 if(c.ext->merges) freemergeinfo(c);
1512 }
1513 if(c.ext->tjoints >= 0) c.ext->tjoints = -1;
1514 }
1515 if(c.children) loopi(8) invalidatemerges(c.children[i]);
1516 }
1517
1518 static int invalidatedmerges = 0;
1519
invalidatemerges(cube & c,bool msg)1520 void invalidatemerges(cube &c, bool msg)
1521 {
1522 if(msg && invalidatedmerges!=totalmillis)
1523 {
1524 progress(0, "invalidating merged surfaces...");
1525 invalidatedmerges = totalmillis;
1526 }
1527 invalidatemerges(c);
1528 }
1529
calcmerges()1530 void calcmerges()
1531 {
1532 genmergeprogress = 0;
1533 genmergeinfo();
1534 }
1535
1536