forked from mirror/ObjToSchematic
Poly-face support, ObjImporter tests, parse normal data
This commit is contained in:
parent
7b87bfbe61
commit
f4ababcd33
@ -145,7 +145,10 @@ export class AppContext {
|
||||
const uiElements = this._ui.layout.import.elements;
|
||||
const filePath = uiElements.input.getCachedValue();
|
||||
|
||||
this._loadedMesh = new ObjImporter().createMesh(filePath);
|
||||
const importer = new ObjImporter();
|
||||
importer.parseFile(filePath);
|
||||
this._loadedMesh = importer.toMesh();
|
||||
this._loadedMesh.processMesh();
|
||||
Renderer.Get.useMesh(this._loadedMesh);
|
||||
}
|
||||
|
||||
|
@ -2,5 +2,6 @@ import { Mesh } from './mesh';
|
||||
import { Warnable } from './util';
|
||||
|
||||
export abstract class IImporter extends Warnable {
|
||||
abstract createMesh(filePath: string): Mesh;
|
||||
abstract parseFile(filePath: string): void;
|
||||
abstract toMesh(): Mesh;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { IImporter } from '../importer';
|
||||
import { MaterialType, Mesh, SolidMaterial, TexturedMaterial, Tri } from '../mesh';
|
||||
import { Vector3 } from '../vector';
|
||||
import { UV, ASSERT, RGB, CustomError, LOG, REGEX_NUMBER, RegExpBuilder, REGEX_NZ_ANY, LOG_ERROR } from '../util';
|
||||
import { UV, ASSERT, RGB, CustomError, REGEX_NUMBER, RegExpBuilder, REGEX_NZ_ANY, LOG_ERROR } from '../util';
|
||||
import { checkFractional, checkNaN } from '../math';
|
||||
|
||||
import fs from 'fs';
|
||||
@ -9,6 +9,7 @@ import path from 'path';
|
||||
|
||||
export class ObjImporter extends IImporter {
|
||||
private _vertices: Vector3[] = [];
|
||||
private _normals: Vector3[] = [];
|
||||
private _uvs: UV[] = [];
|
||||
private _tris: Tri[] = [];
|
||||
|
||||
@ -22,14 +23,14 @@ export class ObjImporter extends IImporter {
|
||||
private _objParsers = [
|
||||
{
|
||||
// e.g. 'mtllib my_file.mtl'
|
||||
regex: new RegExpBuilder().add(/mtllib/).add(/ /).add(REGEX_NZ_ANY, 'path').toRegExp(),
|
||||
regex: new RegExpBuilder().add(/^mtllib/).add(/ /).add(REGEX_NZ_ANY, 'path').toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
this._mtlLibs.push(match.path.trim());
|
||||
},
|
||||
},
|
||||
{
|
||||
// e.g. 'usemtl my_material'
|
||||
regex: new RegExpBuilder().add(/usemtl/).add(/ /).add(REGEX_NZ_ANY, 'name').toRegExp(),
|
||||
regex: new RegExpBuilder().add(/^usemtl/).add(/ /).add(REGEX_NZ_ANY, 'name').toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
this._currentMaterialName = match.name.trim();
|
||||
ASSERT(this._currentMaterialName, 'invalid material name');
|
||||
@ -38,7 +39,7 @@ export class ObjImporter extends IImporter {
|
||||
{
|
||||
// e.g. 'v 0.123 0.456 0.789'
|
||||
regex: new RegExpBuilder()
|
||||
.add(/v/)
|
||||
.add(/^v/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'x')
|
||||
.addNonzeroWhitespace()
|
||||
@ -54,10 +55,29 @@ export class ObjImporter extends IImporter {
|
||||
this._vertices.push(new Vector3(x, y, z));
|
||||
},
|
||||
},
|
||||
{
|
||||
// e.g. 'vn 0.123 0.456 0.789'
|
||||
regex: new RegExpBuilder()
|
||||
.add(/^vn/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'x')
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'y')
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'z')
|
||||
.toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
const x = parseFloat(match.x);
|
||||
const y = parseFloat(match.y);
|
||||
const z = parseFloat(match.z);
|
||||
checkNaN(x, y, z);
|
||||
this._normals.push(new Vector3(x, y, z));
|
||||
},
|
||||
},
|
||||
{
|
||||
// e.g. 'vt 0.123 0.456'
|
||||
regex: new RegExpBuilder()
|
||||
.add(/vt/)
|
||||
.add(/^vt/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'u')
|
||||
.addNonzeroWhitespace()
|
||||
@ -71,78 +91,96 @@ export class ObjImporter extends IImporter {
|
||||
},
|
||||
},
|
||||
{
|
||||
// e.g. 'f 1/2/3 4/5/6 7/8/9 10/11/12' or 'f 1/2 3/4 5/6 7/8'
|
||||
// e.g. 'f 1/2/3 ...' or 'f 1/2 ...' or 'f 1 ...'
|
||||
regex: new RegExpBuilder()
|
||||
.add(/f/)
|
||||
.add(/^f/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'xIndex').addMany(['/'], true).add(REGEX_NUMBER, 'xtIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'yIndex').addMany(['/'], true).add(REGEX_NUMBER, 'ytIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'zIndex').addMany(['/'], true).add(REGEX_NUMBER, 'ztIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'wIndex').addMany(['/'], true).add(REGEX_NUMBER, 'wtIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.add(/.*/, 'line')
|
||||
.toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
const iX = parseInt(match.xIndex) - 1;
|
||||
const iY = parseInt(match.yIndex) - 1;
|
||||
const iZ = parseInt(match.zIndex) - 1;
|
||||
const iW = parseInt(match.wIndex) - 1;
|
||||
const iUVx = parseInt(match.xtIndex) - 1;
|
||||
const iUVy = parseInt(match.ytIndex) - 1;
|
||||
const iUVz = parseInt(match.ztIndex) - 1;
|
||||
const iUVw = parseInt(match.wtIndex) - 1;
|
||||
checkNaN(iX, iY, iZ, iW);
|
||||
ASSERT(this._currentMaterialName, 'unassigned material');
|
||||
this._tris.push({
|
||||
iX: iW,
|
||||
iY: iY,
|
||||
iZ: iX,
|
||||
iXUV: iUVw,
|
||||
iYUV: iUVy,
|
||||
iZUV: iUVx,
|
||||
material: this._currentMaterialName,
|
||||
});
|
||||
this._tris.push({
|
||||
iX: iW,
|
||||
iY: iZ,
|
||||
iZ: iY,
|
||||
iXUV: iUVw,
|
||||
iYUV: iUVz,
|
||||
iZUV: iUVy,
|
||||
material: this._currentMaterialName,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
// e.g. f 1/2/3 4/5/6 7/8/9 or 1/2 3/4 5/6
|
||||
regex: new RegExpBuilder()
|
||||
.add(/f/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'xIndex').addMany(['/'], true).add(REGEX_NUMBER, 'xtIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'yIndex').addMany(['/'], true).add(REGEX_NUMBER, 'ytIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'zIndex').addMany(['/'], true).add(REGEX_NUMBER, 'ztIndex', true).addMany(['/', REGEX_NUMBER], true)
|
||||
.toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
const iX = parseInt(match.xIndex) - 1;
|
||||
const iY = parseInt(match.yIndex) - 1;
|
||||
const iZ = parseInt(match.zIndex) - 1;
|
||||
const iUVx = parseInt(match.xtIndex) - 1;
|
||||
const iUVy = parseInt(match.ytIndex) - 1;
|
||||
const iUVz = parseInt(match.ztIndex) - 1;
|
||||
checkNaN(iX, iY, iZ);
|
||||
ASSERT(this._currentMaterialName, 'unassigned material');
|
||||
this._tris.push({
|
||||
iX: iX,
|
||||
iY: iY,
|
||||
iZ: iZ,
|
||||
iXUV: iUVx,
|
||||
iYUV: iUVy,
|
||||
iZUV: iUVz,
|
||||
material: this._currentMaterialName,
|
||||
const line = match.line.trim();
|
||||
|
||||
const vertices = line.split(' ').filter((x) => {
|
||||
return x.length !== 0;
|
||||
});
|
||||
|
||||
if (vertices.length < 3) {
|
||||
throw new CustomError('Face data should have at least 3 vertices');
|
||||
}
|
||||
|
||||
const points: {
|
||||
positionIndex: number;
|
||||
texcoordIndex?: number;
|
||||
normalIndex?: number;
|
||||
}[] = [];
|
||||
|
||||
for (const vertex of vertices) {
|
||||
const vertexData = vertex.split('/');
|
||||
switch (vertexData.length) {
|
||||
case 1: {
|
||||
const index = parseInt(vertexData[0]);
|
||||
points.push({
|
||||
positionIndex: index,
|
||||
texcoordIndex: index,
|
||||
normalIndex: index,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
const positionIndex = parseInt(vertexData[0]);
|
||||
const texcoordIndex = parseInt(vertexData[1]);
|
||||
points.push({
|
||||
positionIndex: positionIndex,
|
||||
texcoordIndex: texcoordIndex,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
const positionIndex = parseInt(vertexData[0]);
|
||||
const texcoordIndex = parseInt(vertexData[1]);
|
||||
const normalIndex = parseInt(vertexData[2]);
|
||||
points.push({
|
||||
positionIndex: positionIndex,
|
||||
texcoordIndex: texcoordIndex,
|
||||
normalIndex: normalIndex,
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new CustomError(`Face data has unexpected number of vertex data: ${vertexData.length}`);
|
||||
}
|
||||
}
|
||||
|
||||
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.positionIndex - 1,
|
||||
y: pointA.positionIndex - 1,
|
||||
z: pointB.positionIndex - 1,
|
||||
},
|
||||
material: this._currentMaterialName,
|
||||
};
|
||||
if (pointBase.normalIndex || pointA.normalIndex || pointB.normalIndex) {
|
||||
ASSERT(pointBase.normalIndex && pointA.normalIndex && pointB.normalIndex);
|
||||
tri.normalIndices = {
|
||||
x: pointBase.normalIndex - 1,
|
||||
y: pointA.normalIndex - 1,
|
||||
z: pointB.normalIndex - 1,
|
||||
};
|
||||
}
|
||||
if (pointBase.texcoordIndex || pointA.texcoordIndex || pointB.texcoordIndex) {
|
||||
ASSERT(pointBase.texcoordIndex && pointA.texcoordIndex && pointB.texcoordIndex);
|
||||
tri.texcoordIndices = {
|
||||
x: pointBase.texcoordIndex - 1,
|
||||
y: pointA.texcoordIndex - 1,
|
||||
z: pointB.texcoordIndex - 1,
|
||||
};
|
||||
}
|
||||
this._tris.push(tri);
|
||||
}
|
||||
},
|
||||
},
|
||||
];
|
||||
@ -153,7 +191,7 @@ export class ObjImporter extends IImporter {
|
||||
private _mtlParsers = [
|
||||
{
|
||||
// e.g. 'newmtl my_material'
|
||||
regex: new RegExpBuilder().add(/newmtl/).add(REGEX_NZ_ANY, 'name').toRegExp(),
|
||||
regex: new RegExpBuilder().add(/^newmtl/).add(REGEX_NZ_ANY, 'name').toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
this._addCurrentMaterial();
|
||||
this._currentMaterialName = match.name.trim();
|
||||
@ -164,7 +202,7 @@ export class ObjImporter extends IImporter {
|
||||
{
|
||||
// e.g. 'Kd 0.123 0.456 0.789'
|
||||
regex: new RegExpBuilder()
|
||||
.add(/Kd/)
|
||||
.add(/^Kd/)
|
||||
.addNonzeroWhitespace()
|
||||
.add(REGEX_NUMBER, 'r')
|
||||
.addNonzeroWhitespace()
|
||||
@ -184,7 +222,7 @@ export class ObjImporter extends IImporter {
|
||||
},
|
||||
{
|
||||
// e.g. 'map_Kd my/path/to/file.png'
|
||||
regex: new RegExpBuilder().add(/map_Kd/).add(REGEX_NZ_ANY, 'path').toRegExp(),
|
||||
regex: new RegExpBuilder().add(/^map_Kd/).add(REGEX_NZ_ANY, 'path').toRegExp(),
|
||||
delegate: (match: { [key: string]: string }) => {
|
||||
let mtlPath = match.path.trim();
|
||||
if (!path.isAbsolute(mtlPath)) {
|
||||
@ -197,7 +235,7 @@ export class ObjImporter extends IImporter {
|
||||
},
|
||||
];
|
||||
|
||||
override createMesh(filePath: string): Mesh {
|
||||
override parseFile(filePath: string) {
|
||||
ASSERT(path.isAbsolute(filePath), 'path not absolute');
|
||||
|
||||
this._objPath = path.parse(filePath);
|
||||
@ -216,9 +254,10 @@ export class ObjImporter extends IImporter {
|
||||
}
|
||||
|
||||
this._parseMTL();
|
||||
}
|
||||
|
||||
LOG(this);
|
||||
return new Mesh(this._vertices, this._uvs, this._tris, this._materials);
|
||||
override toMesh(): Mesh {
|
||||
return new Mesh(this._vertices, this._normals, this._uvs, this._tris, this._materials);
|
||||
}
|
||||
|
||||
private _parseOBJ(path: string) {
|
||||
@ -234,12 +273,12 @@ export class ObjImporter extends IImporter {
|
||||
const fileLines = fileContents.split('\n');
|
||||
|
||||
for (const line of fileLines) {
|
||||
this._parseOBJLine(line);
|
||||
this.parseOBJLine(line);
|
||||
}
|
||||
}
|
||||
|
||||
private _parseOBJLine(line: string) {
|
||||
const essentialTokens = ['mtllib ', 'usemtl ', 'v ', 'vt ', 'f '];
|
||||
public parseOBJLine(line: string) {
|
||||
const essentialTokens = ['mtllib ', 'usemtl ', 'v ', 'vt ', 'f ', 'vn '];
|
||||
|
||||
for (const parser of this._objParsers) {
|
||||
const match = parser.regex.exec(line);
|
||||
|
89
src/mesh.ts
89
src/mesh.ts
@ -1,18 +1,21 @@
|
||||
import { Vector3 } from './vector';
|
||||
import { UV, Bounds, LOG, ASSERT, CustomError, LOG_WARN, Warnable, getRandomID } from './util';
|
||||
import { UVTriangle } from './triangle';
|
||||
import { Triangle, UVTriangle } from './triangle';
|
||||
import { RGB } from './util';
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
|
||||
interface VertexIndices {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
}
|
||||
|
||||
export interface Tri {
|
||||
iX: number;
|
||||
iY: number;
|
||||
iZ: number;
|
||||
iXUV: number;
|
||||
iYUV: number;
|
||||
iZUV: number;
|
||||
positionIndices: VertexIndices;
|
||||
texcoordIndices?: VertexIndices;
|
||||
normalIndices?: VertexIndices;
|
||||
material: string;
|
||||
}
|
||||
|
||||
@ -24,7 +27,8 @@ export interface TexturedMaterial { path: string; type: MaterialType.textured }
|
||||
export type MaterialMap = {[key: string]: (SolidMaterial | TexturedMaterial)};
|
||||
|
||||
export class Mesh extends Warnable {
|
||||
public vertices!: Vector3[];
|
||||
public vertices: Vector3[];
|
||||
public normals!: Vector3[];
|
||||
public uvs!: UV[];
|
||||
public tris!: Tri[];
|
||||
public materials!: MaterialMap;
|
||||
@ -32,23 +36,23 @@ export class Mesh extends Warnable {
|
||||
|
||||
public static desiredHeight = 8.0;
|
||||
|
||||
constructor(vertices: Vector3[], uvs: UV[], tris: Tri[], materials: MaterialMap) {
|
||||
constructor(vertices: Vector3[], normals: Vector3[], uvs: UV[], tris: Tri[], materials: MaterialMap) {
|
||||
super();
|
||||
LOG('New mesh');
|
||||
|
||||
this.vertices = vertices;
|
||||
this.normals = normals;
|
||||
this.uvs = uvs;
|
||||
this.tris = tris;
|
||||
this.materials = materials;
|
||||
this.id = getRandomID();
|
||||
}
|
||||
|
||||
public processMesh() {
|
||||
this._checkMesh();
|
||||
this._checkMaterials();
|
||||
|
||||
this._centreMesh();
|
||||
this._scaleMesh();
|
||||
|
||||
LOG('Loaded mesh', this);
|
||||
}
|
||||
|
||||
public getBounds() {
|
||||
@ -71,6 +75,7 @@ export class Mesh extends Warnable {
|
||||
}
|
||||
|
||||
// Check UVs are inside [0, 1]
|
||||
/*
|
||||
for (const uv of this.uvs) {
|
||||
if (uv.u < 0.0 || uv.u > 1.0) {
|
||||
uv.u = Math.abs(uv.u % 1);
|
||||
@ -79,6 +84,7 @@ export class Mesh extends Warnable {
|
||||
uv.v = Math.abs(uv.v % 1);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private _checkMaterials() {
|
||||
@ -173,30 +179,56 @@ export class Mesh extends Warnable {
|
||||
public getVertices(triIndex: number) {
|
||||
const tri = this.tris[triIndex];
|
||||
return {
|
||||
v0: this.vertices[tri.iX],
|
||||
v1: this.vertices[tri.iY],
|
||||
v2: this.vertices[tri.iZ],
|
||||
v0: this.vertices[tri.positionIndices.x],
|
||||
v1: this.vertices[tri.positionIndices.y],
|
||||
v2: this.vertices[tri.positionIndices.z],
|
||||
};
|
||||
}
|
||||
|
||||
public getUVs(triIndex: number) {
|
||||
const tri = this.tris[triIndex];
|
||||
if (tri.texcoordIndices) {
|
||||
return {
|
||||
uv0: this.uvs[tri.texcoordIndices.x] || new UV(0.0, 0.0),
|
||||
uv1: this.uvs[tri.texcoordIndices.y] || new UV(0.0, 0.0),
|
||||
uv2: this.uvs[tri.texcoordIndices.z] || new UV(0.0, 0.0),
|
||||
};
|
||||
}
|
||||
return {
|
||||
uv0: this.uvs[tri.iXUV],
|
||||
uv1: this.uvs[tri.iYUV],
|
||||
uv2: this.uvs[tri.iZUV],
|
||||
uv0: new UV(0.0, 0.0),
|
||||
uv1: new UV(0.0, 0.0),
|
||||
uv2: new UV(0.0, 0.0),
|
||||
};
|
||||
}
|
||||
|
||||
public getNormals(triIndex: number) {
|
||||
const vertexData = this.getVertices(triIndex);
|
||||
const faceNormal = new Triangle(vertexData.v0, vertexData.v1, vertexData.v2).getNormal();
|
||||
const tri = this.tris[triIndex];
|
||||
if (tri.normalIndices) {
|
||||
return {
|
||||
v0: this.normals[tri.normalIndices.x] || faceNormal,
|
||||
v1: this.normals[tri.normalIndices.y] || faceNormal,
|
||||
v2: this.normals[tri.normalIndices.z] || faceNormal,
|
||||
};
|
||||
}
|
||||
return {
|
||||
v0: faceNormal,
|
||||
v1: faceNormal,
|
||||
v2: faceNormal,
|
||||
};
|
||||
}
|
||||
|
||||
public getUVTriangle(triIndex: number): UVTriangle {
|
||||
const tri = this.tris[triIndex];
|
||||
const vertices = this.getVertices(triIndex);
|
||||
const texcoords = this.getUVs(triIndex);
|
||||
return new UVTriangle(
|
||||
this.vertices[tri.iX],
|
||||
this.vertices[tri.iY],
|
||||
this.vertices[tri.iZ],
|
||||
this.uvs[tri.iXUV] || 0.0,
|
||||
this.uvs[tri.iYUV] || 0.0,
|
||||
this.uvs[tri.iZUV] || 0.0,
|
||||
vertices.v0,
|
||||
vertices.v1,
|
||||
vertices.v2,
|
||||
texcoords.uv0,
|
||||
texcoords.uv1,
|
||||
texcoords.uv2,
|
||||
);
|
||||
}
|
||||
|
||||
@ -241,6 +273,11 @@ export class Mesh extends Warnable {
|
||||
newVertices[i] = this.vertices[i].copy();
|
||||
}
|
||||
|
||||
const newNormals = new Array<Vector3>(this.normals.length);
|
||||
for (let i = 0; i < this.normals.length; ++i) {
|
||||
newNormals[i] = this.normals[i].copy();
|
||||
}
|
||||
|
||||
const newUVs = new Array<UV>(this.uvs.length);
|
||||
for (let i = 0; i < this.uvs.length; ++i) {
|
||||
newUVs[i] = this.uvs[i].copy();
|
||||
@ -268,7 +305,7 @@ export class Mesh extends Warnable {
|
||||
};
|
||||
}
|
||||
|
||||
return new Mesh(newVertices, newUVs, newTris, materials);
|
||||
return new Mesh(newVertices, newNormals, newUVs, newTris, materials);
|
||||
}
|
||||
|
||||
public getTriangleCount(): number {
|
||||
|
174
test/obj_importer.test.ts
Normal file
174
test/obj_importer.test.ts
Normal file
@ -0,0 +1,174 @@
|
||||
import { ObjImporter } from '../src/importers/obj_importer';
|
||||
import { ASSERT } from '../src/util';
|
||||
import { Vector3 } from '../src/vector';
|
||||
|
||||
test('Parse vertex #1', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('v 1.0 -2.0 3.0');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.vertices.length).toEqual(1);
|
||||
expect(mesh.vertices[0].equals(new Vector3(1, -2, 3))).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse normal #1', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('vn -1.0 -0.5 0.0');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.normals.length).toEqual(1);
|
||||
expect(mesh.normals[0].equals(new Vector3(-1, -0.5, 0))).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse texcoord #1', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('vt 0.5 -0.8');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.uvs.length).toEqual(1);
|
||||
expect(mesh.uvs[0].u === 0.5 && mesh.uvs[0].v === -0.8).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse face #1', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('f 12 24 36');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
const tri = mesh.tris[0];
|
||||
expect(tri.texcoordIndices).toBeDefined(); ASSERT(tri.texcoordIndices);
|
||||
expect(tri.normalIndices).toBeDefined(); ASSERT(tri.normalIndices);
|
||||
expect(tri.positionIndices.x === 12 - 1 && tri.positionIndices.y === 24 - 1 && tri.positionIndices.z === 36 - 1).toBe(true);
|
||||
expect(tri.texcoordIndices.x === 12 - 1 && tri.texcoordIndices.y === 24 - 1 && tri.texcoordIndices.z === 36 - 1).toBe(true);
|
||||
expect(tri.normalIndices.x === 12 - 1 && tri.normalIndices.y === 24 - 1 && tri.normalIndices.z === 36 - 1).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse face #2', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('f 1/2 3/4 5/6');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
const tri = mesh.tris[0];
|
||||
expect(tri.texcoordIndices).toBeDefined(); ASSERT(tri.texcoordIndices);
|
||||
expect(tri.normalIndices).toBeUndefined();
|
||||
expect(tri.positionIndices.x === 1 - 1 && tri.positionIndices.y === 3 - 1 && tri.positionIndices.z === 5 - 1).toBe(true);
|
||||
expect(tri.texcoordIndices.x === 2 - 1 && tri.texcoordIndices.y === 4 - 1 && tri.texcoordIndices.z === 6 - 1).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse face #3', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('f 11/2/3 4/55/6 7/8/99');
|
||||
const mesh = importer.toMesh();
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
const tri = mesh.tris[0];
|
||||
expect(tri.texcoordIndices).toBeDefined(); ASSERT(tri.texcoordIndices);
|
||||
expect(tri.normalIndices).toBeDefined(); ASSERT(tri.normalIndices);
|
||||
expect(tri.positionIndices.x === 11 - 1 && tri.positionIndices.y === 4 - 1&& tri.positionIndices.z === 7 - 1).toBe(true);
|
||||
expect(tri.texcoordIndices.x === 2 - 1 && tri.texcoordIndices.y === 55 - 1 && tri.texcoordIndices.z === 8 - 1).toBe(true);
|
||||
expect(tri.normalIndices.x === 3 - 1 && tri.normalIndices.y === 6 - 1 && tri.normalIndices.z === 99 - 1).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse mini #1', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('v -1 2 3');
|
||||
importer.parseOBJLine('v 4 -5 6');
|
||||
importer.parseOBJLine('v 7 8 -9');
|
||||
importer.parseOBJLine('vn 0.0 0.1 0.2');
|
||||
importer.parseOBJLine('vn 0.3 0.4 0.5');
|
||||
importer.parseOBJLine('vn 0.6 0.7 0.8');
|
||||
importer.parseOBJLine('vt 0.0 0.5');
|
||||
importer.parseOBJLine('vt 0.5 1.0');
|
||||
importer.parseOBJLine('vt 1.0 0.0');
|
||||
importer.parseOBJLine('f 1 2 3');
|
||||
const mesh = importer.toMesh();
|
||||
|
||||
expect(mesh.vertices.length).toEqual(3);
|
||||
expect(mesh.uvs.length).toEqual(3);
|
||||
expect(mesh.normals.length).toEqual(3);
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
|
||||
const vertexData = mesh.getVertices(0);
|
||||
expect(vertexData.v0.equals(new Vector3(-1, 2, 3))).toBe(true);
|
||||
expect(vertexData.v1.equals(new Vector3(4, -5, 6))).toBe(true);
|
||||
expect(vertexData.v2.equals(new Vector3(7, 8, -9))).toBe(true);
|
||||
|
||||
const texcoordData = mesh.getUVs(0);
|
||||
expect(texcoordData.uv0.u === 0.0 && texcoordData.uv0.v === 0.5).toBe(true);
|
||||
expect(texcoordData.uv1.u === 0.5 && texcoordData.uv1.v === 1.0).toBe(true);
|
||||
expect(texcoordData.uv2.u === 1.0 && texcoordData.uv2.v === 0.0).toBe(true);
|
||||
|
||||
const normalData = mesh.getNormals(0);
|
||||
expect(normalData.v0.equals(new Vector3(0.0, 0.1, 0.2))).toBe(true);
|
||||
expect(normalData.v1.equals(new Vector3(0.3, 0.4, 0.5))).toBe(true);
|
||||
expect(normalData.v2.equals(new Vector3(0.6, 0.7, 0.8))).toBe(true);
|
||||
});
|
||||
|
||||
|
||||
test('Parse mini #2', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('v -1 2 3');
|
||||
importer.parseOBJLine('v 4 -5 6');
|
||||
importer.parseOBJLine('v 7 8 -9');
|
||||
importer.parseOBJLine('vn 0.0 0.1 0.2');
|
||||
importer.parseOBJLine('vn 0.3 0.4 0.5');
|
||||
importer.parseOBJLine('vn 0.6 0.7 0.8');
|
||||
importer.parseOBJLine('vt 0.0 0.5');
|
||||
importer.parseOBJLine('vt 0.5 1.0');
|
||||
importer.parseOBJLine('vt 1.0 0.0');
|
||||
importer.parseOBJLine('f 3/1/2 1/2/3 2/3/1');
|
||||
const mesh = importer.toMesh();
|
||||
|
||||
expect(mesh.vertices.length).toEqual(3);
|
||||
expect(mesh.uvs.length).toEqual(3);
|
||||
expect(mesh.normals.length).toEqual(3);
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
|
||||
const vertexData = mesh.getVertices(0);
|
||||
expect(vertexData.v0.equals(new Vector3(7, 8, -9))).toBe(true);
|
||||
expect(vertexData.v1.equals(new Vector3(-1, 2, 3))).toBe(true);
|
||||
expect(vertexData.v2.equals(new Vector3(4, -5, 6))).toBe(true);
|
||||
|
||||
const texcoordData = mesh.getUVs(0);
|
||||
expect(texcoordData.uv0.u === 0.0 && texcoordData.uv0.v === 0.5).toBe(true);
|
||||
expect(texcoordData.uv1.u === 0.5 && texcoordData.uv1.v === 1.0).toBe(true);
|
||||
expect(texcoordData.uv2.u === 1.0 && texcoordData.uv2.v === 0.0).toBe(true);
|
||||
|
||||
const normalData = mesh.getNormals(0);
|
||||
expect(normalData.v0.equals(new Vector3(0.3, 0.4, 0.5))).toBe(true);
|
||||
expect(normalData.v1.equals(new Vector3(0.6, 0.7, 0.8))).toBe(true);
|
||||
expect(normalData.v2.equals(new Vector3(0.0, 0.1, 0.2))).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse mini #3', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('v 0 0 0');
|
||||
importer.parseOBJLine('v 1 0 0');
|
||||
importer.parseOBJLine('v 0 1 0');
|
||||
importer.parseOBJLine('f 1 2 3');
|
||||
const mesh = importer.toMesh();
|
||||
|
||||
expect(mesh.vertices.length).toEqual(3);
|
||||
expect(mesh.uvs.length).toEqual(0);
|
||||
expect(mesh.normals.length).toEqual(0);
|
||||
expect(mesh.tris.length).toEqual(1);
|
||||
|
||||
const texcoordData = mesh.getUVs(0);
|
||||
expect(texcoordData.uv0.u === 0.0 && texcoordData.uv0.v === 0.0).toBe(true);
|
||||
expect(texcoordData.uv1.u === 0.0 && texcoordData.uv1.v === 0.0).toBe(true);
|
||||
expect(texcoordData.uv2.u === 0.0 && texcoordData.uv2.v === 0.0).toBe(true);
|
||||
|
||||
const normalData = mesh.getNormals(0);
|
||||
expect(normalData.v0.equals(new Vector3(0.0, 0.0, 1.0))).toBe(true);
|
||||
expect(normalData.v1.equals(new Vector3(0.0, 0.0, 1.0))).toBe(true);
|
||||
expect(normalData.v2.equals(new Vector3(0.0, 0.0, 1.0))).toBe(true);
|
||||
});
|
||||
|
||||
test('Parse comments', () => {
|
||||
const importer = new ObjImporter();
|
||||
importer.parseOBJLine('# v -1 2 3');
|
||||
importer.parseOBJLine('# vn 0.0 0.1 0.2');
|
||||
importer.parseOBJLine('# vt 0.0 0.5');
|
||||
importer.parseOBJLine('# f 1 1 1');
|
||||
const mesh = importer.toMesh();
|
||||
|
||||
expect(mesh.vertices.length).toEqual(0);
|
||||
expect(mesh.uvs.length).toEqual(0);
|
||||
expect(mesh.normals.length).toEqual(0);
|
||||
expect(mesh.tris.length).toEqual(0);
|
||||
});
|
Loading…
Reference in New Issue
Block a user