diff --git a/lib/loadObj.js b/lib/loadObj.js index c0e5a6f..ec2984e 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -45,10 +45,10 @@ function Primitive() { // 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 + /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 uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; // vt float float + /vn(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)(\s+[\d|\.|\+|\-|e|E]+)/; // vn float 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 scratchCartesian = new Cartesian3(); diff --git a/specs/data/box-with-tabs/box-with-tabs.mtl b/specs/data/box-with-tabs/box-with-tabs.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-with-tabs/box-with-tabs.mtl @@ -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 diff --git a/specs/data/box-with-tabs/box-with-tabs.obj b/specs/data/box-with-tabs/box-with-tabs.obj new file mode 100644 index 0000000..263a0c2 --- /dev/null +++ b/specs/data/box-with-tabs/box-with-tabs.obj @@ -0,0 +1,44 @@ +mtllib box.mtl +o Cube +v -1.000000 -1.00000 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 diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index 11355e6..e7752d3 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -66,6 +66,7 @@ const objIncompleteUvsPath = "specs/data/box-incomplete-attributes/box-incomplete-uvs.obj"; const objIncorrectWindingOrderPath = "specs/data/box-incorrect-winding-order/box-incorrect-winding-order.obj"; +const objWithTabs = "specs/data/box-with-tabs/box-with-tabs.obj"; const objInvalidPath = "invalid.obj"; function getMeshes(data) { @@ -535,6 +536,17 @@ describe("loadObj", () => { expect(baseColorTexture.source).toBeDefined(); }); + it("loads an obj where coordinates are separated by tabs", async () => { + /** + * We know Tinkercad to produce files with coordinates separated by tabs. + */ + const data = await loadObj(objWithTabs, options); + const primitive = getPrimitives(data)[0]; + expect(primitive.positions.length / 3).toBe(24); + expect(primitive.normals.length / 3).toBe(24); + expect(primitive.uvs.length / 2).toBe(24); + }); + it("separates faces that don't use the same attributes as other faces in the primitive", async () => { const data = await loadObj(objMixedAttributesPath, options); const primitives = getPrimitives(data);