Added basic .fbx parsing

This commit is contained in:
Lucas Dower 2022-04-24 03:19:02 +01:00
parent 1a7c33b9b1
commit 53456e957e
7 changed files with 160 additions and 897 deletions

919
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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';
@ -19,6 +18,7 @@ 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 {
@ -139,19 +139,9 @@ export class AppContext {
const uiElements = this._ui.layout.import.elements;
const filePath = uiElements.input.getCachedValue();
const parsedPath = path.parse(filePath);
const importers = [new ObjImporter()];
for (const importer of importers) {
if (importer.supports(parsedPath.ext.toLowerCase())) {
this._loadedMesh = importer.toMesh();
this._loadedMesh.processMesh();
Renderer.Get.useMesh(this._loadedMesh);
return;
}
}
throw new AppError(`Could not load file-type '${parsedPath.ext.toLowerCase()}' when loading ${parsedPath.base}`);
this._loadedMesh = MeshImporter.import(filePath);
this._loadedMesh.processMesh();
Renderer.Get.useMesh(this._loadedMesh);
}
private _simplify() {

View File

@ -1,4 +1,4 @@
import { Mesh } from './mesh';
import { Mesh } from '../mesh';
export abstract class IImporter {
public abstract parseFile(filePath: string): void;

View 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'];
}
}

View File

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

22
src/mesh_importer.ts Normal file
View 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}`);
}
}