/** * adapted from THREE.OBJLoader example * * https://en.wikipedia.org/wiki/Wavefront_.obj_file */ 'use strict'; (function() { if (!self.moto) self.moto = {}; self.moto.OBJ = { parse : parse }; /** * @param {String} text * @returns {Array} */ function parse(text) { var object, objects = [], geometry, material; function parseVertexIndex(value) { var index = parseInt(value); return (index >= 0 ? index - 1 : index + vertices.length / 3) * 3; } function parseNormalIndex(value) { var index = parseInt(value); return (index >= 0 ? index - 1 : index + normals.length / 3) * 3; } function parseUVIndex(value) { var index = parseInt(value); return (index >= 0 ? index - 1 : index + uvs.length / 2) * 2; } function addVertex(a, b, c) { geometry.vertices.push( vertices[a], vertices[a + 1], vertices[a + 2], vertices[b], vertices[b + 1], vertices[b + 2], vertices[c], vertices[c + 1], vertices[c + 2] ); } function addNormal(a, b, c) { geometry.normals.push( normals[a], normals[a + 1], normals[a + 2], normals[b], normals[b + 1], normals[b + 2], normals[c], normals[c + 1], normals[c + 2] ); } function addUV(a, b, c) { geometry.uvs.push( uvs[a], uvs[a + 1], uvs[b], uvs[b + 1], uvs[c], uvs[c + 1] ); } function addFace(a, b, c, d, ua, ub, uc, ud, na, nb, nc, nd) { var ia = parseVertexIndex(a); var ib = parseVertexIndex(b); var ic = parseVertexIndex(c); var id; if (d === undefined) { addVertex(ia, ib, ic); } else { id = parseVertexIndex(d); addVertex(ia, ib, id); addVertex(ib, ic, id); } if (ua !== undefined) { ia = parseUVIndex(ua); ib = parseUVIndex(ub); ic = parseUVIndex(uc); if (d === undefined) { addUV(ia, ib, ic); } else { id = parseUVIndex(ud); addUV(ia, ib, id); addUV(ib, ic, id); } } if (na !== undefined) { ia = parseNormalIndex(na); ib = parseNormalIndex(nb); ic = parseNormalIndex(nc); if (d === undefined) { addNormal(ia, ib, ic); } else { id = parseNormalIndex(nd); addNormal(ia, ib, id); addNormal(ib, ic, id); } } } // create mesh if no objects in text if (/^o /gm.test(text) === false) { geometry = { vertices: [], normals: [], uvs: [] }; object = { name: '', geometry: geometry }; objects.push(object); } var vertices = [], normals = [], uvs = [], // v float float float vertex_pattern = /v(+[\d|\.|\+|\-|e|E]+)(+[\d|\.|\+|\-|e|E]+)(+[\d|\.|\+|\-|e|E]+)/, // vn float float float normal_pattern = /vn(+[\d|\.|\+|\-|e|E]+)(+[\d|\.|\+|\-|e|E]+)(+[\d|\.|\+|\-|e|E]+)/, // vt float float uv_pattern = /vt(+[\d|\.|\+|\-|e|E]+)(+[\d|\.|\+|\-|e|E]+)/, // f vertex vertex vertex ... face_pattern1 = /f(+-?\d+)(+-?\d+)(+-?\d+)(+-?\d+)?/, // f vertex/uv vertex/uv vertex/uv ... face_pattern2 = /f(+(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+))?/, // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... face_pattern3 = /f(+(-?\d+)\/(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+)\/(-?\d+))(+(-?\d+)\/(-?\d+)\/(-?\d+))?/, // f vertex//normal vertex//normal vertex//normal ... face_pattern4 = /f(+(-?\d+)\/\/(-?\d+))(+(-?\d+)\/\/(-?\d+))(+(-?\d+)\/\/(-?\d+))(+(-?\d+)\/\/(-?\d+))?/, // split text into lines lines = text.split('\n'); for (var i = 0; i < lines.length; i++) { var line = lines[i]; line = line.trim(); var result; if (line.length === 0 || line.charAt(0) === '#') { continue; } else if ((result = vertex_pattern.exec(line)) !== null) { // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] vertices.push( parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]) ); } else if ((result = normal_pattern.exec(line)) !== null) { // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] normals.push( parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]) ); } else if ((result = uv_pattern.exec(line)) !== null) { // ["vt 0.1 0.2", "0.1", "0.2"] uvs.push( parseFloat(result[1]), parseFloat(result[2]) ); } else if ((result = face_pattern1.exec(line)) !== null) { // ["f 1 2 3", "1", "2", "3", undefined] addFace( result[1], result[2], result[3], result[4] ); } else if ((result = face_pattern2.exec(line)) !== null) { // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] addFace( result[2], result[5], result[8], result[11], result[3], result[6], result[9], result[12] ); } else if ((result = face_pattern3.exec(line)) !== null) { // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] addFace( result[2], result[6], result[10], result[14], result[3], result[7], result[11], result[15], result[4], result[8], result[12], result[16] ); } else if ((result = face_pattern4.exec(line)) !== null) { // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] addFace( result[2], result[5], result[8], result[11], undefined, undefined, undefined, undefined, result[3], result[6], result[9], result[12] ); } else if (/^o /.test(line)) { geometry = { vertices: [], normals: [], uvs: [] }; object = { name: line.substring(2).trim(), geometry: geometry }; objects.push(object) } else if (/^g /.test(line)) { // group } else if (/^usemtl /.test(line)) { // material material.name = line.substring(7).trim(); } else if (/^mtllib /.test(line)) { // mtl file } else if (/^s /.test(line)) { // smooth shading } else { // unhandled } } objects.forEach(function(object) { object.geometry.vertices = new Float32Array(object.geometry.vertices); object.geometry.normals = new Float32Array(object.geometry.normals); object.geometry.uvs = new Float32Array(object.geometry.uvs); }); return objects; } })();