From 9c29cc96d723f08ae8d3eaba6637343f3d5833d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20=C3=85mdal?= Date: Tue, 17 Aug 2021 20:08:14 +0100 Subject: [PATCH] feat: allow for tab separated obj from Tinkercad This commit changes patterns for obj line parsing to recognize files where the elements are separated by any whitespace (regex \s). This way, we support files exported from Tinkercad. --- lib/loadObj.js | 6 +-- specs/data/box-with-tabs/box-with-tabs.mtl | 12 ++++++ specs/data/box-with-tabs/box-with-tabs.obj | 44 ++++++++++++++++++++++ specs/lib/loadObjSpec.js | 12 ++++++ 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 specs/data/box-with-tabs/box-with-tabs.mtl create mode 100644 specs/data/box-with-tabs/box-with-tabs.obj 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);