forked from mirror/ObjToSchematic
Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
53456e957e | ||
|
1a7c33b9b1 |
919
package-lock.json
generated
919
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -54,6 +54,7 @@
|
||||
"bvh": "^0.5.0",
|
||||
"bvh-tree": "^1.0.1",
|
||||
"color-convert": "^2.0.1",
|
||||
"fbx-parser": "^2.1.3",
|
||||
"jpeg-js": "^0.4.3",
|
||||
"pngjs": "^6.0.0",
|
||||
"prismarine-nbt": "^1.6.0",
|
||||
|
@ -3,7 +3,6 @@ import { Schematic } from './exporters/schematic_exporter';
|
||||
import { Litematic } from './exporters/litematic_exporter';
|
||||
import { Renderer } from './renderer';
|
||||
import { Mesh } from './mesh';
|
||||
import { ObjImporter } from './importers/obj_importer';
|
||||
import { ASSERT, ColourSpace, AppError, LOG, LOG_ERROR, LOG_WARN, TIME_START, TIME_END } from './util';
|
||||
|
||||
import { remote } from 'electron';
|
||||
@ -18,6 +17,9 @@ import { StatusHandler } from './status';
|
||||
import { UIMessageBuilder } from './ui/misc';
|
||||
import { OutputStyle } from './ui/elements/output';
|
||||
|
||||
import path from 'path';
|
||||
import { MeshImporter } from './mesh_importer';
|
||||
|
||||
/* eslint-disable */
|
||||
export enum EAction {
|
||||
Import = 0,
|
||||
@ -137,9 +139,7 @@ export class AppContext {
|
||||
const uiElements = this._ui.layout.import.elements;
|
||||
const filePath = uiElements.input.getCachedValue();
|
||||
|
||||
const importer = new ObjImporter();
|
||||
importer.parseFile(filePath);
|
||||
this._loadedMesh = importer.toMesh();
|
||||
this._loadedMesh = MeshImporter.import(filePath);
|
||||
this._loadedMesh.processMesh();
|
||||
Renderer.Get.useMesh(this._loadedMesh);
|
||||
}
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { Mesh } from './mesh';
|
||||
|
||||
export abstract class IImporter {
|
||||
abstract parseFile(filePath: string): void;
|
||||
abstract toMesh(): Mesh;
|
||||
}
|
11
src/importers/base_importer.ts
Normal file
11
src/importers/base_importer.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Mesh } from '../mesh';
|
||||
|
||||
export abstract class IImporter {
|
||||
public abstract parseFile(filePath: string): void;
|
||||
public abstract toMesh(): Mesh;
|
||||
public supports(fileExtension: string) {
|
||||
return this.getSupportedFileExtensions().includes(fileExtension);
|
||||
}
|
||||
|
||||
protected abstract getSupportedFileExtensions(): string[];
|
||||
}
|
91
src/importers/fbx_importer.ts
Normal file
91
src/importers/fbx_importer.ts
Normal file
@ -0,0 +1,91 @@
|
||||
import { IImporter } from './base_importer';
|
||||
import { MaterialType, Mesh, SolidMaterial, TexturedMaterial, Tri } from '../mesh';
|
||||
import { Vector3 } from '../vector';
|
||||
import { UV, RGB, LOG, ASSERT } from '../util';
|
||||
|
||||
import fs from 'fs';
|
||||
|
||||
import * as FBXParser from 'fbx-parser';
|
||||
|
||||
export class FbxImporter extends IImporter {
|
||||
private _vertices: Vector3[] = [];
|
||||
private _normals: Vector3[] = [];
|
||||
private _uvs: UV[] = [];
|
||||
private _tris: Tri[] = [];
|
||||
private _materials: {[key: string]: (SolidMaterial | TexturedMaterial)} = {
|
||||
'DEFAULT_UNASSIGNED': { type: MaterialType.solid, colour: RGB.white },
|
||||
};
|
||||
|
||||
public override parseFile(filePath: string) {
|
||||
let fbx: FBXParser.FBXData;
|
||||
try {
|
||||
fbx = FBXParser.parseBinary(fs.readFileSync(filePath));
|
||||
} catch (e) {
|
||||
fbx = FBXParser.parseText(fs.readFileSync(filePath, 'utf-8'));
|
||||
}
|
||||
const root = new FBXParser.FBXReader(fbx);
|
||||
|
||||
// Parse vertex data
|
||||
const _vertices = root.node('Objects')?.node('Geometry')?.node('Vertices')?.prop(0, 'number[]');
|
||||
ASSERT(_vertices, 'Could not find vertex data node');
|
||||
ASSERT(_vertices.length % 3 === 0, 'Expected vertices array to be a multiple of 3');
|
||||
const numVertices = _vertices.length / 3;
|
||||
this._vertices = Array<Vector3>(numVertices);
|
||||
for (let i = 0; i < numVertices; ++i) {
|
||||
this._vertices[i] = new Vector3(
|
||||
_vertices[i * 3 + 0],
|
||||
_vertices[i * 3 + 1],
|
||||
_vertices[i * 3 + 2],
|
||||
);
|
||||
}
|
||||
// LOG('vertices', this._vertices);
|
||||
|
||||
// Parse face data
|
||||
const _faces = root.node('Objects')?.node('Geometry')?.node('PolygonVertexIndex')?.prop(0, 'number[]');
|
||||
ASSERT(_faces, 'Could not find face data node');
|
||||
|
||||
let base = [];
|
||||
for (let i = 0; i < _faces.length; ++i) {
|
||||
base.push(_faces[i]);
|
||||
if (_faces[i] < 0) {
|
||||
this._addFaceData(base);
|
||||
base = [];
|
||||
}
|
||||
}
|
||||
// LOG('tris', this._tris);
|
||||
|
||||
LOG(root);
|
||||
}
|
||||
|
||||
private _addFaceData(points: number[]) {
|
||||
// Make all indices positive
|
||||
for (let i = 0; i < points.length; ++i) {
|
||||
if (points[i] < 0) {
|
||||
points[i] = (-points[i]) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
const pointBase = points[0];
|
||||
for (let i = 1; i < points.length - 1; ++i) {
|
||||
const pointA = points[i];
|
||||
const pointB = points[i+1];
|
||||
const tri: Tri = {
|
||||
positionIndices: {
|
||||
x: pointBase,
|
||||
y: pointA,
|
||||
z: pointB,
|
||||
},
|
||||
material: 'None',
|
||||
};
|
||||
this._tris.push(tri);
|
||||
}
|
||||
}
|
||||
|
||||
public override toMesh(): Mesh {
|
||||
return new Mesh(this._vertices, this._normals, this._uvs, this._tris, this._materials);
|
||||
}
|
||||
|
||||
public override getSupportedFileExtensions(): string[] {
|
||||
return ['.fbx'];
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { IImporter } from '../importer';
|
||||
import { IImporter } from './base_importer';
|
||||
import { MaterialType, Mesh, SolidMaterial, TexturedMaterial, Tri } from '../mesh';
|
||||
import { Vector3 } from '../vector';
|
||||
import { UV, ASSERT, RGB, AppError, REGEX_NUMBER, RegExpBuilder, REGEX_NZ_ANY, LOG_ERROR } from '../util';
|
||||
@ -237,7 +237,7 @@ export class ObjImporter extends IImporter {
|
||||
},
|
||||
];
|
||||
|
||||
override parseFile(filePath: string) {
|
||||
public parseFile(filePath: string) {
|
||||
ASSERT(path.isAbsolute(filePath), 'path not absolute');
|
||||
|
||||
this._objPath = path.parse(filePath);
|
||||
@ -363,4 +363,8 @@ export class ObjImporter extends IImporter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override getSupportedFileExtensions(): string[] {
|
||||
return ['.obj'];
|
||||
}
|
||||
}
|
||||
|
22
src/mesh_importer.ts
Normal file
22
src/mesh_importer.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ObjImporter } from './importers/obj_importer';
|
||||
import { FbxImporter } from './importers/fbx_importer';
|
||||
import { AppError, ASSERT } from './util';
|
||||
|
||||
import path from 'path';
|
||||
|
||||
export class MeshImporter {
|
||||
public static import(filepath: string) {
|
||||
ASSERT(path.isAbsolute(filepath));
|
||||
const parsedPath = path.parse(filepath);
|
||||
|
||||
const importers = [new ObjImporter(), new FbxImporter()];
|
||||
for (const importer of importers) {
|
||||
if (importer.supports(parsedPath.ext.toLowerCase())) {
|
||||
importer.parseFile(filepath);
|
||||
return importer.toMesh();
|
||||
}
|
||||
}
|
||||
|
||||
throw new AppError(`Could not load file-type '${parsedPath.ext.toLowerCase()}' when loading ${parsedPath.base}`);
|
||||
}
|
||||
}
|
@ -5,12 +5,12 @@ import { remote } from 'electron';
|
||||
import * as path from 'path';
|
||||
|
||||
export class FileInputElement extends LabelledElement<string> {
|
||||
private _fileExtension: string;
|
||||
private _fileExtensions: string[];
|
||||
private _loadedFilePath: string;
|
||||
|
||||
public constructor(label: string, fileExtension: string) {
|
||||
public constructor(label: string, fileExtensions: string[]) {
|
||||
super(label);
|
||||
this._fileExtension = fileExtension;
|
||||
this._fileExtensions = fileExtensions;
|
||||
this._loadedFilePath = '';
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ export class FileInputElement extends LabelledElement<string> {
|
||||
buttonLabel: 'Load',
|
||||
filters: [{
|
||||
name: 'Waveform obj file',
|
||||
extensions: [`${this._fileExtension}`],
|
||||
extensions: this._fileExtensions,
|
||||
}],
|
||||
});
|
||||
if (files && files.length === 1) {
|
||||
|
@ -34,7 +34,7 @@ export class UI {
|
||||
'import': {
|
||||
label: 'Import',
|
||||
elements: {
|
||||
'input': new FileInputElement('Wavefront .obj file', 'obj'),
|
||||
'input': new FileInputElement('Model file', ['obj', 'fbx']),
|
||||
},
|
||||
elementsOrder: ['input'],
|
||||
submitButton: new ButtonElement('Load mesh', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user