From 74b1d1222b304fd4c67fb7617f79aa81ad46d7f4 Mon Sep 17 00:00:00 2001 From: Lucas Dower Date: Tue, 7 Feb 2023 20:31:45 +0000 Subject: [PATCH] Added prototype .vox importer, refactored importers --- package-lock.json | 13 ++++++++- package.json | 3 +- src/app_context.ts | 37 +++++++++++++++---------- src/importers/base_importer.ts | 11 +++++--- src/importers/importers.ts | 22 ++++++++++----- src/importers/obj_importer.ts | 17 ++++++------ src/importers/vox_importer.ts | 33 ++++++++++++++++++++++ src/ui/layout.ts | 2 +- src/worker_client.ts | 50 +++++++++++++++++++++++++--------- src/worker_types.ts | 3 ++ 10 files changed, 140 insertions(+), 51 deletions(-) create mode 100644 src/importers/vox_importer.ts diff --git a/package-lock.json b/package-lock.json index 2cf3b31..32e01a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,8 @@ "prismarine-nbt": "^1.6.0", "tga": "^1.0.7", "twgl.js": "^4.19.1", - "varint-array": "^0.0.0" + "varint-array": "^0.0.0", + "vox-reader": "^2.1.2" }, "devDependencies": { "@types/adm-zip": "^0.5.0", @@ -7703,6 +7704,11 @@ "varint": "^5.0.0" } }, + "node_modules/vox-reader": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vox-reader/-/vox-reader-2.1.2.tgz", + "integrity": "sha512-33H2ZE1FedHg2xlQmEkID05jc1d05uwhH2JOURblLspTSZI7SLHNTN6jyq4cIIaglSwcC+o21Pss3uRSxtlN/Q==" + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", @@ -13814,6 +13820,11 @@ "varint": "^5.0.0" } }, + "vox-reader": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/vox-reader/-/vox-reader-2.1.2.tgz", + "integrity": "sha512-33H2ZE1FedHg2xlQmEkID05jc1d05uwhH2JOURblLspTSZI7SLHNTN6jyq4cIIaglSwcC+o21Pss3uRSxtlN/Q==" + }, "w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/package.json b/package.json index 48b838a..ca3637d 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "prismarine-nbt": "^1.6.0", "tga": "^1.0.7", "twgl.js": "^4.19.1", - "varint-array": "^0.0.0" + "varint-array": "^0.0.0", + "vox-reader": "^2.1.2" } } diff --git a/src/app_context.ts b/src/app_context.ts index f60f4a4..f9f0838 100644 --- a/src/app_context.ts +++ b/src/app_context.ts @@ -198,24 +198,31 @@ export class AppContext { ASSERT(payload.action === 'Import'); const outputElement = this._ui.getActionOutput(EAction.Import); - const dimensions = new Vector3( - payload.result.dimensions.x, - payload.result.dimensions.y, - payload.result.dimensions.z, - ); - dimensions.mulScalar(AppConfig.Get.CONSTRAINT_MAXIMUM_HEIGHT / 8.0).floor(); - this.maxConstraint = dimensions; - this._materialManager = new MaterialMapManager(payload.result.materials); + if (payload.result.type === 'Mesh') { + const dimensions = new Vector3( + payload.result.dimensions.x, + payload.result.dimensions.y, + payload.result.dimensions.z, + ); + dimensions.mulScalar(AppConfig.Get.CONSTRAINT_MAXIMUM_HEIGHT / 8.0).floor(); + this.maxConstraint = dimensions; + this._materialManager = new MaterialMapManager(payload.result.materials); - if (payload.result.triangleCount < AppConfig.Get.RENDER_TRIANGLE_THRESHOLD) { - outputElement.setTaskInProgress('render', '[Renderer]: Processing...'); - this._workerController.addJob(this._renderMesh()); + if (payload.result.triangleCount < AppConfig.Get.RENDER_TRIANGLE_THRESHOLD) { + outputElement.setTaskInProgress('render', '[Renderer]: Processing...'); + this._workerController.addJob(this._renderMesh()); + } else { + const message = `Will not render mesh as its over ${AppConfig.Get.RENDER_TRIANGLE_THRESHOLD.toLocaleString()} triangles.`; + outputElement.setTaskComplete('render', '[Renderer]: Stopped', [message], 'warning'); + } + + this._updateMaterialsAction(); } else { - const message = `Will not render mesh as its over ${AppConfig.Get.RENDER_TRIANGLE_THRESHOLD.toLocaleString()} triangles.`; - outputElement.setTaskComplete('render', '[Renderer]: Stopped', [message], 'warning'); - } + ASSERT(payload.result.type === 'VoxelMesh'); - this._updateMaterialsAction(); + outputElement.setTaskInProgress('render', '[Renderer]: Processing?'); + this._workerController.addJob(this._renderVoxelMesh()); + } }; callback.bind(this); diff --git a/src/importers/base_importer.ts b/src/importers/base_importer.ts index 80ad5d7..40fbe6a 100644 --- a/src/importers/base_importer.ts +++ b/src/importers/base_importer.ts @@ -1,6 +1,9 @@ -import { Mesh } from '../mesh'; +export abstract class IFileImporter { + protected readonly _filepath: string; -export abstract class IImporter { - abstract parseFile(filePath: string): void; - abstract toMesh(): Mesh; + public constructor(filepath: string) { + this._filepath = filepath; + } + + public abstract load(): T; } diff --git a/src/importers/importers.ts b/src/importers/importers.ts index a1ac0d5..27a85bf 100644 --- a/src/importers/importers.ts +++ b/src/importers/importers.ts @@ -1,16 +1,24 @@ -import { ASSERT } from '../util/error_util'; -import { IImporter } from './base_importer'; +import { Mesh } from '../mesh'; +import { VoxelMesh } from '../voxel_mesh'; +import { IFileImporter } from './base_importer'; import { ObjImporter } from './obj_importer'; +import { VoxImporter } from './vox_importer'; -export type TImporters = 'obj'; +export type TImporters = 'obj' | 'vox'; export class ImporterFactor { - public static GetImporter(importer: TImporters): IImporter { + public static GetImporter(importer: TImporters, filename: string): { type: 'Mesh', importer: IFileImporter } | { type: 'VoxelMesh', importer: IFileImporter } { switch (importer) { case 'obj': - return new ObjImporter(); - default: - ASSERT(false); + return { + type: 'Mesh', + importer: new ObjImporter(filename), + }; + case 'vox': + return { + type: 'VoxelMesh', + importer: new VoxImporter(filename), + }; } } } diff --git a/src/importers/obj_importer.ts b/src/importers/obj_importer.ts index 82ebb91..b13053d 100644 --- a/src/importers/obj_importer.ts +++ b/src/importers/obj_importer.ts @@ -14,9 +14,9 @@ import { RegExpBuilder } from '../util/regex_util'; import { REGEX_NZ_ANY } from '../util/regex_util'; import { REGEX_NUMBER } from '../util/regex_util'; import { Vector3 } from '../vector'; -import { IImporter } from './base_importer'; +import { IFileImporter } from './base_importer'; -export class ObjImporter extends IImporter { +export class ObjImporter extends IFileImporter { private _vertices: Vector3[] = []; private _normals: Vector3[] = []; private _uvs: UV[] = []; @@ -24,8 +24,9 @@ export class ObjImporter extends IImporter { private _materials: Map; - public constructor() { - super(); + public constructor(filepath: string) { + super(filepath); + this._materials = new Map(); this._materials.set('DEFAULT_UNASSIGNED', { type: MaterialType.solid, @@ -286,10 +287,10 @@ export class ObjImporter extends IImporter { }, ]; - override parseFile(filePath: string) { - this._objPath = path.parse(filePath); + public override load(): Mesh { + this._objPath = path.parse(this._filepath); - this._parseOBJ(filePath); + this._parseOBJ(this._filepath); if (this._mtlLibs.length === 0) { StatusHandler.Get.add('warning', 'Could not find associated .mtl file'); @@ -304,9 +305,7 @@ export class ObjImporter extends IImporter { this._parseMTL(); LOG('Materials', this._materials); - } - override toMesh(): Mesh { return new Mesh(this._vertices, this._normals, this._uvs, this._tris, this._materials); } diff --git a/src/importers/vox_importer.ts b/src/importers/vox_importer.ts new file mode 100644 index 0000000..e6b059f --- /dev/null +++ b/src/importers/vox_importer.ts @@ -0,0 +1,33 @@ +import fs from 'fs'; +import readVox from 'vox-reader'; + +import { RGBA_255, RGBAUtil } from '../colour'; +import { Vector3 } from '../vector'; +import { VoxelMesh } from '../voxel_mesh'; +import { IFileImporter } from './base_importer'; + +export class VoxImporter extends IFileImporter { + public override load(): VoxelMesh { + const fileBuffer = fs.readFileSync(this._filepath); + + const voxelMesh = new VoxelMesh({ + enableAmbientOcclusion: true, + voxelOverlapRule: 'first', + }); + + const voxObject = readVox(fileBuffer); + const colourPalette = voxObject.rgba.values as RGBA_255[]; + colourPalette.map((x) => RGBAUtil.fromRGBA255(x)); + + const voxels = voxObject.xyzi.values as { x: number, y: number, z: number, i: number }[]; + + voxels.forEach((voxel) => { + const position = new Vector3(voxel.x, voxel.y, voxel.z); + const colour = RGBAUtil.fromRGBA255(colourPalette[voxel.i]); + + voxelMesh.addVoxel(position, colour); + }); + + return voxelMesh; + } +} diff --git a/src/ui/layout.ts b/src/ui/layout.ts index 5ee987c..71ae7d4 100644 --- a/src/ui/layout.ts +++ b/src/ui/layout.ts @@ -45,7 +45,7 @@ export class UI { label: 'Import', elements: { 'input': new FileInputElement() - .setFileExtensions(['obj']) + .setFileExtensions(['obj', 'vox']) .setLabel('Wavefront .obj file'), 'rotation': new VectorSpinboxElement() .setLabel('Rotation') diff --git a/src/worker_client.ts b/src/worker_client.ts index aacac78..43e18bb 100644 --- a/src/worker_client.ts +++ b/src/worker_client.ts @@ -1,3 +1,5 @@ +import path from 'path'; + import { Atlas } from './atlas'; import { BlockMesh } from './block_mesh'; import { BufferGenerator } from './buffer'; @@ -5,6 +7,7 @@ import { EAppEvent, EventManager } from './event'; import { IExporter } from './exporters/base_exporter'; import { ExporterFactory } from './exporters/exporters'; import { ObjImporter } from './importers/obj_importer'; +import { VoxImporter } from './importers/vox_importer'; import { Mesh } from './mesh'; import { ProgressManager, TTaskHandle } from './progress'; import { StatusHandler } from './status'; @@ -76,20 +79,41 @@ export class WorkerClient { } public import(params: ImportParams.Input, onFinish: (result: TFromWorkerMessage) => void): void { - const importer = new ObjImporter(); - importer.parseFile(params.filepath); - this._loadedMesh = importer.toMesh(); - this._loadedMesh.processMesh(params.rotation.y, params.rotation.x, params.rotation.z); + const parsedPath = path.parse(params.filepath); - onFinish({ - action: 'Import', - statusMessages: StatusHandler.Get.getAllStatusMessages(), - result: { - triangleCount: this._loadedMesh.getTriangleCount(), - dimensions: this._loadedMesh.getBounds().getDimensions(), - materials: this._loadedMesh.getMaterials(), - }, - }); + switch (parsedPath.ext) { + case '.obj': { + const importer = new ObjImporter(params.filepath); + this._loadedMesh = importer.load(); + + this._loadedMesh.processMesh(params.rotation.y, params.rotation.x, params.rotation.z); + + onFinish({ + action: 'Import', + statusMessages: StatusHandler.Get.getAllStatusMessages(), + result: { + type: 'Mesh', + triangleCount: this._loadedMesh.getTriangleCount(), + dimensions: this._loadedMesh.getBounds().getDimensions(), + materials: this._loadedMesh.getMaterials(), + }, + }); + break; + } + case '.vox': { + const importer = new VoxImporter(params.filepath); + this._loadedVoxelMesh = importer.load(); + + onFinish({ + action: 'Import', + statusMessages: StatusHandler.Get.getAllStatusMessages(), + result: { + type: 'VoxelMesh', + }, + }); + break; + } + } } public setMaterials(params: SetMaterialsParams.Input, onFinish: (result: TFromWorkerMessage) => void): void { diff --git a/src/worker_types.ts b/src/worker_types.ts index a8f28dc..900e3f2 100644 --- a/src/worker_types.ts +++ b/src/worker_types.ts @@ -27,9 +27,12 @@ export namespace ImportParams { } export type Output = { + type: 'Mesh', triangleCount: number, dimensions: Vector3, materials: MaterialMap + } | { + type: 'VoxelMesh', } }