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