obj2gltf/lib/writeUris.js

211 lines
6.6 KiB
JavaScript
Raw Normal View History

'use strict';
2017-04-10 17:57:56 -04:00
var Cesium = require('cesium');
var fsExtra = require('fs-extra');
var mime = require('mime');
var path = require('path');
var PNG = require('pngjs').PNG;
var Promise = require('bluebird');
2017-07-19 17:56:24 -04:00
var getBufferPadded = require('./getBufferPadded');
2017-05-04 17:58:13 -04:00
var defined = Cesium.defined;
2017-04-10 17:57:56 -04:00
var RuntimeError = Cesium.RuntimeError;
module.exports = writeUris;
/**
* Write glTF resources as embedded data uris or external files.
*
* @param {Object} gltf The glTF asset.
* @param {String} gltfPath Path where the glTF will be saved.
2017-04-10 17:57:56 -04:00
* @param {Object} options An object with the following properties:
* @param {Boolean} options.separate Writes out separate buffers.
* @param {Boolean} options.separateTextures Write out separate textures only.
* @returns {Promise} A promise that resolves to the glTF asset.
*
* @private
*/
function writeUris(gltf, gltfPath, options) {
return encodeImages(gltf)
.then(function() {
var separate = options.separate;
var separateTextures = options.separateTextures;
var buffer = gltf.buffers[0];
var bufferByteLength = buffer.extras._obj2gltf.source.length;
var texturesByteLength = 0;
var images = gltf.images;
var imagesLength = images.length;
for (var i = 0; i < imagesLength; ++i) {
texturesByteLength += images[i].extras._obj2gltf.source.length;
}
// Buffers larger than ~192MB cannot be base64 encoded due to a NodeJS limitation. Source: https://github.com/nodejs/node/issues/4266
var exceedsMaximum = (texturesByteLength + bufferByteLength > 201326580);
if (exceedsMaximum && !separate) {
return Promise.reject(new RuntimeError('Buffers and textures are too large to encode in the glTF. Use the --separate flag instead.'));
}
var name = path.basename(gltfPath, path.extname(gltfPath));
var promises = [];
if (separateTextures) {
promises.push(writeSeparateTextures(gltf, gltfPath));
} else {
writeEmbeddedTextures(gltf);
}
if (separate) {
promises.push(writeSeparateBuffer(gltf, gltfPath, name));
} else {
writeEmbeddedBuffer(gltf);
}
return Promise.all(promises)
.then(function() {
deleteExtras(gltf);
cleanup(gltf);
return gltf;
});
});
}
function encodePng(image) {
// Constants defined by pngjs
var rgbColorType = 2;
var rgbaColorType = 6;
var png = new PNG({
width : image.width,
height : image.height,
colorType : image.transparent ? rgbaColorType : rgbColorType,
inputColorType : rgbaColorType,
inputHasAlpha : true
});
png.data = image.decoded;
return new Promise(function(resolve, reject) {
var chunks = [];
var stream = png.pack();
stream.on('data', function(chunk) {
chunks.push(chunk);
});
stream.on('end', function() {
resolve(Buffer.concat(chunks));
});
stream.on('error', reject);
});
}
2017-04-20 10:07:01 -04:00
function encodeImage(image) {
var imageExtras = image.extras._obj2gltf;
if (!defined(imageExtras.source) && defined(imageExtras.decoded) && imageExtras.extension === '.png') {
return encodePng(imageExtras)
.then(function(encoded) {
imageExtras.source = encoded;
});
}
}
function encodeImages(gltf) {
// Dynamically generated metallicRoughnessOcclusion and specularGlossiness
// textures need to be encoded to png's prior to being saved.
var encodePromises = [];
var images = gltf.images;
var length = images.length;
for (var i = 0; i < length; ++i) {
encodePromises.push(encodeImage(images[i]));
}
return Promise.all(encodePromises);
}
function deleteExtras(gltf) {
2017-04-18 11:56:08 -04:00
var buffer = gltf.buffers[0];
delete buffer.extras;
var images = gltf.images;
2017-04-18 11:56:08 -04:00
var imagesLength = images.length;
for (var i = 0; i < imagesLength; ++i) {
delete images[i].extras;
}
var materials = gltf.materials;
var materialsLength = materials.length;
for (var j = 0; j < materialsLength; ++j) {
delete materials[j].extras;
}
}
2017-05-04 17:58:13 -04:00
function removeEmpty(json) {
Object.keys(json).forEach(function(key) {
if (!defined(json[key]) || (Array.isArray(json[key]) && json[key].length === 0)) {
delete json[key]; // Delete values that are undefined or []
} else if (typeof json[key] === 'object') {
removeEmpty(json[key]);
}
2017-05-04 17:58:13 -04:00
});
}
function cleanup(gltf) {
removeEmpty(gltf);
}
function writeSeparateBuffer(gltf, gltfPath, name) {
2017-04-18 11:56:08 -04:00
var buffer = gltf.buffers[0];
var source = buffer.extras._obj2gltf.source;
2017-04-20 10:07:01 -04:00
var bufferUri = name + '.bin';
buffer.uri = bufferUri;
var bufferPath = path.join(path.dirname(gltfPath), bufferUri);
return fsExtra.outputFile(bufferPath, source);
}
function writeSeparateTextures(gltf, gltfPath) {
var images = gltf.images;
2017-04-18 11:56:08 -04:00
return Promise.map(images, function(image) {
2017-04-10 17:57:56 -04:00
var extras = image.extras._obj2gltf;
var imageUri = image.name + extras.extension;
image.uri = imageUri;
var imagePath = path.join(path.dirname(gltfPath), imageUri);
return fsExtra.outputFile(imagePath, extras.source);
2017-04-10 17:57:56 -04:00
}, {concurrency : 10});
}
function writeEmbeddedBuffer(gltf) {
2017-04-18 11:56:08 -04:00
var buffer = gltf.buffers[0];
var source = buffer.extras._obj2gltf.source;
buffer.uri = 'data:application/octet-stream;base64,' + source.toString('base64');
}
function writeEmbeddedTextures(gltf) {
2017-07-19 17:56:24 -04:00
var buffer = gltf.buffers[0];
var bufferExtras = buffer.extras._obj2gltf;
var bufferSource = bufferExtras.source;
var images = gltf.images;
2017-04-18 11:56:08 -04:00
var imagesLength = images.length;
2017-07-19 17:56:24 -04:00
var sources = [bufferSource];
var byteOffset = bufferSource.length;
2017-04-18 11:56:08 -04:00
for (var i = 0; i < imagesLength; ++i) {
var image = images[i];
var extras = image.extras._obj2gltf;
var imageSource = extras.source;
2017-07-19 17:56:24 -04:00
var imageByteLength = imageSource.length;
image.mimeType = mime.lookup(extras.extension);
2017-07-19 17:56:24 -04:00
image.bufferView = gltf.bufferViews.length;
gltf.bufferViews.push({
buffer : 0,
2017-07-19 17:56:24 -04:00
byteOffset : byteOffset,
byteLength : imageByteLength
});
2017-07-19 17:56:24 -04:00
byteOffset += imageByteLength;
sources.push(imageSource);
}
2017-07-19 17:56:24 -04:00
var source = getBufferPadded(Buffer.concat(sources));
bufferExtras.source = source;
buffer.byteLength = source.length;
}