Added per-axis constraint options

This commit is contained in:
Lucas Dower 2022-10-29 22:34:00 +01:00
parent 05a756a3b7
commit e36535f2a0
No known key found for this signature in database
GPG Key ID: B3EE6B8499593605
16 changed files with 112 additions and 26 deletions

View File

@ -244,8 +244,9 @@ export class AppContext {
const payload: TToWorkerMessage = {
action: 'Voxelise',
params: {
constraintAxis: uiElements.constraintAxis.getCachedValue(),
voxeliser: uiElements.voxeliser.getCachedValue(),
desiredHeight: uiElements.desiredHeight.getCachedValue(),
size: uiElements.size.getCachedValue(),
useMultisampleColouring: uiElements.multisampleColouring.getCachedValue() === 'on',
textureFiltering: uiElements.textureFiltering.getCachedValue() === 'linear' ? TextureFiltering.Linear : TextureFiltering.Nearest,
enableAmbientOcclusion: uiElements.ambientOcclusion.getCachedValue() === 'on',
@ -273,7 +274,7 @@ export class AppContext {
action: 'RenderNextVoxelMeshChunk',
params: {
enableAmbientOcclusion: uiElements.ambientOcclusion.getCachedValue() === 'on',
desiredHeight: uiElements.desiredHeight.getCachedValue(),
desiredHeight: uiElements.size.getCachedValue(),
},
};

View File

@ -10,6 +10,7 @@ import { EAction } from '../util';
import { ASSERT } from '../util/error_util';
import { LOG } from '../util/log_util';
import { AppPaths } from '../util/path_util';
import { TAxis } from '../util/type_util';
import { TVoxelOverlapRule } from '../voxel_mesh';
import { TVoxelisers } from '../voxelisers/voxelisers';
import { BaseUIElement } from './elements/base';
@ -52,7 +53,21 @@ export class UI {
'voxelise': {
label: 'Voxelise',
elements: {
'desiredHeight': new SliderElement('Desired height', 3, 380, 0, 80, 1),
'constraintAxis': new ComboBoxElement<TAxis>('Constraint axis', [
{
id: 'y',
displayText: 'Y (height) (green)',
},
{
id: 'x',
displayText: 'X (width) (red)',
},
{
id: 'z',
displayText: 'Z (depth) (blue)',
},
]),
'size': new SliderElement('Size', 3, 380, 0, 80, 1),
'voxeliser': new ComboBoxElement<TVoxelisers>('Algorithm', [
{
id: 'bvh-ray',
@ -114,7 +129,7 @@ export class UI {
},
]),
},
elementsOrder: ['desiredHeight', 'voxeliser', 'ambientOcclusion', 'multisampleColouring', 'textureFiltering', 'voxelOverlapRule'],
elementsOrder: ['constraintAxis', 'size', 'voxeliser', 'ambientOcclusion', 'multisampleColouring', 'textureFiltering', 'voxelOverlapRule'],
submitButton: new ButtonElement('Voxelise mesh', () => {
this._appContext.do(EAction.Voxelise);
}),

1
src/util/type_util.ts Normal file
View File

@ -0,0 +1 @@
export type TAxis = 'x' | 'y' | 'z';

View File

@ -17,8 +17,24 @@ const bvhtree = require('bvh-tree');
export class BVHRayVoxeliserPlusThickness extends IVoxeliser {
protected override _voxelise(mesh: Mesh, voxeliseParams: VoxeliseParams.Input): VoxelMesh {
const voxelMesh = new VoxelMesh(voxeliseParams);
const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight;
const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
const meshDimensions = mesh.getBounds().getDimensions();
let scale: number;
let offset = new Vector3(0.0, 0.0, 0.0);
switch (voxeliseParams.constraintAxis) {
case 'x':
scale = (voxeliseParams.size - 1) / meshDimensions.x;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'y':
scale = (voxeliseParams.size - 1) / meshDimensions.y;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'z':
scale = (voxeliseParams.size - 1) / meshDimensions.z;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0);
break;
}
mesh.setTransform((vertex: Vector3) => {
return vertex.copy().mulScalar(scale).add(offset);
@ -87,7 +103,7 @@ export class BVHRayVoxeliserPlusThickness extends IVoxeliser {
for (const intersection of intersections) {
const point = intersection.intersectionPoint;
const position = new Vector3(point.x, point.y, point.z);
// Shrinking towards the perpendicular vector
const triangle = mesh.getUVTriangle(intersection.triangleIndex);
const v0 = Vector3.sub(triangle.v1, triangle.v0);

View File

@ -2,7 +2,7 @@ import { Mesh } from '../mesh';
import { ProgressManager } from '../progress';
import { Axes, axesToDirection, Ray } from '../ray';
import { ASSERT } from '../util/error_util';
import { LOG } from '../util/log_util';
import { LOG, LOGF } from '../util/log_util';
import { Vector3 } from '../vector';
import { VoxelMesh } from '../voxel_mesh';
import { VoxeliseParams } from '../worker_types';
@ -17,8 +17,24 @@ const bvhtree = require('bvh-tree');
export class BVHRayVoxeliser extends IVoxeliser {
protected override _voxelise(mesh: Mesh, voxeliseParams: VoxeliseParams.Input): VoxelMesh {
const voxelMesh = new VoxelMesh(voxeliseParams);
const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight;
const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
const meshDimensions = mesh.getBounds().getDimensions();
let scale: number;
let offset = new Vector3(0.0, 0.0, 0.0);
switch (voxeliseParams.constraintAxis) {
case 'x':
scale = (voxeliseParams.size - 1) / meshDimensions.x;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'y':
scale = (voxeliseParams.size - 1) / meshDimensions.y;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'z':
scale = (voxeliseParams.size - 1) / meshDimensions.z;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0);
break;
}
mesh.setTransform((vertex: Vector3) => {
return vertex.copy().mulScalar(scale).add(offset);

View File

@ -27,17 +27,30 @@ export class NormalCorrectedRayVoxeliser extends IVoxeliser {
this._voxelMesh = new VoxelMesh(voxeliseParams);
this._voxeliseParams = voxeliseParams;
const scale = (voxeliseParams.desiredHeight) / Mesh.desiredHeight;
const meshDimensions = mesh.getBounds().getDimensions();
let scale: number;
switch (voxeliseParams.constraintAxis) {
case 'x':
scale = voxeliseParams.size / meshDimensions.x;
break;
case 'y':
scale = voxeliseParams.size / meshDimensions.y;
break;
case 'z':
scale = voxeliseParams.size / meshDimensions.z;
break;
}
mesh.setTransform((vertex: Vector3) => {
return vertex.copy().mulScalar(scale);
});
const bounds = mesh.getBounds();
this._size = Vector3.sub(bounds.max, bounds.min);
this._size = Vector3.sub(bounds.max.copy().ceil(), bounds.min.copy().floor());
this._offset = new Vector3(
this._size.x % 2 < 0.001 ? 0.5 : 0.0,
this._size.y % 2 < 0.001 ? 0.5 : 0.0,
this._size.z % 2 < 0.001 ? 0.5 : 0.0,
this._size.x % 2 === 0 ? 0.5 : 0.0,
this._size.y % 2 === 0 ? 0.5 : 0.0,
this._size.z % 2 === 0 ? 0.5 : 0.0,
);
const numTris = mesh.getTriangleCount();

View File

@ -25,8 +25,23 @@ export class RayVoxeliser extends IVoxeliser {
this._voxelMesh = new VoxelMesh(voxeliseParams);
this._voxeliseParams = voxeliseParams;
const scale = (voxeliseParams.desiredHeight - 1) / Mesh.desiredHeight;
const offset = (voxeliseParams.desiredHeight % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
const meshDimensions = mesh.getBounds().getDimensions();
let scale: number;
let offset = new Vector3(0.0, 0.0, 0.0);
switch (voxeliseParams.constraintAxis) {
case 'x':
scale = (voxeliseParams.size - 1) / meshDimensions.x;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.5, 0.0, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'y':
scale = (voxeliseParams.size - 1) / meshDimensions.y;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.5, 0.0) : new Vector3(0.0, 0.0, 0.0);
break;
case 'z':
scale = (voxeliseParams.size - 1) / meshDimensions.z;
offset = (voxeliseParams.size % 2 === 0) ? new Vector3(0.0, 0.0, 0.5) : new Vector3(0.0, 0.0, 0.0);
break;
}
mesh.setTransform((vertex: Vector3) => {
return vertex.copy().mulScalar(scale).add(offset);

View File

@ -7,6 +7,7 @@ import { StatusMessage } from './status';
import { TextureFiltering } from './texture';
import { ColourSpace } from './util';
import { AppError } from './util/error_util';
import { TAxis } from './util/type_util';
import { Vector3 } from './vector';
import { TVoxelOverlapRule } from './voxel_mesh';
import { TVoxelisers } from './voxelisers/voxelisers';
@ -42,8 +43,9 @@ export namespace RenderMeshParams {
export namespace VoxeliseParams {
export type Input = {
constraintAxis: TAxis,
voxeliser: TVoxelisers,
desiredHeight: number,
size: number,
useMultisampleColouring: boolean,
textureFiltering: TextureFiltering,
enableAmbientOcclusion: boolean,

View File

@ -98,6 +98,7 @@ canvas {
align-items: center;
/*border: 1px solid yellow;*/
height: var(--property-height);
width: 0px;
}
.item-body-sunken {

View File

@ -18,7 +18,8 @@ test('Voxelise solid 2x2 cube', () => {
const voxeliser = new NormalCorrectedRayVoxeliser();
const voxelMesh = voxeliser.voxelise(mesh, {
desiredHeight: 2,
constraintAxis: 'y',
size: 2,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Nearest,
enableAmbientOcclusion: false,

View File

@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = {
},
voxelise: {
voxeliser: 'bvh-ray',
desiredHeight: 80,
constraintAxis: 'y',
size: 80,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Linear,
voxelOverlapRule: 'average',

View File

@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = {
},
voxelise: {
voxeliser: 'bvh-ray',
desiredHeight: 80,
constraintAxis: 'y',
size: 80,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Linear,
voxelOverlapRule: 'average',

View File

@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = {
},
voxelise: {
voxeliser: 'bvh-ray',
desiredHeight: 80,
constraintAxis: 'y',
size: 80,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Linear,
voxelOverlapRule: 'average',

View File

@ -10,7 +10,8 @@ const baseConfig: THeadlessConfig = {
},
voxelise: {
voxeliser: 'bvh-ray',
desiredHeight: 80,
constraintAxis: 'y',
size: 80,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Linear,
voxelOverlapRule: 'average',

View File

@ -7,8 +7,9 @@ export const headlessConfig: THeadlessConfig = {
filepath: '/Users/lucasdower/ObjToSchematic/res/samples/skull.obj', // Must be an absolute path
},
voxelise: {
constraintAxis: 'y',
voxeliser: 'bvh-ray',
desiredHeight: 80,
size: 80,
useMultisampleColouring: false,
textureFiltering: TextureFiltering.Linear,
voxelOverlapRule: 'average',

View File

@ -51,7 +51,7 @@ export function runHeadless(headlessConfig: THeadlessConfig) {
{
TIME_START('[TIMER] Exporter');
LOG_MAJOR('\nExporting...');
/**
* The OBJExporter is unique in that it uses the actual render buffer used by WebGL
* to create its data, in headless mode this render buffer is not created so we must
@ -62,7 +62,7 @@ export function runHeadless(headlessConfig: THeadlessConfig) {
do {
result = worker.renderChunkedVoxelMesh({
enableAmbientOcclusion: headlessConfig.voxelise.enableAmbientOcclusion,
desiredHeight: headlessConfig.voxelise.desiredHeight,
desiredHeight: headlessConfig.voxelise.size,
});
} while (result.moreVoxelsToBuffer);
}