Added prototype .vox importer, refactored importers

This commit is contained in:
Lucas Dower 2023-02-07 20:31:45 +00:00
parent 6169d51cbb
commit 74b1d1222b
No known key found for this signature in database
GPG Key ID: B3EE6B8499593605
10 changed files with 140 additions and 51 deletions

13
package-lock.json generated
View File

@ -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",

View File

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

View File

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

View File

@ -1,6 +1,9 @@
import { Mesh } from '../mesh';
export abstract class IFileImporter<T> {
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;
}

View File

@ -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<Mesh> } | { type: 'VoxelMesh', importer: IFileImporter<VoxelMesh> } {
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),
};
}
}
}

View File

@ -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<Mesh> {
private _vertices: Vector3[] = [];
private _normals: Vector3[] = [];
private _uvs: UV[] = [];
@ -24,8 +24,9 @@ export class ObjImporter extends IImporter {
private _materials: Map<string, SolidMaterial | TexturedMaterial>;
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);
}

View File

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

View File

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

View File

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

View File

@ -27,9 +27,12 @@ export namespace ImportParams {
}
export type Output = {
type: 'Mesh',
triangleCount: number,
dimensions: Vector3,
materials: MaterialMap
} | {
type: 'VoxelMesh',
}
}