2017-03-13 15:28:51 -04:00
|
|
|
'use strict';
|
|
|
|
var Cesium = require('cesium');
|
|
|
|
var fsExtra = require('fs-extra');
|
2016-08-25 13:48:34 -04:00
|
|
|
var GltfPipeline = require('gltf-pipeline').Pipeline;
|
2017-04-20 10:07:01 -04:00
|
|
|
var os = require('os');
|
2017-03-13 15:28:51 -04:00
|
|
|
var path = require('path');
|
|
|
|
var Promise = require('bluebird');
|
2017-04-20 10:07:01 -04:00
|
|
|
var uuid = require('uuid');
|
2017-04-12 16:55:03 -04:00
|
|
|
var createGltf = require('./createGltf');
|
|
|
|
var loadObj = require('./loadObj');
|
2017-03-17 15:44:01 -04:00
|
|
|
var writeUris = require('./writeUris');
|
2017-03-13 15:28:51 -04:00
|
|
|
|
|
|
|
var fsExtraOutputJson = Promise.promisify(fsExtra.outputJson);
|
|
|
|
|
2016-06-09 13:33:08 -04:00
|
|
|
var defaultValue = Cesium.defaultValue;
|
2017-03-13 15:28:51 -04:00
|
|
|
var defined = Cesium.defined;
|
|
|
|
var DeveloperError = Cesium.DeveloperError;
|
2017-04-27 13:35:47 -04:00
|
|
|
var RuntimeError = Cesium.RuntimeError;
|
2016-06-09 13:33:08 -04:00
|
|
|
|
2017-04-12 16:55:03 -04:00
|
|
|
module.exports = obj2gltf;
|
2016-06-09 13:33:08 -04:00
|
|
|
|
2017-03-13 15:28:51 -04:00
|
|
|
/**
|
|
|
|
* Converts an obj file to a glTF file.
|
|
|
|
*
|
|
|
|
* @param {String} objPath Path to the obj file.
|
|
|
|
* @param {String} gltfPath Path of the converted glTF file.
|
|
|
|
* @param {Object} [options] An object with the following properties:
|
|
|
|
* @param {Boolean} [options.binary=false] Save as binary glTF.
|
2017-03-14 16:42:42 -04:00
|
|
|
* @param {Boolean} [options.separate=false] Writes out separate geometry data files, shader files, and textures instead of embedding them in the glTF.
|
2017-03-13 15:28:51 -04:00
|
|
|
* @param {Boolean} [options.separateTextures=false] Write out separate textures only.
|
|
|
|
* @param {Boolean} [options.compress=false] Quantize positions, compress texture coordinates, and oct-encode normals.
|
2017-04-10 17:57:56 -04:00
|
|
|
* @param {Boolean} [options.optimize=false] Optimize the glTF for size and runtime performance.
|
2017-03-13 15:28:51 -04:00
|
|
|
* @param {Boolean} [options.optimizeForCesium=false] Optimize the glTF for Cesium by using the sun as a default light source.
|
|
|
|
* @param {Boolean} [options.generateNormals=false] Generate normals if they are missing.
|
|
|
|
* @param {Boolean} [options.ao=false] Apply ambient occlusion to the converted model.
|
|
|
|
* @param {Boolean} [options.textureCompressionOptions] Options sent to the compressTextures stage of gltf-pipeline.
|
2017-04-20 10:23:00 -04:00
|
|
|
* @param {Boolean} [options.bypassPipeline=false] Bypass the gltf-pipeline for debugging purposes. This option overrides many of the options above.
|
2017-04-10 17:57:56 -04:00
|
|
|
* @param {Boolean} [options.checkTransparency=false] Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
2017-04-04 16:45:21 -04:00
|
|
|
* @param {Boolean} [options.secure=false] Prevent the converter from reading image or mtl files outside of the input obj directory.
|
2017-04-20 14:41:39 -04:00
|
|
|
* @param {String} [options.inputUpAxis='Y'] Up axis of the obj. Choices are 'X', 'Y', and 'Z'.
|
2017-04-20 15:09:03 -04:00
|
|
|
* @param {String} [options.outputUpAxis='Y'] Up axis of the converted glTF. Choices are 'X', 'Y', and 'Z'.
|
2017-05-03 17:59:24 -04:00
|
|
|
* @param {Boolean} [options.packOcclusion=false] Pack the occlusion texture in the red channel of metallic-roughness texture.
|
2017-05-04 15:39:01 -04:00
|
|
|
* @param {Boolean} [options.metallicRoughness=false] The values in the mtl file are already metallic-roughness PBR values and no conversion step should be applied. Metallic is stored in the Ks and map_Ks slots and roughness is stored in the Ns and map_Ns slots.
|
|
|
|
* @param {Boolean} [options.specularGlossiness=false] The values in the mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the KHR_materials_pbrSpecularGlossiness extension.
|
|
|
|
* @param {Boolean} [options.materialsCommon=false] The glTF will be saved with the KHR_materials_common extension.
|
2017-04-04 17:21:10 -04:00
|
|
|
* @param {Logger} [options.logger] A callback function for handling logged messages. Defaults to console.log.
|
2017-03-13 15:28:51 -04:00
|
|
|
*/
|
2017-04-12 16:55:03 -04:00
|
|
|
function obj2gltf(objPath, gltfPath, options) {
|
|
|
|
var defaults = obj2gltf.defaults;
|
2017-04-10 17:57:56 -04:00
|
|
|
|
|
|
|
options = defaultValue(options, {});
|
|
|
|
var binary = defaultValue(options.binary, defaults.binary);
|
|
|
|
var separate = defaultValue(options.separate, defaults.separate);
|
|
|
|
var separateTextures = defaultValue(options.separateTextures, defaults.separateTextures) || separate;
|
|
|
|
var compress = defaultValue(options.compress, defaults.compress);
|
|
|
|
var optimize = defaultValue(options.optimize, defaults.optimize);
|
|
|
|
var optimizeForCesium = defaultValue(options.optimizeForCesium, defaults.optimizeForCesium);
|
|
|
|
var generateNormals = defaultValue(options.generateNormals, defaults.generateNormals);
|
|
|
|
var ao = defaultValue(options.ao, defaults.ao);
|
|
|
|
var textureCompressionOptions = options.textureCompressionOptions;
|
|
|
|
var bypassPipeline = defaultValue(options.bypassPipeline, defaults.bypassPipeline);
|
|
|
|
var checkTransparency = defaultValue(options.checkTransparency, defaults.checkTransparency);
|
|
|
|
var secure = defaultValue(options.secure, defaults.secure);
|
2017-04-20 14:41:39 -04:00
|
|
|
var inputUpAxis = defaultValue(options.inputUpAxis, defaults.inputUpAxis);
|
|
|
|
var outputUpAxis = defaultValue(options.outputUpAxis, defaults.outputUpAxis);
|
2017-05-03 17:59:24 -04:00
|
|
|
var packOcclusion = defaultValue(options.packOcclusion, defaults.packOcclusion);
|
2017-05-04 15:39:01 -04:00
|
|
|
var metallicRoughness = defaultValue(options.metallicRoughness, defaults.metallicRoughness);
|
|
|
|
var specularGlossiness = defaultValue(options.specularGlossiness, defaults.specularGlossiness);
|
|
|
|
var materialsCommon = defaultValue(options.materialsCommon, defaults.materialsCommon);
|
2017-04-10 17:57:56 -04:00
|
|
|
var logger = defaultValue(options.logger, defaults.logger);
|
|
|
|
|
|
|
|
options.separate = separate;
|
|
|
|
options.separateTextures = separateTextures;
|
|
|
|
options.checkTransparency = checkTransparency;
|
|
|
|
options.secure = secure;
|
2017-04-20 14:41:39 -04:00
|
|
|
options.inputUpAxis = inputUpAxis;
|
|
|
|
options.outputUpAxis = outputUpAxis;
|
2017-05-03 17:59:24 -04:00
|
|
|
options.packOcclusion = packOcclusion;
|
2017-05-04 15:39:01 -04:00
|
|
|
options.metallicRoughness = metallicRoughness;
|
|
|
|
options.specularGlossiness = specularGlossiness;
|
|
|
|
options.materialsCommon = materialsCommon;
|
2017-04-10 17:57:56 -04:00
|
|
|
options.logger = logger;
|
|
|
|
|
|
|
|
if (!defined(objPath)) {
|
|
|
|
throw new DeveloperError('objPath is required');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!defined(gltfPath)) {
|
|
|
|
throw new DeveloperError('gltfPath is required');
|
|
|
|
}
|
|
|
|
|
|
|
|
var extension = path.extname(gltfPath).toLowerCase();
|
|
|
|
var modelName = path.basename(gltfPath, path.extname(gltfPath));
|
|
|
|
if (extension === '.glb') {
|
|
|
|
binary = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (binary && bypassPipeline) {
|
2017-04-27 13:35:47 -04:00
|
|
|
return Promise.reject(new RuntimeError('--bypassPipeline does not convert to binary glTF'));
|
2017-04-10 17:57:56 -04:00
|
|
|
}
|
|
|
|
|
2017-05-04 15:39:01 -04:00
|
|
|
if (metallicRoughness + specularGlossiness + materialsCommon > 1) {
|
2017-05-04 17:58:13 -04:00
|
|
|
return Promise.reject(new RuntimeError('Only one material type may be set from [--metallicRoughness, --specularGlossiness, --materialsCommon].'));
|
2017-05-03 17:59:24 -04:00
|
|
|
}
|
|
|
|
|
2017-04-10 17:57:56 -04:00
|
|
|
gltfPath = path.join(path.dirname(gltfPath), modelName + extension);
|
2017-04-25 13:02:14 -04:00
|
|
|
var resourcesDirectory = options.bypassPipeline ? path.dirname(gltfPath) : obj2gltf._getTempDirectory();
|
2017-04-10 17:57:56 -04:00
|
|
|
|
|
|
|
var aoOptions = ao ? {} : undefined;
|
|
|
|
|
|
|
|
var pipelineOptions = {
|
|
|
|
createDirectory : false,
|
2017-04-20 10:07:01 -04:00
|
|
|
basePath : resourcesDirectory,
|
2017-04-10 17:57:56 -04:00
|
|
|
binary : binary,
|
|
|
|
embed : !separate,
|
|
|
|
embedImage : !separateTextures,
|
|
|
|
quantize : compress,
|
|
|
|
compressTextureCoordinates : compress,
|
|
|
|
encodeNormals : compress,
|
|
|
|
preserve : !optimize,
|
|
|
|
optimizeForCesium : optimizeForCesium,
|
|
|
|
smoothNormals : generateNormals,
|
|
|
|
aoOptions : aoOptions,
|
|
|
|
textureCompressionOptions : textureCompressionOptions
|
|
|
|
};
|
|
|
|
|
|
|
|
return loadObj(objPath, options)
|
|
|
|
.then(function(objData) {
|
2017-04-18 11:56:08 -04:00
|
|
|
return createGltf(objData, options);
|
2017-04-10 17:57:56 -04:00
|
|
|
})
|
|
|
|
.then(function(gltf) {
|
2017-04-20 10:07:01 -04:00
|
|
|
return writeUris(gltf, gltfPath, resourcesDirectory, options);
|
2017-04-10 17:57:56 -04:00
|
|
|
})
|
|
|
|
.then(function(gltf) {
|
2017-03-13 15:28:51 -04:00
|
|
|
if (bypassPipeline) {
|
2017-04-12 16:55:03 -04:00
|
|
|
return obj2gltf._outputJson(gltfPath, gltf);
|
2017-04-10 17:57:56 -04:00
|
|
|
} else {
|
|
|
|
return GltfPipeline.processJSONToDisk(gltf, gltfPath, pipelineOptions);
|
2017-03-13 15:28:51 -04:00
|
|
|
}
|
2017-04-14 12:00:43 -04:00
|
|
|
})
|
2017-04-25 13:02:14 -04:00
|
|
|
.finally(function() {
|
2017-04-20 10:07:01 -04:00
|
|
|
return cleanup(resourcesDirectory, options);
|
2017-04-10 17:57:56 -04:00
|
|
|
});
|
2017-03-13 15:28:51 -04:00
|
|
|
}
|
|
|
|
|
2017-04-20 10:07:01 -04:00
|
|
|
function cleanup(resourcesDirectory, options) {
|
2017-04-14 12:00:43 -04:00
|
|
|
if (!options.bypassPipeline && options.separate) {
|
2017-04-25 13:02:14 -04:00
|
|
|
fsExtra.remove(resourcesDirectory, function () {
|
|
|
|
// Don't fail simply because we couldn't
|
|
|
|
// clean up the temporary files.
|
2017-04-10 17:57:56 -04:00
|
|
|
});
|
2017-04-14 12:00:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-10 17:57:56 -04:00
|
|
|
/**
|
2017-04-12 16:55:03 -04:00
|
|
|
* Default values that will be used when calling obj2gltf(options) unless specified in the options object.
|
2017-04-10 17:57:56 -04:00
|
|
|
*/
|
2017-04-12 16:55:03 -04:00
|
|
|
obj2gltf.defaults = {
|
2017-04-10 17:57:56 -04:00
|
|
|
/**
|
|
|
|
* Gets or sets whether the model will be saved as binary glTF.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
binary: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether to write out separate geometry/animation data files,
|
|
|
|
* shader files, and textures instead of embedding them in the glTF.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
separate: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether to write out separate textures only.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
separateTextures: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether to compress attribute data. This includes quantizing positions, compressing texture coordinates, and oct-encoding normals.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
compress: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the model is optimized for size and runtime performance.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
optimize: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the model is optimized for Cesium by using the sun as a default light source.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
optimizeForCesium: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether normals will be generated for the model if they are missing.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
generateNormals: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the model will have ambient occlusion applied.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
ao: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the converter will bypass the gltf-pipeline for debugging purposes.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
bypassPipeline: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the converter will do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
checkTransparency: false,
|
|
|
|
/**
|
|
|
|
* Gets or sets whether the source model can reference paths outside of its directory.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
secure: false,
|
2017-04-20 14:41:39 -04:00
|
|
|
/**
|
|
|
|
* Gets or sets the up axis of the obj.
|
|
|
|
* @type String
|
|
|
|
* @default 'Y'
|
|
|
|
*/
|
|
|
|
inputUpAxis: 'Y',
|
|
|
|
/**
|
|
|
|
* Gets or sets the up axis of the converted glTF.
|
|
|
|
* @type String
|
|
|
|
* @default 'Y'
|
|
|
|
*/
|
|
|
|
outputUpAxis: 'Y',
|
2017-05-03 17:59:24 -04:00
|
|
|
/**
|
|
|
|
* Gets or sets whether to pack the occlusion texture in the red channel of the metallic-roughness texture.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
packOcclusion: false,
|
|
|
|
/**
|
|
|
|
* The values in the mtl file are already metallic-roughness PBR values and no conversion step should be applied. Metallic is stored in the Ks and map_Ks slots and roughness is stored in the Ns and map_Ns slots.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
2017-05-04 15:39:01 -04:00
|
|
|
metallicRoughness: false,
|
2017-05-03 17:59:24 -04:00
|
|
|
/**
|
|
|
|
* The values in the mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the KHR_materials_pbrSpecularGlossiness extension.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
2017-05-04 15:39:01 -04:00
|
|
|
specularGlossiness: false,
|
|
|
|
/**
|
|
|
|
* The values in the mtl file are already specular-glossiness PBR values and no conversion step should be applied. Specular is stored in the Ks and map_Ks slots and glossiness is stored in the Ns and map_Ns slots. The glTF will be saved with the KHR_materials_pbrSpecularGlossiness extension.
|
|
|
|
* @type Boolean
|
|
|
|
* @default false
|
|
|
|
*/
|
|
|
|
materialsCommon: false,
|
2017-05-03 17:59:24 -04:00
|
|
|
|
2017-04-10 17:57:56 -04:00
|
|
|
/**
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
logger: function(message) {
|
|
|
|
console.log(message);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-03-14 16:42:42 -04:00
|
|
|
/**
|
|
|
|
* Exposed for testing
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
2017-04-12 16:55:03 -04:00
|
|
|
obj2gltf._outputJson = fsExtraOutputJson;
|
2017-04-10 17:57:56 -04:00
|
|
|
|
2017-04-25 13:02:14 -04:00
|
|
|
/**
|
|
|
|
* Exposed for testing
|
|
|
|
*
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
obj2gltf._getTempDirectory = function () {
|
|
|
|
return path.join(os.tmpdir(), uuid());
|
|
|
|
};
|
|
|
|
|
2017-04-10 17:57:56 -04:00
|
|
|
/**
|
|
|
|
* A callback function that logs messages.
|
|
|
|
* @callback Logger
|
|
|
|
*
|
|
|
|
* @param {String} message The message to log.
|
|
|
|
*/
|
|
|
|
|