diff --git a/README.md b/README.md index 9b7a809..f1e7784 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ var convert = obj2gltf.convert; var options = { embedImage : false // Don't embed image in the converted glTF } -convert('model.obj', 'model.gltf', options, function() { - console.log('Converted model'); -}); +convert('model.obj', 'model.gltf', options) + .then(function() { + console.log('Converted model'); + }); ``` Using obj2gltf as a command-line tool: diff --git a/bin/obj2gltf.js b/bin/obj2gltf.js index 947fb91..19a5ea7 100644 --- a/bin/obj2gltf.js +++ b/bin/obj2gltf.js @@ -40,6 +40,10 @@ var options = { ao : ao }; -convert(objFile, outputPath, options, function() { - console.timeEnd('Total'); -}); +convert(objFile, outputPath, options) + .then(function() { + console.timeEnd('Total'); + }) + .catch(function(err) { + console.log(err); + }); diff --git a/lib/convert.js b/lib/convert.js index 73c6c7c..4eaea00 100644 --- a/lib/convert.js +++ b/lib/convert.js @@ -9,7 +9,7 @@ var defaultValue = Cesium.defaultValue; module.exports = convert; -function convert(objFile, outputPath, options, done) { +function convert(objFile, outputPath, options) { options = defaultValue(options, {}); var binary = defaultValue(options.binary, false); var embed = defaultValue(options.embed, true); @@ -37,24 +37,21 @@ function convert(objFile, outputPath, options, done) { extension = binary ? '.glb' : '.gltf'; var gltfFile = path.join(outputPath, modelName + extension); - parseObj(objFile, inputPath, function(data) { - createGltf(data, inputPath, modelName, function(gltf) { + return parseObj(objFile, inputPath) + .then(function(data) { + return createGltf(data, inputPath, modelName); + }) + .then(function(gltf) { var aoOptions = ao ? {} : undefined; var options = { - binary : binary, - embed : embed, - embedImage : embedImage, - quantize : quantize, - aoOptions : aoOptions, - createDirectory : false, - basePath : inputPath + binary: binary, + embed: embed, + embedImage: embedImage, + quantize: quantize, + aoOptions: aoOptions, + createDirectory: false, + basePath: inputPath }; - gltfPipeline.processJSONToDisk(gltf, gltfFile, options, function(error) { - if (error) { - throw error; - } - done(); - }); + return gltfPipeline.processJSONToDisk(gltf, gltfFile, options); }); - }); } diff --git a/lib/gltf.js b/lib/gltf.js index 4ce81aa..4a79303 100644 --- a/lib/gltf.js +++ b/lib/gltf.js @@ -1,14 +1,18 @@ "use strict"; +var Cesium = require('cesium'); +var Promise = require('bluebird'); var fs = require('fs-extra'); var path = require('path'); -var Cesium = require('cesium'); + var defined = Cesium.defined; var defaultValue = Cesium.defaultValue; var WebGLConstants = Cesium.WebGLConstants; +var fsWriteFile = Promise.promisify(fs.writeFile); + module.exports = createGltf; -function createGltf(data, inputPath, modelName, done) { +function createGltf(data, inputPath, modelName) { var vertexCount = data.vertexCount; var vertexArray = data.vertexArray; var positionMin = data.positionMin; @@ -328,13 +332,7 @@ function createGltf(data, inputPath, modelName, done) { if (bufferSeparate) { var bufferPath = path.join(inputPath, modelName + '.bin'); - fs.writeFile(bufferPath, buffer, function(err) { - if (err) { - throw err; - } - done(gltf); - }); - } else { - done(gltf); + return fsWriteFile(bufferPath, buffer); } + return gltf; } diff --git a/lib/image.js b/lib/image.js index b7e8177..0be148b 100644 --- a/lib/image.js +++ b/lib/image.js @@ -1,7 +1,10 @@ "use strict"; +var Promise = require('bluebird'); var fs = require('fs-extra'); var path = require('path'); +var fsReadFile = Promise.promisify(fs.readFile); + module.exports = loadImage; function getChannels(colorType) { @@ -34,31 +37,27 @@ function getUriType(extension) { } } -function loadImage(imagePath, done) { - fs.readFile(imagePath, function(error, data) { - if (error) { - throw(error); - } +function loadImage(imagePath) { + return fsReadFile(imagePath) + .then(function(data) { + var extension = path.extname(imagePath).slice(1); + var uriType = getUriType(extension); + var uri = uriType + ';base64,' + data.toString('base64'); - var extension = path.extname(imagePath).slice(1); - var uriType = getUriType(extension); - var uri = uriType + ';base64,' + data.toString('base64'); + var info = { + transparent: false, + channels: 3, + data: data, + uri: uri + }; - var info = { - transparent: false, - channels: 3, - data: data, - uri: uri - }; - - if (path.extname(imagePath) === 'png') { - // Color type is encoded in the 25th bit of the png - var colorType = data[25]; - var channels = getChannels(colorType); - info.channels = channels; - info.transparent = (channels === 4); - } - - done(info); + if (path.extname(imagePath) === 'png') { + // Color type is encoded in the 25th bit of the png + var colorType = data[25]; + var channels = getChannels(colorType); + info.channels = channels; + info.transparent = (channels === 4); + } + return info; }); } diff --git a/lib/mtl.js b/lib/mtl.js index fecdf28..941be7f 100644 --- a/lib/mtl.js +++ b/lib/mtl.js @@ -1,7 +1,10 @@ "use strict"; +var Promise = require('bluebird'); var fs = require('fs-extra'); var defined = require('cesium').defined; +var fsReadFile = Promise.promisify(fs.readFile); + module.exports = { getDefault : getDefault, parse : parse @@ -31,89 +34,85 @@ function getDefault() { return material; } -function parse(mtlPath, done) { - fs.readFile(mtlPath, 'utf8', function (error, contents) { - if (error) { - console.log('Could not read material file at ' + mtlPath + '. Using default material instead.'); - done({}); - return; - } - - var materials = {}; - var material; - - var values; - var value; - var lines = contents.split('\n'); - var length = lines.length; - for (var i = 0; i < length; ++i) { - var line = lines[i].trim(); - if (/^newmtl /i.test(line)) { - var name = line.substring(7).trim(); - material = createMaterial(); - materials[name] = material; - } else if (/^Ka /i.test(line)) { - values = line.substring(3).trim().split(' '); - material.ambientColor = [ - parseFloat(values[0]), - parseFloat(values[1]), - parseFloat(values[2]), - 1.0 - ]; - } else if (/^Ke /i.test(line)) { - values = line.substring(3).trim().split(' '); - material.emissionColor = [ - parseFloat(values[0]), - parseFloat(values[1]), - parseFloat(values[2]), - 1.0 - ]; - } else if (/^Kd /i.test(line)) { - values = line.substring(3).trim().split(' '); - material.diffuseColor = [ - parseFloat(values[0]), - parseFloat(values[1]), - parseFloat(values[2]), - 1.0 - ]; - } else if (/^Ks /i.test(line)) { - values = line.substring(3).trim().split(' '); - material.specularColor = [ - parseFloat(values[0]), - parseFloat(values[1]), - parseFloat(values[2]), - 1.0 - ]; - } else if (/^Ns /i.test(line)) { - value = line.substring(3).trim(); - material.specularShininess = parseFloat(value); - } else if (/^d /i.test(line)) { - value = line.substring(2).trim(); - material.alpha = parseFloat(value); - } else if (/^Tr /i.test(line)) { - value = line.substring(3).trim(); - material.alpha = parseFloat(value); - } else if (/^map_Ka /i.test(line)) { - material.ambientColorMap = line.substring(7).trim(); - } else if (/^map_Ke /i.test(line)) { - material.emissionColorMap = line.substring(7).trim(); - } else if (/^map_Kd /i.test(line)) { - material.diffuseColorMap = line.substring(7).trim(); - } else if (/^map_Ks /i.test(line)) { - material.specularColorMap = line.substring(7).trim(); - } else if (/^map_Ns /i.test(line)) { - material.specularShininessMap = line.substring(7).trim(); - } else if (/^map_Bump /i.test(line)) { - material.normalMap = line.substring(9).trim(); - } else if (/^map_d /i.test(line)) { - material.alphaMap = line.substring(6).trim(); +function parse(mtlPath) { + return fsReadFile(mtlPath, 'utf8') + .then(function (contents) { + var materials = {}; + var material; + var values; + var value; + var lines = contents.split('\n'); + var length = lines.length; + for (var i = 0; i < length; ++i) { + var line = lines[i].trim(); + if (/^newmtl /i.test(line)) { + var name = line.substring(7).trim(); + material = createMaterial(); + materials[name] = material; + } else if (/^Ka /i.test(line)) { + values = line.substring(3).trim().split(' '); + material.ambientColor = [ + parseFloat(values[0]), + parseFloat(values[1]), + parseFloat(values[2]), + 1.0 + ]; + } else if (/^Ke /i.test(line)) { + values = line.substring(3).trim().split(' '); + material.emissionColor = [ + parseFloat(values[0]), + parseFloat(values[1]), + parseFloat(values[2]), + 1.0 + ]; + } else if (/^Kd /i.test(line)) { + values = line.substring(3).trim().split(' '); + material.diffuseColor = [ + parseFloat(values[0]), + parseFloat(values[1]), + parseFloat(values[2]), + 1.0 + ]; + } else if (/^Ks /i.test(line)) { + values = line.substring(3).trim().split(' '); + material.specularColor = [ + parseFloat(values[0]), + parseFloat(values[1]), + parseFloat(values[2]), + 1.0 + ]; + } else if (/^Ns /i.test(line)) { + value = line.substring(3).trim(); + material.specularShininess = parseFloat(value); + } else if (/^d /i.test(line)) { + value = line.substring(2).trim(); + material.alpha = parseFloat(value); + } else if (/^Tr /i.test(line)) { + value = line.substring(3).trim(); + material.alpha = parseFloat(value); + } else if (/^map_Ka /i.test(line)) { + material.ambientColorMap = line.substring(7).trim(); + } else if (/^map_Ke /i.test(line)) { + material.emissionColorMap = line.substring(7).trim(); + } else if (/^map_Kd /i.test(line)) { + material.diffuseColorMap = line.substring(7).trim(); + } else if (/^map_Ks /i.test(line)) { + material.specularColorMap = line.substring(7).trim(); + } else if (/^map_Ns /i.test(line)) { + material.specularShininessMap = line.substring(7).trim(); + } else if (/^map_Bump /i.test(line)) { + material.normalMap = line.substring(9).trim(); + } else if (/^map_d /i.test(line)) { + material.alphaMap = line.substring(6).trim(); + } } - } - - if (defined(material.alpha)) { - material.diffuseColor[3] = material.alpha; - } - - done(materials); - }); + if (defined(material.alpha)) { + material.diffuseColor[3] = material.alpha; + } + return materials; + }) + .catch(function() { + console.log('Could not read material file at ' + mtlPath + '. Using default material instead.'); + return {}; + }); } diff --git a/lib/obj.js b/lib/obj.js index d8e11dc..6e8a362 100644 --- a/lib/obj.js +++ b/lib/obj.js @@ -1,11 +1,14 @@ "use strict"; -var async = require('async'); + +var Cesium = require('cesium'); +var Promise = require('bluebird'); var byline = require('byline'); var fs = require('fs-extra'); var path = require('path'); + var loadImage = require('./image'); var Material = require('./mtl'); -var Cesium = require('cesium'); + var Cartesian3 = Cesium.Cartesian3; var defined = Cesium.defined; @@ -13,231 +16,237 @@ module.exports = parseObj; // OBJ regex patterns are from ThreeJS (https://github.com/mrdoob/three.js/blob/master/examples/js/loaders/OBJLoader.js) -function parseObj(objFile, inputPath, done) { - getObjInfo(objFile, inputPath, function(info, materials, images) { - processObj(objFile, info, materials, images, done); - }); +function parseObj(objFile, inputPath) { + return getObjInfo(objFile, inputPath) + .then(function(result) { + var info = result.info; + var materials = result.materials; + var images = result.images; + return processObj(objFile, info, materials, images); + }); } -function processObj(objFile, info, materials, images, done) { - // A vertex is specified by indexes into each of the attribute arrays, - // but these indexes may be different. This maps the separate indexes to a single index. - var vertexCache = {}; - var vertexCount = 0; +function processObj(objFile, info, materials, images) { + return new Promise(function(resolve) { + // A vertex is specified by indexes into each of the attribute arrays, + // but these indexes may be different. This maps the separate indexes to a single index. + var vertexCache = {}; + var vertexCount = 0; - var vertexArray = []; - - var positions = []; - var normals = []; - var uvs = []; + var vertexArray = []; - var positionMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; - var positionMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; + var positions = []; + var normals = []; + var uvs = []; - var hasNormals = info.hasNormals; - var hasUVs = info.hasUVs; + var positionMin = [Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE]; + var positionMax = [-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE]; - var materialGroups = {}; // Map material to index array - var currentIndexArray; + var hasNormals = info.hasNormals; + var hasUVs = info.hasUVs; - // Switch to the material-specific index array, or create it if it doesn't exist - function useMaterial(material) { - if (!defined(materials[material])) { - useDefaultMaterial(); - } else { - currentIndexArray = materialGroups[material]; - if (!defined(currentIndexArray)) { - currentIndexArray = []; - materialGroups[material] = currentIndexArray; - } - } - } + var materialGroups = {}; // Map material to index array + var currentIndexArray; - function useDefaultMaterial() { - var defaultMaterial = 'czmDefaultMat'; - if (!defined(materials[defaultMaterial])) { - materials[defaultMaterial] = Material.getDefault(); - } - useMaterial(defaultMaterial); - } - - var materialsLength = Object.keys(materials).length; - if (materialsLength === 0) { - useDefaultMaterial(); - } - - function getOffset(a, data, components) { - var i = parseInt(a); - if (i < 0) { - // Negative vertex indexes reference the vertices immediately above it - return (data.length / components + i) * components; - } - return (i - 1) * components; - } - - function createVertex(p, u, n) { - // Positions - var pi = getOffset(p, positions, 3); - var px = positions[pi + 0]; - var py = positions[pi + 1]; - var pz = positions[pi + 2]; - - positionMin[0] = Math.min(px, positionMin[0]); - positionMin[1] = Math.min(py, positionMin[1]); - positionMin[2] = Math.min(pz, positionMin[2]); - positionMax[0] = Math.max(px, positionMax[0]); - positionMax[1] = Math.max(py, positionMax[1]); - positionMax[2] = Math.max(pz, positionMax[2]); - vertexArray.push(px, py, pz); - - // Normals - if (hasNormals) { - var ni = getOffset(n, normals, 3); - var nx = normals[ni + 0]; - var ny = normals[ni + 1]; - var nz = normals[ni + 2]; - vertexArray.push(nx, ny, nz); - } - - // UVs - if (hasUVs) { - if (defined(u)) { - var ui = getOffset(u, uvs, 2); - var ux = uvs[ui + 0]; - var uy = uvs[ui + 1]; - // Flip y so 0.0 is the bottom of the image - uy = 1.0 - uy; - vertexArray.push(ux, uy); + // Switch to the material-specific index array, or create it if it doesn't exist + function useMaterial(material) { + if (!defined(materials[material])) { + useDefaultMaterial(); } else { - // Some objects in the model may not have uvs, fill with 0's for consistency - vertexArray.push(0.0, 0.0); + currentIndexArray = materialGroups[material]; + if (!defined(currentIndexArray)) { + currentIndexArray = []; + materialGroups[material] = currentIndexArray; + } } } - } - function addVertex(v, p, u, n) { - var index = vertexCache[v]; - if (!defined(index)) { - index = vertexCount++; - vertexCache[v] = index; - createVertex(p, u, n); + function useDefaultMaterial() { + var defaultMaterial = 'czmDefaultMat'; + if (!defined(materials[defaultMaterial])) { + materials[defaultMaterial] = Material.getDefault(); + } + useMaterial(defaultMaterial); } - return index; - } + var materialsLength = Object.keys(materials).length; + if (materialsLength === 0) { + useDefaultMaterial(); + } - function addFace(v1, p1, u1, n1, v2, p2, u2, n2, v3, p3, u3, n3, v4, p4, u4, n4) { - var index1 = addVertex(v1, p1, u1, n1); - var index2 = addVertex(v2, p2, u2, n2); - var index3 = addVertex(v3, p3, u3, n3); + function getOffset(a, data, components) { + var i = parseInt(a); + if (i < 0) { + // Negative vertex indexes reference the vertices immediately above it + return (data.length / components + i) * components; + } + return (i - 1) * components; + } - currentIndexArray.push(index1); - currentIndexArray.push(index2); - currentIndexArray.push(index3); + function createVertex(p, u, n) { + // Positions + var pi = getOffset(p, positions, 3); + var px = positions[pi + 0]; + var py = positions[pi + 1]; + var pz = positions[pi + 2]; + + positionMin[0] = Math.min(px, positionMin[0]); + positionMin[1] = Math.min(py, positionMin[1]); + positionMin[2] = Math.min(pz, positionMin[2]); + positionMax[0] = Math.max(px, positionMax[0]); + positionMax[1] = Math.max(py, positionMax[1]); + positionMax[2] = Math.max(pz, positionMax[2]); + vertexArray.push(px, py, pz); + + // Normals + if (hasNormals) { + var ni = getOffset(n, normals, 3); + var nx = normals[ni + 0]; + var ny = normals[ni + 1]; + var nz = normals[ni + 2]; + vertexArray.push(nx, ny, nz); + } + + // UVs + if (hasUVs) { + if (defined(u)) { + var ui = getOffset(u, uvs, 2); + var ux = uvs[ui + 0]; + var uy = uvs[ui + 1]; + // Flip y so 0.0 is the bottom of the image + uy = 1.0 - uy; + vertexArray.push(ux, uy); + } else { + // Some objects in the model may not have uvs, fill with 0's for consistency + vertexArray.push(0.0, 0.0); + } + } + } + + function addVertex(v, p, u, n) { + var index = vertexCache[v]; + if (!defined(index)) { + index = vertexCount++; + vertexCache[v] = index; + createVertex(p, u, n); + } + + return index; + } + + function addFace(v1, p1, u1, n1, v2, p2, u2, n2, v3, p3, u3, n3, v4, p4, u4, n4) { + var index1 = addVertex(v1, p1, u1, n1); + var index2 = addVertex(v2, p2, u2, n2); + var index3 = addVertex(v3, p3, u3, n3); - // Triangulate if the face is a quad - if (defined(v4)) { - var index4 = addVertex(v4, p4, u4, n4); currentIndexArray.push(index1); + currentIndexArray.push(index2); currentIndexArray.push(index3); - currentIndexArray.push(index4); + + // Triangulate if the face is a quad + if (defined(v4)) { + var index4 = addVertex(v4, p4, u4, n4); + currentIndexArray.push(index1); + currentIndexArray.push(index3); + currentIndexArray.push(index4); + } } - } - // v float float float - var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + // v float float float + var vertexPattern = /v( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; - // vn float float float - var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + // vn float float float + var normalPattern = /vn( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; - // vt float float - var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; + // vt float float + var uvPattern = /vt( +[\d|\.|\+|\-|e|E]+)( +[\d|\.|\+|\-|e|E]+)/; - // f vertex vertex vertex ... - var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/; + // f vertex vertex vertex ... + var facePattern1 = /f( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)\/?( +-?\d+)?\/?/; - // f vertex/uv vertex/uv vertex/uv ... - var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)?/; + // f vertex/uv vertex/uv vertex/uv ... + var facePattern2 = /f( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)( +(-?\d+)\/(-?\d+)\/?)?/; - // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... - var facePattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; + // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... + var facePattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; - // f vertex//normal vertex//normal vertex//normal ... - var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/; + // f vertex//normal vertex//normal vertex//normal ... + var facePattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/; - var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'})); - stream.on('data', function(line) { - line = line.trim(); - var result; - if ((line.length === 0) || (line.charAt(0) === '#')) { - // Don't process empty lines or comments - } else if ((result = vertexPattern.exec(line)) !== null) { - positions.push( - parseFloat(result[1]), - parseFloat(result[2]), - parseFloat(result[3]) - ); - } else if ((result = normalPattern.exec(line) ) !== null) { - var nx = parseFloat(result[1]); - var ny = parseFloat(result[2]); - var nz = parseFloat(result[3]); - var normal = Cartesian3.normalize(new Cartesian3(nx, ny, nz), new Cartesian3()); - normals.push(normal.x, normal.y, normal.z); - } else if ((result = uvPattern.exec(line)) !== null) { - uvs.push( - parseFloat(result[1]), - parseFloat(result[2]) - ); - } else if ((result = facePattern1.exec(line)) !== null) { - addFace( - result[1], result[1], undefined, undefined, - result[2], result[2], undefined, undefined, - result[3], result[3], undefined, undefined, - result[4], result[4], undefined, undefined - ); - } else if ((result = facePattern2.exec(line)) !== null) { - addFace( - result[1], result[2], result[3], undefined, - result[4], result[5], result[6], undefined, - result[7], result[8], result[9], undefined, - result[10], result[11], result[12], undefined - ); - } else if ((result = facePattern3.exec(line)) !== null) { - addFace( - result[1], result[2], result[3], result[4], - result[5], result[6], result[7], result[8], - result[9], result[10], result[11], result[12], - result[13], result[14], result[15], result[16] - ); - } else if ((result = facePattern4.exec(line)) !== null) { - addFace( - result[1], result[2], undefined, result[3], - result[4], result[5], undefined, result[6], - result[7], result[8], undefined, result[9], - result[10], result[11], undefined, result[12] - ); - } else if (/^usemtl /.test(line)) { - var materialName = line.substring(7).trim(); - useMaterial(materialName); - } - }); + var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'})); + stream.on('data', function (line) { + line = line.trim(); + var result; + if ((line.length === 0) || (line.charAt(0) === '#')) { + // Don't process empty lines or comments + } else if ((result = vertexPattern.exec(line)) !== null) { + positions.push( + parseFloat(result[1]), + parseFloat(result[2]), + parseFloat(result[3]) + ); + } else if ((result = normalPattern.exec(line) ) !== null) { + var nx = parseFloat(result[1]); + var ny = parseFloat(result[2]); + var nz = parseFloat(result[3]); + var normal = Cartesian3.normalize(new Cartesian3(nx, ny, nz), new Cartesian3()); + normals.push(normal.x, normal.y, normal.z); + } else if ((result = uvPattern.exec(line)) !== null) { + uvs.push( + parseFloat(result[1]), + parseFloat(result[2]) + ); + } else if ((result = facePattern1.exec(line)) !== null) { + addFace( + result[1], result[1], undefined, undefined, + result[2], result[2], undefined, undefined, + result[3], result[3], undefined, undefined, + result[4], result[4], undefined, undefined + ); + } else if ((result = facePattern2.exec(line)) !== null) { + addFace( + result[1], result[2], result[3], undefined, + result[4], result[5], result[6], undefined, + result[7], result[8], result[9], undefined, + result[10], result[11], result[12], undefined + ); + } else if ((result = facePattern3.exec(line)) !== null) { + addFace( + result[1], result[2], result[3], result[4], + result[5], result[6], result[7], result[8], + result[9], result[10], result[11], result[12], + result[13], result[14], result[15], result[16] + ); + } else if ((result = facePattern4.exec(line)) !== null) { + addFace( + result[1], result[2], undefined, result[3], + result[4], result[5], undefined, result[6], + result[7], result[8], undefined, result[9], + result[10], result[11], undefined, result[12] + ); + } else if (/^usemtl /.test(line)) { + var materialName = line.substring(7).trim(); + useMaterial(materialName); + } + }); - stream.on('end', function() { - done({ - vertexCount : vertexCount, - vertexArray : vertexArray, - positionMin : positionMin, - positionMax : positionMax, - hasUVs : hasUVs, - hasNormals : hasNormals, - materialGroups : materialGroups, - materials : materials, - images : images + stream.on('end', function () { + resolve({ + vertexCount: vertexCount, + vertexArray: vertexArray, + positionMin: positionMin, + positionMax: positionMax, + hasUVs: hasUVs, + hasNormals: hasNormals, + materialGroups: materialGroups, + materials: materials, + images: images + }); }); }); } -function getImages(inputPath, materials, done) { +function getImages(inputPath, materials) { // Collect all the image files from the materials var images = []; for (var name in materials) { @@ -259,84 +268,100 @@ function getImages(inputPath, materials, done) { } // Load the image files + var promises = []; var imagesInfo = {}; - async.each(images, function (image, callback) { - var imagePath = image; + var imagesLength = images.length; + for (var i = 0; i < imagesLength; i++) { + var imagePath = images[i]; if (!path.isAbsolute(imagePath)) { - imagePath = path.join(inputPath, image); + imagePath = path.join(inputPath, imagePath); } - loadImage(imagePath, function(info) { - imagesInfo[image] = info; - callback(); + promises.push(loadImage(imagePath)); + } + return Promise.all(promises) + .then(function(imageInfoArray) { + var imageInfoArrayLength = imageInfoArray.length; + for (var j = 0; j < imageInfoArrayLength; j++) { + var image = images[j]; + var imageInfo = imageInfoArray[j]; + imagesInfo[image] = imageInfo; + } + return imagesInfo; }); - }, function (error) { - if (error) { - throw error; - } - done(imagesInfo); - }); } -function getMaterials(mtlPath, hasMaterialGroups, done) { +function getMaterials(mtlPath, hasMaterialGroups) { if (!hasMaterialGroups) { - done({}); return; } if (defined(mtlPath)) { - Material.parse(mtlPath, function(materials) { - done(materials); - }); - } else { - done({}); + return Material.parse(mtlPath); } } -function getObjInfo(objFile, inputPath, done) { +function getObjInfo(objFile, inputPath) { var mtlPath; + var materials; + var info; var hasMaterialGroups = false; var hasPositions = false; var hasNormals = false; var hasUVs = false; - - var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'})); - stream.on('data', function(line) { - if (!defined(mtlPath)) { - var mtllibMatches = line.match(/^mtllib.*/gm); - if (mtllibMatches !== null) { - var mtlFile = mtllibMatches[0].substring(7).trim(); - mtlPath = mtlFile; - if (!path.isAbsolute(mtlPath)) { - mtlPath = path.join(inputPath, mtlFile); + return new Promise(function(resolve, reject) { + var stream = byline(fs.createReadStream(objFile, {encoding: 'utf8'})); + stream.on('data', function (line) { + if (!defined(mtlPath)) { + var mtllibMatches = line.match(/^mtllib.*/gm); + if (mtllibMatches !== null) { + var mtlFile = mtllibMatches[0].substring(7).trim(); + mtlPath = mtlFile; + if (!path.isAbsolute(mtlPath)) { + mtlPath = path.join(inputPath, mtlFile); + } } } - } - if (!hasMaterialGroups) { - hasMaterialGroups = /^usemtl/gm.test(line); - } - if (!hasPositions) { - hasPositions = /^v\s/gm.test(line); - } - if (!hasNormals) { - hasNormals = /^vn/gm.test(line); - } - if (!hasUVs) { - hasUVs = /^vt/gm.test(line); - } - }); - - stream.on('end', function() { - if (!hasPositions) { - throw new Error('Could not process OBJ file, no positions.'); - } - var info = { - hasNormals : hasNormals, - hasUVs : hasUVs - }; - getMaterials(mtlPath, hasMaterialGroups, function(materials) { - getImages(inputPath, materials, function(images) { - done(info, materials, images); - }); + if (!hasMaterialGroups) { + hasMaterialGroups = /^usemtl/gm.test(line); + } + if (!hasPositions) { + hasPositions = /^v\s/gm.test(line); + } + if (!hasNormals) { + hasNormals = /^vn/gm.test(line); + } + if (!hasUVs) { + hasUVs = /^vt/gm.test(line); + } + }); + + stream.on('error', function(err) { + reject(err); + }); + + stream.on('end', function () { + if (!hasPositions) { + reject(new Error('Could not process OBJ file, no positions.')); + } + info = { + hasNormals: hasNormals, + hasUVs: hasUVs + }; + resolve(); + }); + }) + .then(function() { + return getMaterials(mtlPath, hasMaterialGroups); + }) + .then(function(returnedMaterials) { + materials = returnedMaterials; + return getImages(inputPath, materials); + }) + .then(function(images) { + return { + info : info, + materials : materials, + images : images + }; }); - }); } diff --git a/package.json b/package.json index 709a6b7..9bef28a 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "async": "2.0.0-rc.6", + "bluebird": "^3.4.1", "byline": "4.2.1", "cesium": "1.23.0", "fs-extra": "0.30.0", diff --git a/specs/lib/convertSpec.js b/specs/lib/convertSpec.js index 66bca77..2c81be5 100644 --- a/specs/lib/convertSpec.js +++ b/specs/lib/convertSpec.js @@ -1,4 +1,6 @@ 'use strict'; +var Promise = require('bluebird'); + var gltfPipeline = require('gltf-pipeline').gltfPipeline; var path = require('path'); var convert = require('../../lib/convert'); @@ -8,14 +10,14 @@ var gltfFile = './specs/data/BoxTextured/BoxTextured.gltf'; describe('convert', function() { it('converts an obj to gltf', function(done) { - var spy = spyOn(gltfPipeline, 'processJSONToDisk').and.callFake(function(gltf, gltfFile, options, callback) { - callback(); - }); - convert(objFile, gltfFile, {}, function() { - var args = spy.calls.first().args; - expect(args[0]).toBeDefined(); - expect(path.normalize(args[1])).toEqual(path.normalize(gltfFile)); - done(); + var spy = spyOn(gltfPipeline, 'processJSONToDisk').and.callFake(function(gltf, outputPath, options, callback) { + return; }); + expect(convert(objFile, gltfFile, {}) + .then(function() { + var args = spy.calls.first().args; + expect(args[0]).toBeDefined(); + expect(path.normalize(args[1])).toEqual(path.normalize(gltfFile)); + }), done).toResolve(); }); });