This commit is contained in:
Sean Lilley 2019-05-29 08:50:35 -04:00
parent 601fca2794
commit 0f0bb8296a
6 changed files with 103 additions and 20 deletions

View File

@ -1,6 +1,10 @@
Change Log Change Log
========== ==========
### 3.0.3 2019-??-??
* Fixed parsing obj files that contain tabs.
### 3.0.2 2019-03-21 ### 3.0.2 2019-03-21
* Fixed a crash when saving separate resources that would exceed the Node buffer size limit. [#173](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/173) * Fixed a crash when saving separate resources that would exceed the Node buffer size limit. [#173](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/173)

View File

@ -96,7 +96,7 @@ function loadMtl(mtlPath, options) {
if (/^newmtl/i.test(line)) { if (/^newmtl/i.test(line)) {
const name = line.substring(7).trim(); const name = line.substring(7).trim();
createMaterial(name); createMaterial(name);
} else if (/^Ka /i.test(line)) { } else if (/^Ka\s+/i.test(line)) {
values = line.substring(3).trim().split(' '); values = line.substring(3).trim().split(' ');
material.ambientColor = [ material.ambientColor = [
parseFloat(values[0]), parseFloat(values[0]),
@ -104,7 +104,7 @@ function loadMtl(mtlPath, options) {
parseFloat(values[2]), parseFloat(values[2]),
1.0 1.0
]; ];
} else if (/^Ke /i.test(line)) { } else if (/^Ke\s+/i.test(line)) {
values = line.substring(3).trim().split(' '); values = line.substring(3).trim().split(' ');
material.emissiveColor = [ material.emissiveColor = [
parseFloat(values[0]), parseFloat(values[0]),
@ -112,7 +112,7 @@ function loadMtl(mtlPath, options) {
parseFloat(values[2]), parseFloat(values[2]),
1.0 1.0
]; ];
} else if (/^Kd /i.test(line)) { } else if (/^Kd\s+/i.test(line)) {
values = line.substring(3).trim().split(' '); values = line.substring(3).trim().split(' ');
material.diffuseColor = [ material.diffuseColor = [
parseFloat(values[0]), parseFloat(values[0]),
@ -120,7 +120,7 @@ function loadMtl(mtlPath, options) {
parseFloat(values[2]), parseFloat(values[2]),
1.0 1.0
]; ];
} else if (/^Ks /i.test(line)) { } else if (/^Ks\s+/i.test(line)) {
values = line.substring(3).trim().split(' '); values = line.substring(3).trim().split(' ');
material.specularColor = [ material.specularColor = [
parseFloat(values[0]), parseFloat(values[0]),
@ -128,40 +128,40 @@ function loadMtl(mtlPath, options) {
parseFloat(values[2]), parseFloat(values[2]),
1.0 1.0
]; ];
} else if (/^Ns /i.test(line)) { } else if (/^Ns\s+/i.test(line)) {
value = line.substring(3).trim(); value = line.substring(3).trim();
material.specularShininess = parseFloat(value); material.specularShininess = parseFloat(value);
} else if (/^d /i.test(line)) { } else if (/^d\s+/i.test(line)) {
value = line.substring(2).trim(); value = line.substring(2).trim();
material.alpha = correctAlpha(parseFloat(value)); material.alpha = correctAlpha(parseFloat(value));
} else if (/^Tr /i.test(line)) { } else if (/^Tr\s+/i.test(line)) {
value = line.substring(3).trim(); value = line.substring(3).trim();
material.alpha = correctAlpha(1.0 - parseFloat(value)); material.alpha = correctAlpha(1.0 - parseFloat(value));
} else if (/^map_Ka /i.test(line)) { } else if (/^map_Ka\s+/i.test(line)) {
if (!defined(overridingAmbientTexture)) { if (!defined(overridingAmbientTexture)) {
material.ambientTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); material.ambientTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory);
} }
} else if (/^map_Ke /i.test(line)) { } else if (/^map_Ke\s+/i.test(line)) {
if (!defined(overridingEmissiveTexture)) { if (!defined(overridingEmissiveTexture)) {
material.emissiveTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); material.emissiveTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory);
} }
} else if (/^map_Kd /i.test(line)) { } else if (/^map_Kd\s+/i.test(line)) {
if (!defined(overridingDiffuseTexture)) { if (!defined(overridingDiffuseTexture)) {
material.diffuseTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); material.diffuseTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory);
} }
} else if (/^map_Ks /i.test(line)) { } else if (/^map_Ks\s+/i.test(line)) {
if (!defined(overridingSpecularTexture)) { if (!defined(overridingSpecularTexture)) {
material.specularTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); material.specularTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory);
} }
} else if (/^map_Ns /i.test(line)) { } else if (/^map_Ns\s+/i.test(line)) {
if (!defined(overridingSpecularShininessTexture)) { if (!defined(overridingSpecularShininessTexture)) {
material.specularShininessTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory); material.specularShininessTexture = normalizeTexturePath(line.substring(7).trim(), mtlDirectory);
} }
} else if (/^map_Bump /i.test(line)) { } else if (/^map_Bump\s+/i.test(line)) {
if (!defined(overridingNormalTexture)) { if (!defined(overridingNormalTexture)) {
material.normalTexture = normalizeTexturePath(line.substring(9).trim(), mtlDirectory); material.normalTexture = normalizeTexturePath(line.substring(9).trim(), mtlDirectory);
} }
} else if (/^map_d /i.test(line)) { } else if (/^map_d\s+/i.test(line)) {
if (!defined(overridingAlphaTexture)) { if (!defined(overridingAlphaTexture)) {
material.alphaTexture = normalizeTexturePath(line.substring(6).trim(), mtlDirectory); material.alphaTexture = normalizeTexturePath(line.substring(6).trim(), mtlDirectory);
} }

View File

@ -42,10 +42,11 @@ function Primitive() {
} }
// OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js) // OBJ regex patterns are modified from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js)
const vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // v float float float const vertexPattern = /v(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)/; // v float float float
const normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vn float float float const normalPattern = /vn(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)/; // vn float float float
const uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float const uvPattern = /vt(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)/; // vt float float
const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g; // for any face format "f v", "f v/v", "f v//v", "f v/v/v" const facePattern = /(-?\d+)\/?(-?\d*)\/?(-?\d*)/g; // for any face format "f v", "f v/v", "f v//v", "f v/v/v"
const faceStartPattern = /f\s+/;
/** /**
* Parse an obj file. * Parse an obj file.
@ -356,11 +357,11 @@ function loadObj(objPath, options) {
// Because face lines can contain n vertices, we use a line buffer in case the face data spans multiple lines. // Because face lines can contain n vertices, we use a line buffer in case the face data spans multiple lines.
// If there's a line continuation don't create face yet // If there's a line continuation don't create face yet
if (line.slice(-1) === '\\') { if (line.slice(-1) === '\\') {
lineBuffer += line.substring(0, line.length-1); lineBuffer += line.slice(0, -1);
return; return;
} }
lineBuffer += line; lineBuffer += line;
if (lineBuffer.substring(0, 2) === 'f ') { if (faceStartPattern.test(lineBuffer)) {
while ((result = facePattern.exec(lineBuffer)) !== null) { while ((result = facePattern.exec(lineBuffer)) !== null) {
faceVertices.push(result[0]); faceVertices.push(result[0]);
facePositions.push(result[1]); facePositions.push(result[1]);
@ -402,7 +403,7 @@ function loadObj(objPath, options) {
function getMtlPaths(mtllibLine) { function getMtlPaths(mtllibLine) {
// Handle paths with spaces. E.g. mtllib my material file.mtl // Handle paths with spaces. E.g. mtllib my material file.mtl
const mtlPaths = []; const mtlPaths = [];
const splits = mtllibLine.split(' '); const splits = mtllibLine.split(/\s/);
const length = splits.length; const length = splits.length;
let startIndex = 0; let startIndex = 0;
for (let i = 0; i < length; ++i) { for (let i = 0; i < length; ++i) {

View File

@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Material
Ns 96.078431
Ka 0.100000 0.000000 0.000000
Kd 0.640000 0.640000 0.640000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.100000
Ni 1.000000
d 1.000000
illum 2

View File

@ -0,0 +1,46 @@
#Blender v2.78 (sub 0) OBJ File: ''
# www.blender.org
mtllib box-tabs.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 0.0000 0.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 1.0000
vt 1.0000 0.0000
vt 1.0000 1.0000
vt 0.0000 0.0000
vt 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl Material
s off
f 1/1/1 2/2/1 4/3/1 3/4/1
f 3/5/2 4/6/2 8/7/2 7/8/2
f 7/9/3 8/10/3 6/11/3 5/12/3
f 5/13/4 6/14/4 2/15/4 1/16/4
f 3/5/5 7/17/5 5/18/5 1/16/5
f 8/19/6 4/6/6 2/15/6 6/20/6

View File

@ -10,6 +10,7 @@ const clone = Cesium.clone;
const RuntimeError = Cesium.RuntimeError; const RuntimeError = Cesium.RuntimeError;
const objPath = 'specs/data/box/box.obj'; const objPath = 'specs/data/box/box.obj';
const objTabsPath = 'specs/data/box-tabs/box-tabs.obj';
const objNormalsPath = 'specs/data/box-normals/box-normals.obj'; const objNormalsPath = 'specs/data/box-normals/box-normals.obj';
const objUvsPath = 'specs/data/box-uvs/box-uvs.obj'; const objUvsPath = 'specs/data/box-uvs/box-uvs.obj';
const objPositionsOnlyPath = 'specs/data/box-positions-only/box-positions-only.obj'; const objPositionsOnlyPath = 'specs/data/box-positions-only/box-positions-only.obj';
@ -102,6 +103,25 @@ describe('loadObj', () => {
expect(primitive.material).toBe('Material'); expect(primitive.material).toBe('Material');
}); });
it('loads obj with tabs', async () => {
const data = await loadObj(objTabsPath, options);
const primitives = getPrimitives(data);
const primitive = primitives[0];
expect(primitive.positions.length / 3).toBe(24);
expect(primitive.normals.length / 3).toBe(24);
expect(primitive.uvs.length / 2).toBe(24);
expect(primitive.indices.length).toBe(36);
expect(primitive.material).toBe('Material');
const material = data.materials[0];
const pbr = material.pbrMetallicRoughness;
expect(pbr.baseColorFactor).toEqual([0.64, 0.64, 0.64, 1.0]);
expect(pbr.metallicFactor).toBe(0.0);
expect(CesiumMath.equalsEpsilon(pbr.roughnessFactor, 0.903921569, CesiumMath.EPSILON7)).toBe(true);
expect(material.emissiveFactor).toEqual([0.0, 0.0, 0.1]);
});
it('loads obj with normals', async () => { it('loads obj with normals', async () => {
const data = await loadObj(objNormalsPath, options); const data = await loadObj(objNormalsPath, options);
const primitive = getPrimitives(data)[0]; const primitive = getPrimitives(data)[0];