from libs import * import heapq import mesh def intersect_box(ro, rd, c, s): m = 1 / rd n = m * (ro - c) k = np.abs(m) * s tn = np.max(-n - k) tf = np.min(-n + k) if tf > tn and tf > 0: return tn, tf else: return None, None class WorldRegion(mesh.SquareMesh): def __init__(self, size=16): super().__init__() self.grid = np.zeros((size, size, size), dtype=VOXEL_TYPE) self.r = size self.dirty = 0 def set_voxel(self, pos, vt=0): self.grid[*pos] = vt bp = bool(vt) for d in range(3): for s in range(2): if pos[d] == self.r - 1 and s == 0 or pos[d] == 0 and s == 1: continue a = (d + 1) * (-1) ** s o = np.array(axis2offset(a), dtype=np.int64) bc = bool(self.grid[*(pos + o)]) if bp ^ bc: a = (bp - bc) * (d + 1) self.add_sq(a, 1, pos + o / 2, pos + (0 if bp else o)) self.dirty += 1 if self.dirty > self.r: self.dirty = 0 self.remesh() def remesh(self): offset = vec([0.5, 0.0, 0.0]) self.faces = [] prev, curr = 0, 0 vl = [] for d in range(3): offset = np.array(axis2offset(d + 1)) for i in range(self.r): for j in range(self.r): for k in range(self.r + 1): p = np.array([k, i, j]) roll_vec3(p, d) prev, curr = curr, (self.grid[*p] if k < self.r else 0) bp, bc = bool(prev), bool(curr) if bp ^ bc: sq = vec([ [0, 1, 1], [0, -1, 1], [0, 1, -1], [0, -1, -1], [0, 1, -1], [0, -1, 1] ]) sq /= 2 * (bp - bc) for v in sq: roll_vec3(v, d) sq += p - offset / 2 vl.extend(sq) if bp: p -= offset for _ in range(2): self.faces.append(((bp - bc) * (d + 1), p)) self.verts = vec(vl) def raycast(self, ro, rd): vr = np.repeat(self.r, 3) tn, tf = intersect_box(ro, rd, self.pos + vr / 2 - 0.5, vr / 2) if tn is not None: if tn < 0: tn = 0 ids = [[(d, i) for d in ((np.arange(self.r) + 0.5) * np.sign(rd[i]) - np.mod(ro[i] + tn * rd[i], 1)) / rd[i]] for i in range(3)] for (te, i) in heapq.merge(*ids): ts = tn + te p = np.astype(np.round(ro + (ts + 1e-2) * rd - self.pos), np.int16) if any(p < 0) or any(p >= self.r): break elif self.grid[*p]: sa = np.astype((i + 1) * -np.sign(rd[i]), np.int8) return ts, (sa, p) return None, None