forked from mirror/ObjToSchematic
Added prototype .vox importer, refactored importers
This commit is contained in:
parent
6169d51cbb
commit
74b1d1222b
13
package-lock.json
generated
13
package-lock.json
generated
@ -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",
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
33
src/importers/vox_importer.ts
Normal file
33
src/importers/vox_importer.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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')
|
||||
|
@ -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 {
|
||||
|
@ -27,9 +27,12 @@ export namespace ImportParams {
|
||||
}
|
||||
|
||||
export type Output = {
|
||||
type: 'Mesh',
|
||||
triangleCount: number,
|
||||
dimensions: Vector3,
|
||||
materials: MaterialMap
|
||||
} | {
|
||||
type: 'VoxelMesh',
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user