Transition to ray-based voxelisation

This commit is contained in:
Lucas Dower 2021-11-13 16:19:56 +00:00
parent c7238ef214
commit 80be8d8a62
9 changed files with 161 additions and 54 deletions

View File

@ -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 };
}

View File

@ -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();
}

View File

@ -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
View 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));
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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) {