From 6e81858e35985c12530f78829fbea7436ebf22ec Mon Sep 17 00:00:00 2001 From: Lucas Dower Date: Sun, 22 Oct 2023 17:57:44 +0100 Subject: [PATCH] Optimised voxelisation --- Core/src/ots_voxel_mesh_converter.ts | 90 ++++++++++++++------------- Core/src/ray.ts | 93 +++++++++++++++++++++++++--- Sandbox/index.ts | 1 - 3 files changed, 131 insertions(+), 53 deletions(-) diff --git a/Core/src/ots_voxel_mesh_converter.ts b/Core/src/ots_voxel_mesh_converter.ts index 0fa9601..b22af2b 100644 --- a/Core/src/ots_voxel_mesh_converter.ts +++ b/Core/src/ots_voxel_mesh_converter.ts @@ -2,7 +2,7 @@ import { OtS_ReplaceMode, OtS_VoxelMesh } from './ots_voxel_mesh'; import { TAxis } from './util/type_util'; import { Vector3 } from './vector'; import { Triangle } from './triangle'; -import { Axes, Ray, rayIntersectTriangle } from './ray'; +import { rayIntersectTriangleFastX, rayIntersectTriangleFastY, rayIntersectTriangleFastZ } from './ray'; import { OtS_Colours, RGBA, RGBAUtil } from './colour'; import { OtS_Mesh, OtS_Triangle } from './ots_mesh'; import { UV } from './util'; @@ -10,7 +10,7 @@ import { UV } from './util'; export type OtS_VoxelMesh_ConverterConfig = { constraintAxis: TAxis, size: number, - multisampling: boolean, + multisampling?: number, replaceMode: OtS_ReplaceMode, } @@ -21,7 +21,7 @@ export class OtS_VoxelMesh_Converter { this._config = { constraintAxis: 'y', size: 80, - multisampling: true, + multisampling: 8, replaceMode: 'average', }; } @@ -60,68 +60,70 @@ export class OtS_VoxelMesh_Converter { bounds.min.floor(); bounds.max.ceil(); - const ray: Ray = { axis: Axes.x, origin: new Vector3(0, 0, 0) }; + const rayOrigin = new Vector3(0, 0, 0); - ray.origin.x = bounds.min.x - 1; - ray.axis = Axes.x; + rayOrigin.x = bounds.min.x - 1; for (let y = bounds.min.y; y <= bounds.max.y; ++y) { - ray.origin.y = y; + rayOrigin.y = y; for (let z = bounds.min.z; z <= bounds.max.z; ++z) { - ray.origin.z = z; - this._handleRay(ray, triangle, voxelMesh); + rayOrigin.z = z; + const intersection = rayIntersectTriangleFastX(rayOrigin, triangle); + if (intersection) { + this._handleRayHit(intersection, triangle, voxelMesh); + } } } - ray.origin.y = bounds.min.y - 1; - ray.axis = Axes.y; + rayOrigin.y = bounds.min.y - 1; for (let z = bounds.min.z; z <= bounds.max.z; ++z) { - ray.origin.z = z; + rayOrigin.z = z; for (let x = bounds.min.x; x <= bounds.max.x; ++x) { - ray.origin.x = x; - this._handleRay(ray, triangle, voxelMesh); + rayOrigin.x = x; + const intersection = rayIntersectTriangleFastY(rayOrigin, triangle); + if (intersection) { + this._handleRayHit(intersection, triangle, voxelMesh); + } } } - ray.origin.z = bounds.min.z - 1; - ray.axis = Axes.z; + rayOrigin.z = bounds.min.z - 1; for (let x = bounds.min.x; x <= bounds.max.x; ++x) { - ray.origin.x = x; + rayOrigin.x = x; for (let y = bounds.min.y; y <= bounds.max.y; ++y) { - ray.origin.y = y; - this._handleRay(ray, triangle, voxelMesh); + rayOrigin.y = y; + const intersection = rayIntersectTriangleFastZ(rayOrigin, triangle); + if (intersection) { + this._handleRayHit(intersection, triangle, voxelMesh); + } } } } - private _handleRay(ray: Ray, triangle: OtS_Triangle, voxelMesh: OtS_VoxelMesh) { - const intersection = rayIntersectTriangle(ray, triangle.data.v0.position, triangle.data.v1.position, triangle.data.v2.position); - - if (intersection) { - const voxelPosition = new Vector3( - intersection.x, - intersection.y, - intersection.z, - ).round(); + private _handleRayHit(intersection: Vector3, triangle: OtS_Triangle, voxelMesh: OtS_VoxelMesh) { + const voxelPosition = new Vector3( + intersection.x, + intersection.y, + intersection.z, + ).round(); - let voxelColour: RGBA; - if (this._config.multisampling) { - const samples: RGBA[] = []; - for (let i = 0; i < 8; ++i) { - samples.push(this._getVoxelColour( - triangle, - Vector3.random().divScalar(2.0).add(voxelPosition), - )) - } - voxelColour = RGBAUtil.average(...samples); - } else { - voxelColour = this._getVoxelColour( + let voxelColour: RGBA; + if (this._config.multisampling !== undefined) { + const samples: RGBA[] = []; + for (let i = 0; i < this._config.multisampling; ++i) { + samples.push(this._getVoxelColour( triangle, - voxelPosition, - ); + Vector3.random().divScalar(2.0).add(voxelPosition), + )) } - - voxelMesh.addVoxel(voxelPosition.x, voxelPosition.y, voxelPosition.z, voxelColour, this._config.replaceMode); + voxelColour = RGBAUtil.average(...samples); + } else { + voxelColour = this._getVoxelColour( + triangle, + voxelPosition, + ); } + + voxelMesh.addVoxel(voxelPosition.x, voxelPosition.y, voxelPosition.z, voxelColour, this._config.replaceMode); } private _getVoxelColour(triangle: OtS_Triangle, location: Vector3): RGBA { diff --git a/Core/src/ray.ts b/Core/src/ray.ts index e763497..49ac776 100644 --- a/Core/src/ray.ts +++ b/Core/src/ray.ts @@ -1,3 +1,4 @@ +import { OtS_Triangle } from './ots_mesh'; import { ASSERT } from './util/error_util'; import { Vector3 } from './vector'; @@ -27,12 +28,13 @@ export interface Ray { axis: Axes } -export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vector3): (Vector3 | undefined) { - const edge1 = Vector3.sub(v1, v0); - const edge2 = Vector3.sub(v2, v0); +export type RayIntersect = (origin: Vector3, v0: Vector3, v1: Vector3, v2: Vector3) => (Vector3 | undefined); - const rayDirection = axesToDirection(ray.axis); - const h = Vector3.cross(rayDirection, edge2); +export function rayIntersectTriangleFastX(origin: Vector3, triangle: OtS_Triangle): (Vector3 | undefined) { + const edge1 = Vector3.sub(triangle.data.v1.position, triangle.data.v0.position); + const edge2 = Vector3.sub(triangle.data.v2.position, triangle.data.v0.position); + + const h = new Vector3(0, -edge2.z, edge2.y); // Vector3.cross(rayDirection, edge2); const a = Vector3.dot(edge1, h); if (a > -EPSILON && a < EPSILON) { @@ -40,7 +42,7 @@ export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vec } const f = 1.0 / a; - const s = Vector3.sub(ray.origin, v0); + const s = Vector3.sub(origin, triangle.data.v0.position); const u = f * Vector3.dot(s, h); if (u < 0.0 || u > 1.0) { @@ -48,7 +50,7 @@ export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vec } const q = Vector3.cross(s, edge1); - const v = f * Vector3.dot(rayDirection, q); + const v = f * q.x; // f * Vector3.dot(rayDirection, q); if (v < 0.0 || u + v > 1.0) { return; @@ -57,6 +59,81 @@ export function rayIntersectTriangle(ray: Ray, v0: Vector3, v1: Vector3, v2: Vec const t = f * Vector3.dot(edge2, q); if (t > EPSILON) { - return Vector3.add(ray.origin, Vector3.mulScalar(rayDirection, t)); + const result = Vector3.copy(origin); + result.x += t; + return result; + //return Vector3.add(origin, Vector3.mulScalar(rayDirection, t)); } } + +export function rayIntersectTriangleFastY(origin: Vector3, triangle: OtS_Triangle): (Vector3 | undefined) { + const edge1 = Vector3.sub(triangle.data.v1.position, triangle.data.v0.position); + const edge2 = Vector3.sub(triangle.data.v2.position, triangle.data.v0.position); + + const h = new Vector3(edge2.z, 0, -edge2.x); // Vector3.cross(rayDirection, 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(origin, triangle.data.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 * q.y; // f * Vector3.dot(rayDirection, q); + + if (v < 0.0 || u + v > 1.0) { + return; + } + + const t = f * Vector3.dot(edge2, q); + + if (t > EPSILON) { + const result = Vector3.copy(origin); + result.y += t; + return result; + //return Vector3.add(origin, Vector3.mulScalar(rayDirection, t)); + } +} + +export function rayIntersectTriangleFastZ(origin: Vector3, triangle: OtS_Triangle): (Vector3 | undefined) { + const edge1 = Vector3.sub(triangle.data.v1.position, triangle.data.v0.position); + const edge2 = Vector3.sub(triangle.data.v2.position, triangle.data.v0.position); + + const h = new Vector3(-edge2.y, edge2.x, 0); // Vector3.cross(rayDirection, 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(origin, triangle.data.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 * q.z; // f * Vector3.dot(rayDirection, q); + + if (v < 0.0 || u + v > 1.0) { + return; + } + + const t = f * Vector3.dot(edge2, q); + + if (t > EPSILON) { + const result = Vector3.copy(origin); + result.z += t; + return result; + //return Vector3.add(origin, Vector3.mulScalar(rayDirection, t)); + } +} \ No newline at end of file diff --git a/Sandbox/index.ts b/Sandbox/index.ts index 9e2dd60..7fae27b 100644 --- a/Sandbox/index.ts +++ b/Sandbox/index.ts @@ -90,7 +90,6 @@ import { OtS_Colours } from 'ots-core/src/colour'; voxelMeshConverter.setConfig({ constraintAxis: 'y', size: 380, - multisampling: false, replaceMode: 'keep', });