mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2025-02-07 07:22:51 -05:00
Merge pull request #21 from AnalyticalGraphicsInc/promises-everywhere
Convert callbacks to promises
This commit is contained in:
commit
60ba99d4ec
@ -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:
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
18
lib/gltf.js
18
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;
|
||||
}
|
||||
|
47
lib/image.js
47
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;
|
||||
});
|
||||
}
|
||||
|
167
lib/mtl.js
167
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 {};
|
||||
});
|
||||
}
|
||||
|
529
lib/obj.js
529
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
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user