diff --git a/modules/voxels/octree.js b/modules/voxels/octree.js new file mode 100644 index 00000000..625d4130 --- /dev/null +++ b/modules/voxels/octree.js @@ -0,0 +1,126 @@ +import * as vec from "../../web/app/math/vec"; + +export class Node { + + constructor() { + this.nodes = null; + this.tag = 0; + } + + + get leaf() { + return this.nodes == null; + } + + breakDown() { + this.nodes = [new Node(), new Node(), new Node(), new Node(), new Node(), new Node(), new Node(), new Node()]; + } +} + +const directors = [ + [0,0,0], [1,0,0], [0,1,0], [1,1,0], + [0,0,1], [1,0,1], [0,1,1], [1,1,1] +]; + +export function traverseOctree(root, baseSize, handler) { + + const stack = []; + + stack.push([root, [0,0,0], baseSize]); + + while (stack.length !== 0) { + + const [node, [x,y,z], size] = stack.pop(); + if (node.leaf) { + handler(x, y, z, size, node.tag); + continue; + } + const subSize = size / 2; + + for (let i = 0; i < 8; i ++) { + const subNode = node.nodes[i]; + if (subNode) { + const [dx, dy, dz] = directors[i]; + const subLocation = [x + dx*subSize, y + dy*subSize, z + dz*subSize]; + stack.push([subNode, subLocation, subSize]); + } + } + + } + +} + +export function pushVoxel(root, baseSize, [vx, vy, vz], tag) { + const stack = []; + + stack.push([root, [0,0,0], baseSize]); + + while (stack.length !== 0) { + + const [node, [x,y,z], size] = stack.pop(); + + if (size === 1 && x === vx && y === vy && z === vz) { + node.tag = tag; + return; + } + if (size === 1) { + continue; + } + if (node.leaf) { + node.breakDown(); + } + + const subSize = size / 2; + + for (let i = 0; i < 8; i ++) { + const [dx, dy, dz] = directors[i]; + const subLocation = [x + dx*subSize, y + dy*subSize, z + dz*subSize]; + const [sx1, sy1, sz1] = subLocation; + const [sx2, sy2, sz2] = subLocation.map(v => v + subSize); + if (vx >= sx1 && vy >= sy1 && vz >= sz1 && vx < sx2 && vy < sy2 && vz < sz2) { + const subNode = node.nodes[i]; + stack.push([subNode, subLocation, subSize]); + } + } + } +} + +export function createOctreeFromSurface(origin, sceneSize, treeSize, surface, tag) { + + const root = new Node(); + + const stack = []; + + const [uOMin, uOMax] = surface.domainU; + const [vOMin, vOMax] = surface.domainV; + + stack.push([[uOMin, vOMin], [uOMax, vOMax]]); + + const resolution = sceneSize / treeSize; + const rSq = resolution * resolution; + + while (stack.length !== 0) { + + const [[uMin, vMin], [uMax, vMax]] = stack.pop(); + + const pMin = surface.point(uMin, vMin); + const pMax = surface.point(uMax, vMax); + + if (vec.distanceSq(pMin, pMax) < rSq) { + const voxel = vec.sub(pMin, origin); + vec._div(voxel, resolution); + vec.scalarOperand(voxel, voxel, v => Math.floor(v)); + + pushVoxel(root, treeSize, voxel, tag); + } else { + const uMid = uMin + (uMax - uMin) / 2; + const vMid = vMin + (vMax - vMin) / 2; + stack.push([[uMin, vMin], [uMid, vMid]]); + stack.push([[uMid, vMin], [uMax, vMid]]); + stack.push([[uMid, vMid], [uMax, vMax]]); + stack.push([[uMin, vMid], [uMid, vMax]]); + } + } + + return root; +}