mirror of
https://github.com/LucasDower/ObjToSchematic.git
synced 2024-12-21 03:09:14 +08:00
Transition to ray-based voxelisation
This commit is contained in:
parent
c7238ef214
commit
80be8d8a62
@ -110,7 +110,7 @@ export class AppContext {
|
||||
}
|
||||
|
||||
private _voxelise(): ReturnStatus {
|
||||
const newVoxelSize = $("#inputVoxelSize").prop('value');
|
||||
const newVoxelSize: number = parseFloat($("#inputVoxelSize").prop('value'));
|
||||
if (newVoxelSize < 0.001) {
|
||||
return { message: "Voxel size must be at least 0.001", style: ToastStyle.Failure };
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ export class SegmentedBuffer {
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
//console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
@ -303,7 +303,7 @@ export class BottomlessBuffer {
|
||||
}
|
||||
|
||||
if (this._willOverflow(data)) {
|
||||
console.log("Cycling buffer...");
|
||||
//console.log("Cycling buffer...");
|
||||
this._cycle();
|
||||
}
|
||||
|
||||
|
12
src/math.ts
12
src/math.ts
@ -21,6 +21,18 @@ export const clamp = (value: number, min: number, max: number) => {
|
||||
return Math.max(Math.min(max, value), min);
|
||||
}
|
||||
|
||||
export const floorToNearest = (value: number, base: number) => {
|
||||
return Math.floor(value / base) * base;
|
||||
}
|
||||
|
||||
export const ceilToNearest = (value: number, base: number) => {
|
||||
return Math.ceil(value / base) * base;
|
||||
}
|
||||
|
||||
export const roundToNearest = (value: number, base: number) => {
|
||||
return Math.round(value / base) * base;
|
||||
}
|
||||
|
||||
export const triangleArea = (a: number, b: number, c: number) => {
|
||||
const p = (a + b + c) / 2;
|
||||
return Math.sqrt(p * (p - a) * (p - b) * (p - c));
|
||||
|
120
src/ray.ts
Normal file
120
src/ray.ts
Normal file
@ -0,0 +1,120 @@
|
||||
import { Vector3 } from "./vector";
|
||||
import { Triangle } from "./triangle";
|
||||
import { floorToNearest, ceilToNearest, xAxis, yAxis, zAxis } from "./math";
|
||||
import { VoxelManager } from "./voxel_manager";
|
||||
|
||||
const EPSILON = 0.0000001;
|
||||
|
||||
export enum Axes {
|
||||
x, y, z
|
||||
}
|
||||
|
||||
interface Ray {
|
||||
origin: Vector3,
|
||||
direction: Vector3,
|
||||
axis: Axes
|
||||
}
|
||||
|
||||
interface Bounds {
|
||||
minX: number,
|
||||
minY: number,
|
||||
minZ: number,
|
||||
maxX: number,
|
||||
maxY: number,
|
||||
maxZ: number,
|
||||
}
|
||||
|
||||
export function generateRays(triangle: Triangle): Array<Ray> {
|
||||
const voxelSize = VoxelManager.Get._voxelSize;
|
||||
|
||||
const bounds: Bounds = {
|
||||
minX: floorToNearest(Math.min(triangle.v0.position.x, triangle.v1.position.x, triangle.v2.position.x), voxelSize),
|
||||
minY: floorToNearest(Math.min(triangle.v0.position.y, triangle.v1.position.y, triangle.v2.position.y), voxelSize),
|
||||
minZ: floorToNearest(Math.min(triangle.v0.position.z, triangle.v1.position.z, triangle.v2.position.z), voxelSize),
|
||||
maxX: ceilToNearest(Math.max(triangle.v0.position.x, triangle.v1.position.x, triangle.v2.position.x), voxelSize),
|
||||
maxY: ceilToNearest(Math.max(triangle.v0.position.y, triangle.v1.position.y, triangle.v2.position.y), voxelSize),
|
||||
maxZ: ceilToNearest(Math.max(triangle.v0.position.z, triangle.v1.position.z, triangle.v2.position.z), voxelSize),
|
||||
}
|
||||
|
||||
let rayList: Array<Ray> = [];
|
||||
if (Math.abs(triangle.normal.x) >= 0.5) {
|
||||
traverseX(rayList, bounds, voxelSize);
|
||||
}
|
||||
if (Math.abs(triangle.normal.y) >= 0.5) {
|
||||
traverseY(rayList, bounds, voxelSize);
|
||||
}
|
||||
if (Math.abs(triangle.normal.z) >= 0.5) {
|
||||
traverseZ(rayList, bounds, voxelSize);
|
||||
}
|
||||
|
||||
return rayList;
|
||||
}
|
||||
|
||||
function traverseX(rayList: Array<Ray>, bounds: Bounds, voxelSize: number) {
|
||||
for (let y = bounds.minY; y <= bounds.maxY; y += voxelSize) {
|
||||
for (let z = bounds.minZ; z <= bounds.maxZ; z += voxelSize) {
|
||||
rayList.push({
|
||||
origin: new Vector3(bounds.minX, y, z),
|
||||
direction: new Vector3(1, 0, 0),
|
||||
axis: Axes.x
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function traverseY(rayList: Array<Ray>, bounds: Bounds, voxelSize: number) {
|
||||
for (let x = bounds.minX; x <= bounds.maxX; x += voxelSize) {
|
||||
for (let z = bounds.minZ; z <= bounds.maxZ; z += voxelSize) {
|
||||
rayList.push({
|
||||
origin: new Vector3(x, bounds.minY, z),
|
||||
direction: new Vector3(0, 1, 0),
|
||||
axis: Axes.y
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function traverseZ(rayList: Array<Ray>, bounds: Bounds, voxelSize: number) {
|
||||
for (let x = bounds.minX; x <= bounds.maxX; x += voxelSize) {
|
||||
for (let y = bounds.minY; y <= bounds.maxY; y += voxelSize) {
|
||||
rayList.push({
|
||||
origin: new Vector3(x, y, bounds.minZ),
|
||||
direction: new Vector3(0, 0, 1),
|
||||
axis: Axes.z
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function rayIntersectTriangle(ray: Ray, triangle: Triangle): (Vector3 | void) {
|
||||
const edge1 = Vector3.sub(triangle.v1.position, triangle.v0.position);
|
||||
const edge2 = Vector3.sub(triangle.v2.position, triangle.v0.position);
|
||||
|
||||
const h = Vector3.cross(ray.direction, edge2);
|
||||
const a = Vector3.dot(edge1, h);
|
||||
|
||||
if (a > -EPSILON && a < EPSILON) {
|
||||
return; // Ray is parallel to triangle
|
||||
}
|
||||
|
||||
const f = 1.0 / a;
|
||||
const s = Vector3.sub(ray.origin, triangle.v0.position);
|
||||
const u = f * Vector3.dot(s, h);
|
||||
|
||||
if (u < 0.0 || u > 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const q = Vector3.cross(s, edge1);
|
||||
const v = f * Vector3.dot(ray.direction, q);
|
||||
|
||||
if (v < 0.0 || u + v > 1.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const t = f * Vector3.dot(edge2, q);
|
||||
|
||||
if (t > EPSILON) {
|
||||
return Vector3.add(ray.origin, Vector3.mulScalar(ray.direction, t));
|
||||
}
|
||||
}
|
7
src/vendor/bootstrap.bundle.min.js
vendored
7
src/vendor/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
8
src/vendor/bootstrap.css
vendored
8
src/vendor/bootstrap.css
vendored
File diff suppressed because one or more lines are too long
7
src/vendor/bootstrap.min.js
vendored
7
src/vendor/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
5
src/vendor/popper.min.js
vendored
5
src/vendor/popper.min.js
vendored
File diff suppressed because one or more lines are too long
@ -6,7 +6,8 @@ import { BlockAtlas, BlockInfo, FaceInfo } from "./block_atlas";
|
||||
import { RGB } from "./util";
|
||||
import { Triangle } from "./triangle";
|
||||
import { Mesh, MaterialType } from "./mesh";
|
||||
import { triangleArea } from "./math";
|
||||
import { roundToNearest, floorToNearest, triangleArea } from "./math";
|
||||
import { Axes, generateRays, rayIntersectTriangle } from "./ray";
|
||||
|
||||
interface Block {
|
||||
position: Vector3;
|
||||
@ -147,11 +148,13 @@ export class VoxelManager {
|
||||
|
||||
// [HACK] FIXME: Fix misaligned voxels
|
||||
// Some vec data is not not grid-snapped to voxelSize-spacing
|
||||
/*
|
||||
const test = Vector3.divScalar(vec, this._voxelSize);
|
||||
if ((test.x % 1 < 0.9 && test.x % 1 > 0.1) || (test.y % 1 < 0.9 && test.y % 1 > 0.1) || (test.z % 1 < 0.9 && test.z % 1 > 0.1)) {
|
||||
console.warn("Misaligned voxel, skipping...");
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
// Is there already a voxel in this position?
|
||||
let voxel = this.voxelsHash.get(pos);
|
||||
@ -199,32 +202,31 @@ export class VoxelManager {
|
||||
}
|
||||
|
||||
public voxeliseTriangle(triangle: Triangle) {
|
||||
const cubeAABB = this._getTriangleCubeAABB(triangle);
|
||||
const rayList = generateRays(triangle);
|
||||
const voxelSize = VoxelManager.Get._voxelSize;
|
||||
|
||||
const triangleAABBs = [];
|
||||
|
||||
let queue = [cubeAABB];
|
||||
while (true) {
|
||||
const aabb = queue.shift();
|
||||
if (!aabb) {
|
||||
break;
|
||||
}
|
||||
if (triangle.intersectAABB(aabb)) {
|
||||
if (aabb.width > this._voxelSize) {
|
||||
// Continue to subdivide
|
||||
queue.push(...aabb.subdivide());
|
||||
} else {
|
||||
// We've reached the voxel level, stop
|
||||
const voxelColour = this._getVoxelColour(triangle, aabb.centre);
|
||||
const block = this.blockAtlas.getBlock(voxelColour);
|
||||
|
||||
this.addVoxel(aabb.centre, block);
|
||||
triangleAABBs.push(aabb);
|
||||
rayList.forEach(ray => {
|
||||
const intersection = rayIntersectTriangle(ray, triangle);
|
||||
if (intersection) {
|
||||
let voxelPosition: Vector3;
|
||||
switch (ray.axis) {
|
||||
case Axes.x:
|
||||
voxelPosition = new Vector3(floorToNearest(intersection.x, voxelSize), intersection.y, intersection.z);
|
||||
break;
|
||||
case Axes.y:
|
||||
voxelPosition = new Vector3(intersection.x, floorToNearest(intersection.y, voxelSize), intersection.z);
|
||||
break;
|
||||
case Axes.z:
|
||||
voxelPosition = new Vector3(intersection.x, intersection.y, floorToNearest(intersection.z, voxelSize));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.triangleAABBs.push({triangle: triangle, AABBs: triangleAABBs});
|
||||
const voxelColour = this._getVoxelColour(triangle, voxelPosition);
|
||||
const block = this.blockAtlas.getBlock(voxelColour);
|
||||
|
||||
this.addVoxel(voxelPosition, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
voxeliseMesh(mesh: Mesh) {
|
||||
|
Loading…
Reference in New Issue
Block a user