mirror of
https://github.com/xibyte/jsketcher
synced 2025-12-15 12:53:52 +01:00
voxels
This commit is contained in:
parent
2675355307
commit
2e97a8a9bf
1 changed files with 126 additions and 0 deletions
126
modules/voxels/octree.js
Normal file
126
modules/voxels/octree.js
Normal file
|
|
@ -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;
|
||||
}
|
||||
Loading…
Reference in a new issue