mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2025-02-07 07:22:51 -05:00
2.0
This commit is contained in:
parent
a6ff230fc3
commit
ce5221c80a
@ -4,16 +4,16 @@ module.exports = Material;
|
||||
|
||||
function Material() {
|
||||
this.ambientColor = [0.0, 0.0, 0.0, 1.0]; // Ka
|
||||
this.emissionColor = [0.0, 0.0, 0.0, 1.0]; // Ke
|
||||
this.emissiveColor = [0.0, 0.0, 0.0, 1.0]; // Ke
|
||||
this.diffuseColor = [0.5, 0.5, 0.5, 1.0]; // Kd
|
||||
this.specularColor = [0.0, 0.0, 0.0, 1.0]; // Ks
|
||||
this.specularShininess = 0.0; // Ns
|
||||
this.alpha = 1.0; // d / Tr
|
||||
this.ambientTexture = undefined; // map_Ka
|
||||
this.emissionTexture = undefined; // map_Ke
|
||||
this.emissiveTexture = undefined; // map_Ke
|
||||
this.diffuseTexture = undefined; // map_Kd
|
||||
this.specularTexture = undefined; // map_Ks
|
||||
this.specularShininessMap = undefined; // map_Ns
|
||||
this.normalMap = undefined; // map_Bump
|
||||
this.alphaMap = undefined; // map_d
|
||||
this.specularShininessTexture = undefined; // map_Ns
|
||||
this.normalTexture = undefined; // map_Bump
|
||||
this.alphaTexture = undefined; // map_d
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var path = require('path');
|
||||
var PNG = require('pngjs').PNG;
|
||||
var Material = require('./Material');
|
||||
|
||||
var defined = Cesium.defined;
|
||||
@ -13,160 +14,463 @@ module.exports = createGltf;
|
||||
* Create a glTF from obj data.
|
||||
*
|
||||
* @param {Object} objData Output of obj.js, containing an array of nodes containing geometry information, materials, and images.
|
||||
* @returns {Object} A glTF asset with the KHR_materials_common extension.
|
||||
* @param {Object} options An object with the following properties:
|
||||
* @param {Boolean} options.logger A callback function for handling logged messages. Defaults to console.log.
|
||||
* @returns {Object} A glTF asset.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function createGltf(objData) {
|
||||
function createGltf(objData, options) {
|
||||
var nodes = objData.nodes;
|
||||
var materials = objData.materials;
|
||||
var images = objData.images;
|
||||
var sceneId = 'scene';
|
||||
var samplerId = 'sampler';
|
||||
var bufferId = 'buffer';
|
||||
var vertexBufferViewId = 'bufferView_vertex';
|
||||
var indexBufferViewId = 'bufferView_index';
|
||||
|
||||
var gltf = {
|
||||
accessors : {},
|
||||
accessors : [],
|
||||
asset : {},
|
||||
buffers : {},
|
||||
bufferViews : {},
|
||||
extensionsUsed : ['KHR_materials_common'],
|
||||
images : {},
|
||||
materials : {},
|
||||
meshes : {},
|
||||
nodes : {},
|
||||
samplers : {},
|
||||
scene : sceneId,
|
||||
scenes : {},
|
||||
textures : {}
|
||||
buffers : [],
|
||||
bufferViews : [],
|
||||
images : [],
|
||||
materials : [],
|
||||
meshes : [],
|
||||
nodes : [],
|
||||
samplers : [],
|
||||
scene : 0,
|
||||
scenes : [],
|
||||
textures : []
|
||||
};
|
||||
|
||||
gltf.asset = {
|
||||
generator : 'obj2gltf',
|
||||
profile : {
|
||||
api : 'WebGL',
|
||||
version : '1.0'
|
||||
},
|
||||
version: '1.0'
|
||||
version: '2.0'
|
||||
};
|
||||
|
||||
gltf.scenes[sceneId] = {
|
||||
gltf.scenes.push({
|
||||
nodes : []
|
||||
});
|
||||
|
||||
var bufferState = {
|
||||
vertexBuffers : [],
|
||||
vertexBufferByteOffset : 0,
|
||||
vertexBufferViewIndex : 0,
|
||||
indexBuffers : [],
|
||||
indexBufferByteOffset : 0,
|
||||
indexBufferViewIndex : 1
|
||||
};
|
||||
|
||||
function getImageId(imagePath) {
|
||||
return path.basename(imagePath, path.extname(imagePath));
|
||||
}
|
||||
var uint32Indices = requiresUint32Indices(nodes);
|
||||
|
||||
function getTextureId(imagePath) {
|
||||
if (!defined(imagePath) || !defined(images[imagePath])) {
|
||||
return undefined;
|
||||
}
|
||||
return 'texture_' + getImageId(imagePath);
|
||||
}
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var node = nodes[i];
|
||||
var meshes = node.meshes;
|
||||
var meshesLength = meshes.length;
|
||||
var meshIndex;
|
||||
|
||||
function createMaterial(material, hasNormals) {
|
||||
var ambient = defaultValue(defaultValue(getTextureId(material.ambientTexture), material.ambientColor));
|
||||
var diffuse = defaultValue(defaultValue(getTextureId(material.diffuseTexture), material.diffuseColor));
|
||||
var emission = defaultValue(defaultValue(getTextureId(material.emissionTexture), material.emissionColor));
|
||||
var specular = defaultValue(defaultValue(getTextureId(material.specularTexture), material.specularColor));
|
||||
var alpha = defaultValue(defaultValue(material.alpha), 1.0);
|
||||
var shininess = defaultValue(material.specularShininess, 0.0);
|
||||
var hasSpecular = (shininess > 0.0) && (specular[0] > 0.0 || specular[1] > 0.0 || specular[2] > 0.0);
|
||||
|
||||
var transparent;
|
||||
var transparency = 1.0;
|
||||
if (typeof diffuse === 'string') {
|
||||
transparency = alpha;
|
||||
transparent = images[material.diffuseTexture].transparent || (transparency < 1.0);
|
||||
if (meshesLength === 1) {
|
||||
meshIndex = addMesh(gltf, materials, images, bufferState, uint32Indices, meshes[0], options);
|
||||
addNode(gltf, node.name, meshIndex);
|
||||
} else {
|
||||
diffuse[3] = alpha;
|
||||
transparent = diffuse[3] < 1.0;
|
||||
// Add meshes as child nodes
|
||||
var parentIndex = addNode(gltf, node.name);
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
var mesh = meshes[j];
|
||||
meshIndex = addMesh(gltf, materials, images, bufferState, uint32Indices, mesh, options);
|
||||
addNode(gltf, mesh.name, meshIndex, parentIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var doubleSided = transparent;
|
||||
|
||||
if (!hasNormals) {
|
||||
// Constant technique only factors in ambient and emission sources - set emission to diffuse
|
||||
emission = diffuse;
|
||||
diffuse = [0, 0, 0, 1];
|
||||
}
|
||||
|
||||
var technique = hasNormals ? (hasSpecular ? 'PHONG' : 'LAMBERT') : 'CONSTANT';
|
||||
return {
|
||||
extensions : {
|
||||
KHR_materials_common : {
|
||||
technique : technique,
|
||||
transparent : transparent,
|
||||
doubleSided : doubleSided,
|
||||
values : {
|
||||
ambient : ambient,
|
||||
diffuse : diffuse,
|
||||
emission : emission,
|
||||
specular : specular,
|
||||
shininess : shininess,
|
||||
transparency : transparency,
|
||||
transparent : transparent,
|
||||
doubleSided : doubleSided
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (Object.keys(images).length > 0) {
|
||||
gltf.samplers[samplerId] = {
|
||||
if (Object.keys(gltf.images).length > 0) {
|
||||
gltf.samplers.push({
|
||||
magFilter : WebGLConstants.LINEAR,
|
||||
minFilter : WebGLConstants.LINEAR,
|
||||
wrapS : WebGLConstants.REPEAT,
|
||||
wrapT : WebGLConstants.REPEAT
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
for (var imagePath in images) {
|
||||
if (images.hasOwnProperty(imagePath)) {
|
||||
var image = images[imagePath];
|
||||
var imageId = getImageId(imagePath);
|
||||
var textureId = getTextureId(imagePath);
|
||||
addBuffers(gltf, bufferState);
|
||||
|
||||
gltf.images[imageId] = {
|
||||
name : imageId,
|
||||
return gltf;
|
||||
}
|
||||
|
||||
function addBuffers(gltf, bufferState) {
|
||||
var bufferName = 'buffer';
|
||||
var vertexBufferViewName = 'bufferView_vertex';
|
||||
var indexBufferViewName = 'bufferView_index';
|
||||
|
||||
var vertexBuffers = bufferState.vertexBuffers;
|
||||
var indexBuffers = bufferState.indexBuffers;
|
||||
var vertexBufferByteLength = bufferState.vertexBufferByteOffset;
|
||||
var indexBufferByteLength = bufferState.indexBufferByteOffset;
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(vertexBuffers, indexBuffers);
|
||||
var buffer = Buffer.concat(buffers);
|
||||
|
||||
gltf.buffers.push({
|
||||
name : bufferName,
|
||||
byteLength : buffer.byteLength,
|
||||
extras : {
|
||||
_obj2gltf : {
|
||||
source : buffer
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gltf.bufferViews.push({
|
||||
name : vertexBufferViewName,
|
||||
buffer : 0,
|
||||
byteLength : vertexBufferByteLength,
|
||||
byteOffset : 0,
|
||||
target : WebGLConstants.ARRAY_BUFFER
|
||||
});
|
||||
|
||||
gltf.bufferViews.push({
|
||||
name : indexBufferViewName,
|
||||
buffer : 0,
|
||||
byteLength : indexBufferByteLength,
|
||||
byteOffset : vertexBufferByteLength,
|
||||
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
|
||||
});
|
||||
}
|
||||
|
||||
function getImage(images, imagePath) {
|
||||
if (!defined(imagePath) || !defined(images[imagePath])) {
|
||||
return undefined;
|
||||
}
|
||||
return images[imagePath];
|
||||
}
|
||||
|
||||
function getImageName(imagePath) {
|
||||
return path.basename(imagePath, path.extname(imagePath));
|
||||
}
|
||||
|
||||
function getTextureName(imagePath) {
|
||||
return getImageName(imagePath);
|
||||
}
|
||||
|
||||
function addTexture(gltf, image, imagePath) {
|
||||
var imageName = getImageName(imagePath);
|
||||
var textureName = getTextureName(imagePath);
|
||||
var imageIndex = gltf.images.length;
|
||||
var textureIndex = gltf.textures.length;
|
||||
|
||||
gltf.images.push({
|
||||
name : imageName,
|
||||
extras : {
|
||||
_obj2gltf : {
|
||||
source : image.source,
|
||||
extension : image.extension
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
gltf.textures.push({
|
||||
name : textureName,
|
||||
sampler : 0,
|
||||
source : imageIndex
|
||||
});
|
||||
|
||||
return textureIndex;
|
||||
}
|
||||
|
||||
function getTextureIndex(gltf, imagePath) {
|
||||
var name = getTextureName(imagePath);
|
||||
var textures = gltf.textures;
|
||||
var length = textures.length;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (textures[i].name === name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTexture(gltf, images, imagePath) {
|
||||
var image = getImage(images, imagePath);
|
||||
if (!defined(image)) {
|
||||
return undefined;
|
||||
}
|
||||
var textureIndex = getTextureIndex(gltf, imagePath);
|
||||
if (!defined(textureIndex)) {
|
||||
textureIndex = addTexture(gltf, image, imagePath);
|
||||
}
|
||||
return textureIndex;
|
||||
}
|
||||
|
||||
function luminance(color) {
|
||||
var value = 0.2125 * color[0] + 0.7154 * color[1] + 0.0721 * color[2];
|
||||
return Math.min(value, 1.0); // Clamp just to handle edge cases
|
||||
}
|
||||
|
||||
function addColors(left, right) {
|
||||
var red = Math.min(left[0] + right[0], 1.0);
|
||||
var green = Math.min(left[1] + right[1], 1.0);
|
||||
var blue = Math.min(left[2] + right[2], 1.0);
|
||||
return [red, green, blue];
|
||||
}
|
||||
|
||||
function resizeChannel(sourcePixels, sourceWidth, sourceHeight, targetWidth, targetHeight) {
|
||||
// Nearest neighbor sampling
|
||||
var targetPixels = Buffer.alloc(targetWidth * targetHeight);
|
||||
var widthRatio = sourceWidth / targetWidth;
|
||||
var heightRatio = sourceHeight / targetHeight;
|
||||
|
||||
for (var y = 0; y < targetHeight; ++y) {
|
||||
for (var x = 0; x < targetWidth; ++x) {
|
||||
var targetIndex = y * targetWidth + x;
|
||||
var sourceY = Math.round(y * heightRatio);
|
||||
var sourceX = Math.round(x * widthRatio);
|
||||
var sourceIndex = sourceY * sourceWidth + sourceX;
|
||||
var sourceValue = sourcePixels.readUInt8(sourceIndex);
|
||||
targetPixels.writeUInt8(sourceValue, targetIndex);
|
||||
}
|
||||
}
|
||||
return targetPixels;
|
||||
}
|
||||
|
||||
var scratchColor = new Array(3);
|
||||
|
||||
function getGrayscaleChannel(image, targetWidth, targetHeight) {
|
||||
var pixels = image.decoded; // RGBA
|
||||
var width = image.width;
|
||||
var height = image.height;
|
||||
var pixelsLength = width * height;
|
||||
var grayPixels = Buffer.alloc(pixelsLength);
|
||||
for (var i = 0; i < pixelsLength; ++i) {
|
||||
scratchColor[0] = pixels.readUInt8(i * 4);
|
||||
scratchColor[1] = pixels.readUInt8(i * 4 + 1);
|
||||
scratchColor[2] = pixels.readUInt8(i * 4 + 2);
|
||||
var value = luminance(scratchColor) * 255;
|
||||
grayPixels.writeUInt8(value, i);
|
||||
}
|
||||
if (width !== targetWidth || height !== targetHeight) {
|
||||
grayPixels = resizeChannel(grayPixels, width, height, targetWidth, targetHeight);
|
||||
}
|
||||
return grayPixels;
|
||||
}
|
||||
|
||||
function writeChannel(pixels, channel, index, width, height) {
|
||||
var pixelsLength = width * height;
|
||||
for (var i = 0; i < pixelsLength; ++i) {
|
||||
var value = channel.readUInt8(i);
|
||||
pixels.writeUInt8(value, i * 4 + index);
|
||||
}
|
||||
}
|
||||
|
||||
function createMetallicRoughnessTexture(gltf, materialName, metallicImage, roughnessImage, options) {
|
||||
if (!defined(metallicImage) && !defined(roughnessImage)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (defined(metallicImage) && !defined(metallicImage.decoded)) {
|
||||
options.logger('Could not get decoded image data for ' + metallicImage + '. The material will be created without a metallicRoughness texture.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (defined(roughnessImage) && !defined(roughnessImage.decoded)) {
|
||||
options.logger('Could not get decoded image data for ' + roughnessImage + '. The material will be created without a metallicRoughness texture.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var width;
|
||||
var height;
|
||||
|
||||
if (defined(metallicImage) && defined(roughnessImage)) {
|
||||
width = Math.min(metallicImage.width, roughnessImage.width);
|
||||
height = Math.min(metallicImage.height, roughnessImage.height);
|
||||
} else if (defined(metallicImage)) {
|
||||
width = metallicImage.width;
|
||||
height = metallicImage.height;
|
||||
} else if (defined(roughnessImage)) {
|
||||
width = roughnessImage.width;
|
||||
height = roughnessImage.height;
|
||||
}
|
||||
|
||||
var pixelsLength = width * height;
|
||||
var pixels = Buffer.alloc(pixelsLength * 4, 0xFF); // Initialize with 4 channels, unused channels will be white
|
||||
|
||||
if (defined(metallicImage)) {
|
||||
// Write into the B channel
|
||||
var metallicChannel = getGrayscaleChannel(metallicImage, width, height);
|
||||
writeChannel(pixels, metallicChannel, 2, width, height);
|
||||
}
|
||||
|
||||
if (defined(roughnessImage)) {
|
||||
// Write into the G channel
|
||||
var roughnessChannel = getGrayscaleChannel(roughnessImage, width, height);
|
||||
writeChannel(pixels, roughnessChannel, 1, width, height);
|
||||
}
|
||||
|
||||
var pngInput = {
|
||||
data : pixels,
|
||||
width : width,
|
||||
height : height
|
||||
};
|
||||
gltf.textures[textureId] = {
|
||||
format : image.format,
|
||||
internalFormat : image.format,
|
||||
sampler : samplerId,
|
||||
source : imageId,
|
||||
target : WebGLConstants.TEXTURE_2D,
|
||||
type : WebGLConstants.UNSIGNED_BYTE
|
||||
|
||||
var pngOptions = {
|
||||
width : width,
|
||||
height : height,
|
||||
colorType : 2, // RGB
|
||||
inputHasAlpha : true
|
||||
};
|
||||
|
||||
var encoded = PNG.sync.write(pngInput, pngOptions);
|
||||
|
||||
var image = {
|
||||
transparent : false,
|
||||
source : encoded,
|
||||
extension : '.png'
|
||||
};
|
||||
|
||||
var imageName = materialName + '-' + 'MetallicRoughness';
|
||||
return addTexture(gltf, image, imageName);
|
||||
}
|
||||
|
||||
function addMaterial(gltf, images, material, name, hasNormals, options) {
|
||||
// Translate the traditional diffuse/specular material to pbr metallic roughness.
|
||||
// Specular intensity is extracted from the specular color and treated as the metallic factor.
|
||||
// Specular shininess is typically an exponent from 0 to 1000, and is converted to a 0-1 range as the roughness factor.
|
||||
var ambientTexture = getTexture(gltf, images, material.ambientTexture);
|
||||
var emissiveTexture = getTexture(gltf, images, material.emissiveTexture);
|
||||
var baseColorTexture = getTexture(gltf, images, material.diffuseTexture);
|
||||
var normalTexture = getTexture(gltf, images, material.normalTexture);
|
||||
|
||||
// Emissive and ambient represent roughly the same concept, so chose whichever is defined.
|
||||
emissiveTexture = defaultValue(emissiveTexture, ambientTexture);
|
||||
|
||||
var metallicImage = getImage(images, material.specularTexture);
|
||||
var roughnessImage = getImage(images, material.specularShininessTexture);
|
||||
var metallicRoughnessTexture = createMetallicRoughnessTexture(gltf, name, metallicImage, roughnessImage, options);
|
||||
|
||||
var baseColorFactor = [1.0, 1.0, 1.0, 1.0];
|
||||
var metallicFactor = 1.0;
|
||||
var roughnessFactor = 1.0;
|
||||
var emissiveFactor = [1.0, 1.0, 1.0];
|
||||
|
||||
if (!defined(baseColorTexture)) {
|
||||
baseColorFactor = material.diffuseColor;
|
||||
}
|
||||
|
||||
if (!defined(metallicImage)) {
|
||||
metallicFactor = luminance(material.specularColor);
|
||||
}
|
||||
|
||||
if (!defined(roughnessImage)) {
|
||||
var specularShininess = material.specularShininess;
|
||||
if (specularShininess > 1.0) {
|
||||
specularShininess /= 1000.0;
|
||||
}
|
||||
roughnessFactor = specularShininess;
|
||||
}
|
||||
|
||||
if (!defined(emissiveTexture)) {
|
||||
// If ambient color is [1, 1, 1] assume it is a multiplier and instead change to [0, 0, 0]
|
||||
var ambientColor = material.ambientColor;
|
||||
if (ambientColor[0] === 1.0 && ambientColor[1] === 1.0 && ambientColor[2] === 1.0) {
|
||||
ambientColor = [0.0, 0.0, 0.0, 1.0];
|
||||
}
|
||||
emissiveFactor = addColors(material.emissiveColor, ambientColor);
|
||||
}
|
||||
|
||||
var alpha = material.alpha;
|
||||
baseColorFactor[3] = alpha;
|
||||
|
||||
var transparent = alpha < 1.0;
|
||||
if (defined(material.diffuseTexture)) {
|
||||
transparent |= images[material.diffuseTexture].transparent;
|
||||
}
|
||||
|
||||
var doubleSided = transparent;
|
||||
var alphaMode = transparent ? 'BLEND' : 'OPAQUE';
|
||||
|
||||
if (!hasNormals) {
|
||||
// TODO : what is the lighting like for models that don't have normals? Can pbrMetallicRoughness just be undefined? Is setting the baseColor to black a good approach here?
|
||||
emissiveTexture = baseColorTexture;
|
||||
emissiveFactor = baseColorFactor.slice(0, 3);
|
||||
baseColorTexture = undefined;
|
||||
baseColorFactor = [0.0, 0.0, 0.0, baseColorFactor[3]];
|
||||
metallicRoughnessTexture = undefined;
|
||||
metallicFactor = 0.0;
|
||||
roughnessFactor = 0.0;
|
||||
normalTexture = undefined;
|
||||
}
|
||||
|
||||
var gltfMaterial = {
|
||||
name : name,
|
||||
pbrMetallicRoughness : {
|
||||
baseColorTexture : baseColorTexture,
|
||||
baseColorFactor : baseColorFactor,
|
||||
metallicFactor : metallicFactor,
|
||||
roughnessFactor : roughnessFactor,
|
||||
metallicRoughnessTexture : metallicRoughnessTexture
|
||||
},
|
||||
normalTexture : normalTexture,
|
||||
emissiveTexture : emissiveTexture,
|
||||
emissiveFactor : emissiveFactor,
|
||||
alphaMode : alphaMode,
|
||||
doubleSided : doubleSided,
|
||||
extras : {
|
||||
_obj2gltf : {
|
||||
hasNormals : hasNormals
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var materialIndex = gltf.materials.length;
|
||||
gltf.materials.push(gltfMaterial);
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
|
||||
function getMaterialIndex(gltf, name) {
|
||||
var materials = gltf.materials;
|
||||
var length = materials.length;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
if (materials[i].name === name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getMaterial(gltf, materials, images, materialName, hasNormals, options) {
|
||||
if (!defined(materialName)) {
|
||||
// Create a default material if the primitive does not specify one
|
||||
materialName = 'default';
|
||||
}
|
||||
|
||||
var material = materials[materialName];
|
||||
material = defined(material) ? material : new Material();
|
||||
var materialIndex = getMaterialIndex(gltf, materialName);
|
||||
|
||||
// Check if this material has already been added but with incompatible shading
|
||||
if (defined(materialIndex)) {
|
||||
var gltfMaterial = gltf.materials[materialIndex];
|
||||
var normalShading = gltfMaterial.extras._obj2gltf.hasNormals;
|
||||
if (hasNormals !== normalShading) {
|
||||
materialName += (hasNormals ? '_shaded' : '_constant');
|
||||
materialIndex = getMaterialIndex(gltf, materialName);
|
||||
}
|
||||
}
|
||||
|
||||
var vertexBuffers = [];
|
||||
var vertexBufferByteOffset = 0;
|
||||
var indexBuffers = [];
|
||||
var indexBufferByteOffset = 0;
|
||||
var accessorCount = 0;
|
||||
if (!defined(materialIndex)) {
|
||||
materialIndex = addMaterial(gltf, images, material, materialName, hasNormals, options);
|
||||
}
|
||||
|
||||
function addVertexAttribute(array, components) {
|
||||
var count = array.length / components;
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
function addVertexAttribute(gltf, bufferState, array, components) {
|
||||
var buffer = array.toFloatBuffer();
|
||||
var count = array.length / components;
|
||||
var minMax = array.getMinMax(components);
|
||||
|
||||
var type = (components === 3 ? 'VEC3' : 'VEC2');
|
||||
|
||||
var accessor = {
|
||||
bufferView : vertexBufferViewId,
|
||||
byteOffset : vertexBufferByteOffset,
|
||||
byteStride : 0,
|
||||
bufferView : bufferState.vertexBufferViewIndex,
|
||||
byteOffset : bufferState.vertexBufferByteOffset,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : count,
|
||||
min : minMax.min,
|
||||
@ -174,38 +478,39 @@ function createGltf(objData) {
|
||||
type : type
|
||||
};
|
||||
|
||||
vertexBufferByteOffset += buffer.length;
|
||||
vertexBuffers.push(buffer);
|
||||
var accessorId = 'accessor_' + accessorCount++;
|
||||
gltf.accessors[accessorId] = accessor;
|
||||
return accessorId;
|
||||
}
|
||||
bufferState.vertexBufferByteOffset += buffer.length;
|
||||
bufferState.vertexBuffers.push(buffer);
|
||||
|
||||
function addIndexArray(array, uint32Indices) {
|
||||
var accessorIndex = gltf.accessors.length;
|
||||
gltf.accessors.push(accessor);
|
||||
return accessorIndex;
|
||||
}
|
||||
|
||||
function addIndexArray(gltf, bufferState, array, uint32Indices) {
|
||||
var buffer = uint32Indices ? array.toUint32Buffer() : array.toUint16Buffer();
|
||||
var componentType = uint32Indices ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT;
|
||||
var length = array.length;
|
||||
var count = array.length;
|
||||
var minMax = array.getMinMax(1);
|
||||
|
||||
var accessor = {
|
||||
bufferView : indexBufferViewId,
|
||||
byteOffset : indexBufferByteOffset,
|
||||
byteStride : 0,
|
||||
bufferView : bufferState.indexBufferViewIndex,
|
||||
byteOffset : bufferState.indexBufferByteOffset,
|
||||
componentType : componentType,
|
||||
count : length,
|
||||
count : count,
|
||||
min : minMax.min,
|
||||
max : minMax.max,
|
||||
type : 'SCALAR'
|
||||
};
|
||||
|
||||
indexBufferByteOffset += buffer.length;
|
||||
indexBuffers.push(buffer);
|
||||
bufferState.indexBufferByteOffset += buffer.length;
|
||||
bufferState.indexBuffers.push(buffer);
|
||||
|
||||
var accessorId = 'accessor_' + accessorCount++;
|
||||
gltf.accessors[accessorId] = accessor;
|
||||
return accessorId;
|
||||
}
|
||||
var accessorIndex = gltf.accessors.length;
|
||||
gltf.accessors.push(accessor);
|
||||
return accessorIndex;
|
||||
}
|
||||
|
||||
function requiresUint32Indices(nodes) {
|
||||
function requiresUint32Indices(nodes) {
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
var meshes = nodes[i].meshes;
|
||||
@ -219,43 +524,22 @@ function createGltf(objData) {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var uint32Indices = requiresUint32Indices(nodes);
|
||||
var gltfSceneNodes = gltf.scenes[sceneId].nodes;
|
||||
var nodesLength = nodes.length;
|
||||
for (var i = 0; i < nodesLength; ++i) {
|
||||
// Add node
|
||||
var node = nodes[i];
|
||||
var nodeId = node.name;
|
||||
gltfSceneNodes.push(nodeId);
|
||||
var gltfNodeMeshes = [];
|
||||
gltf.nodes[nodeId] = {
|
||||
name : nodeId,
|
||||
meshes : gltfNodeMeshes
|
||||
};
|
||||
|
||||
// Add meshes to node
|
||||
var meshes = node.meshes;
|
||||
var meshesLength = meshes.length;
|
||||
for (var j = 0; j < meshesLength; ++j) {
|
||||
var mesh = meshes[j];
|
||||
var meshId = mesh.name;
|
||||
gltfNodeMeshes.push(meshId);
|
||||
}
|
||||
|
||||
function addMesh(gltf, materials, images, bufferState, uint32Indices, mesh, options) {
|
||||
var hasPositions = mesh.positions.length > 0;
|
||||
var hasNormals = mesh.normals.length > 0;
|
||||
var hasUVs = mesh.uvs.length > 0;
|
||||
|
||||
var attributes = {};
|
||||
if (hasPositions) {
|
||||
attributes.POSITION = addVertexAttribute(mesh.positions, 3);
|
||||
attributes.POSITION = addVertexAttribute(gltf, bufferState, mesh.positions, 3);
|
||||
}
|
||||
if (hasNormals) {
|
||||
attributes.NORMAL = addVertexAttribute(mesh.normals, 3);
|
||||
attributes.NORMAL = addVertexAttribute(gltf, bufferState, mesh.normals, 3);
|
||||
}
|
||||
if (hasUVs) {
|
||||
attributes.TEXCOORD_0 = addVertexAttribute(mesh.uvs, 2);
|
||||
attributes.TEXCOORD_0 = addVertexAttribute(gltf, bufferState, mesh.uvs, 2);
|
||||
}
|
||||
|
||||
// Unload resources
|
||||
@ -263,78 +547,52 @@ function createGltf(objData) {
|
||||
mesh.normals = undefined;
|
||||
mesh.uvs = undefined;
|
||||
|
||||
var gltfMeshPrimitives = [];
|
||||
gltf.meshes[meshId] = {
|
||||
name : meshId,
|
||||
primitives : gltfMeshPrimitives
|
||||
};
|
||||
|
||||
// Add primitives to mesh
|
||||
var gltfPrimitives = [];
|
||||
var primitives = mesh.primitives;
|
||||
var primitivesLength = primitives.length;
|
||||
for (var k = 0; k < primitivesLength; ++k) {
|
||||
var primitive = primitives[k];
|
||||
var indexAccessorId = addIndexArray(primitive.indices, uint32Indices);
|
||||
for (var i = 0; i < primitivesLength; ++i) {
|
||||
var primitive = primitives[i];
|
||||
var indexAccessorIndex = addIndexArray(gltf, bufferState, primitive.indices, uint32Indices);
|
||||
primitive.indices = undefined; // Unload resources
|
||||
var materialId = primitive.material;
|
||||
|
||||
if (!defined(materialId)) {
|
||||
// Create a default material if the primitive does not specify one
|
||||
materialId = 'default';
|
||||
}
|
||||
var materialIndex = getMaterial(gltf, materials, images, primitive.material, hasNormals, options);
|
||||
|
||||
var material = materials[materialId];
|
||||
material = defined(material) ? material : new Material();
|
||||
var gltfMaterial = gltf.materials[materialId];
|
||||
if (defined(gltfMaterial)) {
|
||||
// Check if this material has already been added but with incompatible shading
|
||||
var normalShading = (gltfMaterial.extensions.KHR_materials_common.technique !== 'CONSTANT');
|
||||
if (hasNormals !== normalShading) {
|
||||
materialId += (hasNormals ? '_shaded' : '_constant');
|
||||
gltfMaterial = gltf.materials[materialId];
|
||||
}
|
||||
}
|
||||
|
||||
if (!defined(gltfMaterial)) {
|
||||
gltf.materials[materialId] = createMaterial(material, hasNormals);
|
||||
}
|
||||
|
||||
gltfMeshPrimitives.push({
|
||||
gltfPrimitives.push({
|
||||
attributes : attributes,
|
||||
indices : indexAccessorId,
|
||||
material : materialId,
|
||||
indices : indexAccessorIndex,
|
||||
material : materialIndex,
|
||||
mode : WebGLConstants.TRIANGLES
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(vertexBuffers, indexBuffers);
|
||||
var buffer = Buffer.concat(buffers);
|
||||
|
||||
gltf.buffers[bufferId] = {
|
||||
byteLength : buffer.byteLength,
|
||||
extras : {
|
||||
_obj2gltf : {
|
||||
source : buffer
|
||||
}
|
||||
}
|
||||
var gltfMesh = {
|
||||
name : mesh.name,
|
||||
primitives : gltfPrimitives
|
||||
};
|
||||
|
||||
gltf.bufferViews[vertexBufferViewId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : vertexBufferByteOffset,
|
||||
byteOffset : 0,
|
||||
target : WebGLConstants.ARRAY_BUFFER
|
||||
};
|
||||
|
||||
gltf.bufferViews[indexBufferViewId] = {
|
||||
buffer : bufferId,
|
||||
byteLength : indexBufferByteOffset,
|
||||
byteOffset : vertexBufferByteOffset,
|
||||
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
|
||||
};
|
||||
|
||||
return gltf;
|
||||
var meshIndex = gltf.meshes.length;
|
||||
gltf.meshes.push(gltfMesh);
|
||||
return meshIndex;
|
||||
}
|
||||
|
||||
function addNode(gltf, name, meshIndex, parentIndex) {
|
||||
var node = {
|
||||
name : name,
|
||||
mesh : meshIndex
|
||||
};
|
||||
|
||||
var nodeIndex = gltf.nodes.length;
|
||||
gltf.nodes.push(node);
|
||||
|
||||
if (defined(parentIndex)) {
|
||||
var parentNode = gltf.nodes[parentIndex];
|
||||
if (!defined(parentNode.children)) {
|
||||
parentNode.children = [];
|
||||
}
|
||||
parentNode.children.push(nodeIndex);
|
||||
} else {
|
||||
gltf.scenes[gltf.scene].nodes.push(nodeIndex);
|
||||
}
|
||||
|
||||
return nodeIndex;
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var fsExtra = require('fs-extra');
|
||||
var jpeg = require('jpeg-js');
|
||||
var path = require('path');
|
||||
var PNG = require('pngjs').PNG;
|
||||
var Promise = require('bluebird');
|
||||
|
||||
var fsExtraReadFile = Promise.promisify(fsExtra.readFile);
|
||||
|
||||
var defined = Cesium.defined;
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
|
||||
module.exports = loadImage;
|
||||
|
||||
@ -17,67 +17,49 @@ module.exports = loadImage;
|
||||
*
|
||||
* @param {String} imagePath Path to the image file.
|
||||
* @param {Object} options An object with the following properties:
|
||||
* @param {Boolean} options.checkTransparency Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
||||
* @param {Boolean} [options.checkTransparency=false] Do a more exhaustive check for texture transparency by looking at the alpha channel of each pixel.
|
||||
* @param {Boolean} [options.decode=false] Decode image.
|
||||
* @returns {Promise} A promise resolving to the image information, or undefined if the file doesn't exist.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function loadImage(imagePath, options) {
|
||||
options = defaultValue(options, {});
|
||||
options.checkTransparency = defaultValue(options.checkTransparency, false);
|
||||
options.decode = defaultValue(options.decode, false);
|
||||
|
||||
return fsExtraReadFile(imagePath)
|
||||
.then(function(data) {
|
||||
var extension = path.extname(imagePath).toLowerCase();
|
||||
|
||||
var info = {
|
||||
transparent : false,
|
||||
format : getFormat(3),
|
||||
source : data,
|
||||
extension : extension
|
||||
extension : extension,
|
||||
decoded : undefined,
|
||||
width : undefined,
|
||||
height : undefined
|
||||
};
|
||||
|
||||
if (extension === '.png') {
|
||||
return getPngInfo(data, info, options);
|
||||
} else if (extension === '.jpg' || extension === '.jpeg') {
|
||||
return getJpegInfo(data, info, options);
|
||||
}
|
||||
|
||||
return info;
|
||||
});
|
||||
}
|
||||
|
||||
function getPngInfo(data, info, options) {
|
||||
// Color type is encoded in the 25th bit of the png
|
||||
var colorType = data[25];
|
||||
var channels = getChannels(colorType);
|
||||
info.format = getFormat(channels);
|
||||
|
||||
if (channels === 4) {
|
||||
if (options.checkTransparency) {
|
||||
return isTransparent(data)
|
||||
.then(function(transparent) {
|
||||
info.transparent = transparent;
|
||||
return info;
|
||||
});
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
function isTransparent(data) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
new PNG().parse(data, function(error, data) {
|
||||
if (defined(error)) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
var pixels = data.data;
|
||||
var pixelsLength = data.width * data.height;
|
||||
function hasTransparency(info) {
|
||||
var pixels = info.decoded;
|
||||
var pixelsLength = info.width * info.height;
|
||||
for (var i = 0; i < pixelsLength; ++i) {
|
||||
if (pixels[i * 4 + 3] < 255) {
|
||||
resolve(true);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
function getChannels(colorType) {
|
||||
@ -95,15 +77,32 @@ function getChannels(colorType) {
|
||||
}
|
||||
}
|
||||
|
||||
function getFormat(channels) {
|
||||
switch (channels) {
|
||||
case 1:
|
||||
return WebGLConstants.ALPHA;
|
||||
case 2:
|
||||
return WebGLConstants.LUMINANCE_ALPHA;
|
||||
case 3:
|
||||
return WebGLConstants.RGB;
|
||||
case 4:
|
||||
return WebGLConstants.RGBA;
|
||||
function getPngInfo(data, info, options) {
|
||||
// Color type is encoded in the 25th bit of the png
|
||||
var colorType = data[25];
|
||||
var channels = getChannels(colorType);
|
||||
|
||||
var checkTransparency = (channels === 4 && options.checkTransparency);
|
||||
var decode = options.decode || checkTransparency;
|
||||
|
||||
if (decode) {
|
||||
var decodedResults = PNG.sync.read(data);
|
||||
info.decoded = decodedResults.data;
|
||||
info.width = decodedResults.width;
|
||||
info.height = decodedResults.height;
|
||||
if (checkTransparency) {
|
||||
info.transparent = hasTransparency(info);
|
||||
}
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
function getJpegInfo(data, info, options) {
|
||||
if (options.decode) {
|
||||
var decodedResults = jpeg.decode(data);
|
||||
info.decoded = decodedResults.data;
|
||||
info.width = decodedResults.width;
|
||||
info.height = decodedResults.height;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ function loadMtl(mtlPath) {
|
||||
];
|
||||
} else if (/^Ke /i.test(line)) {
|
||||
values = line.substring(3).trim().split(' ');
|
||||
material.emissionColor = [
|
||||
material.emissiveColor = [
|
||||
parseFloat(values[0]),
|
||||
parseFloat(values[1]),
|
||||
parseFloat(values[2]),
|
||||
@ -70,17 +70,17 @@ function loadMtl(mtlPath) {
|
||||
} else if (/^map_Ka /i.test(line)) {
|
||||
material.ambientTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
} else if (/^map_Ke /i.test(line)) {
|
||||
material.emissionTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
material.emissiveTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
} else if (/^map_Kd /i.test(line)) {
|
||||
material.diffuseTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
} else if (/^map_Ks /i.test(line)) {
|
||||
material.specularTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
} else if (/^map_Ns /i.test(line)) {
|
||||
material.specularShininessMap = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
material.specularShininessTexture = path.resolve(mtlDirectory, line.substring(7).trim());
|
||||
} else if (/^map_Bump /i.test(line)) {
|
||||
material.normalMap = path.resolve(mtlDirectory, line.substring(9).trim());
|
||||
material.normalTexture = path.resolve(mtlDirectory, line.substring(9).trim());
|
||||
} else if (/^map_d /i.test(line)) {
|
||||
material.alphaMap = path.resolve(mtlDirectory, line.substring(6).trim());
|
||||
material.alphaTexture = path.resolve(mtlDirectory, line.substring(6).trim());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -287,8 +287,8 @@ function finishLoading(nodes, mtlPaths, objPath, options) {
|
||||
}
|
||||
return loadMaterials(mtlPaths, objPath, options)
|
||||
.then(function(materials) {
|
||||
var imagePaths = getImagePaths(materials);
|
||||
return loadImages(imagePaths, objPath, options)
|
||||
var imagesOptions = getImagesOptions(materials, options);
|
||||
return loadImages(imagesOptions, objPath, options)
|
||||
.then(function(images) {
|
||||
return {
|
||||
nodes : nodes,
|
||||
@ -325,16 +325,17 @@ function loadMaterials(mtlPaths, objPath, options) {
|
||||
.thenReturn(materials);
|
||||
}
|
||||
|
||||
function loadImages(imagePaths, objPath, options) {
|
||||
function loadImages(imagesOptions, objPath, options) {
|
||||
var secure = options.secure;
|
||||
var logger = options.logger;
|
||||
var images = {};
|
||||
return Promise.map(imagePaths, function(imagePath) {
|
||||
return Promise.map(imagesOptions, function(imageOptions) {
|
||||
var imagePath = imageOptions.imagePath;
|
||||
if (secure && outsideDirectory(imagePath, objPath)) {
|
||||
logger('Could not read image file at ' + imagePath + ' because it is outside of the obj directory and the secure flag is true. Material will ignore this image.');
|
||||
return;
|
||||
}
|
||||
return loadImage(imagePath, options)
|
||||
return loadImage(imagePath, imageOptions)
|
||||
.then(function(image) {
|
||||
images[imagePath] = image;
|
||||
})
|
||||
@ -345,26 +346,47 @@ function loadImages(imagePaths, objPath, options) {
|
||||
.thenReturn(images);
|
||||
}
|
||||
|
||||
function getImagePaths(materials) {
|
||||
var imagePaths = {};
|
||||
function getImagesOptions(materials, options) {
|
||||
var imagesOptions = [];
|
||||
for (var name in materials) {
|
||||
if (materials.hasOwnProperty(name)) {
|
||||
var material = materials[name];
|
||||
if (defined(material.ambientTexture)) {
|
||||
imagePaths[material.ambientTexture] = true;
|
||||
imagesOptions.push({
|
||||
imagePath : material.ambientTexture
|
||||
});
|
||||
}
|
||||
if (defined(material.emissiveTexture)) {
|
||||
imagesOptions.push({
|
||||
imagePath : material.emissiveTexture
|
||||
});
|
||||
}
|
||||
if (defined(material.diffuseTexture)) {
|
||||
imagePaths[material.diffuseTexture] = true;
|
||||
}
|
||||
if (defined(material.emissionTexture)) {
|
||||
imagePaths[material.emissionTexture] = true;
|
||||
imagesOptions.push({
|
||||
imagePath : material.diffuseTexture,
|
||||
checkTransparency : options.checkTransparency
|
||||
});
|
||||
}
|
||||
if (defined(material.specularTexture)) {
|
||||
imagePaths[material.specularTexture] = true;
|
||||
imagesOptions.push({
|
||||
imagePath : material.specularTexture,
|
||||
decode : true
|
||||
});
|
||||
}
|
||||
if (defined(material.specularShininessTexture)) {
|
||||
imagesOptions.push({
|
||||
imagePath : material.specularShininessTexture,
|
||||
decode : true
|
||||
});
|
||||
}
|
||||
if (defined(material.normalTexture)) {
|
||||
imagesOptions.push({
|
||||
imagePath : material.normalTexture
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return Object.keys(imagePaths);
|
||||
return imagesOptions;
|
||||
}
|
||||
|
||||
function removeEmptyMeshes(meshes) {
|
||||
|
@ -107,7 +107,7 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
|
||||
return loadObj(objPath, options)
|
||||
.then(function(objData) {
|
||||
return createGltf(objData);
|
||||
return createGltf(objData, options);
|
||||
})
|
||||
.then(function(gltf) {
|
||||
return writeUris(gltf, gltfPath, options);
|
||||
|
@ -29,15 +29,14 @@ function writeUris(gltf, gltfPath, options) {
|
||||
|
||||
var promises = [];
|
||||
|
||||
var buffer = gltf.buffers[Object.keys(gltf.buffers)[0]];
|
||||
var buffer = gltf.buffers[0];
|
||||
var bufferByteLength = buffer.extras._obj2gltf.source.length;
|
||||
|
||||
var texturesByteLength = 0;
|
||||
var images = gltf.images;
|
||||
for (var id in images) {
|
||||
if (images.hasOwnProperty(id)) {
|
||||
texturesByteLength += images[id].extras._obj2gltf.source.length;
|
||||
}
|
||||
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
|
||||
@ -67,20 +66,24 @@ function writeUris(gltf, gltfPath, options) {
|
||||
}
|
||||
|
||||
function deleteExtras(gltf) {
|
||||
var buffer = gltf.buffers[Object.keys(gltf.buffers)[0]];
|
||||
var buffer = gltf.buffers[0];
|
||||
delete buffer.extras;
|
||||
|
||||
var images = gltf.images;
|
||||
for (var id in images) {
|
||||
if (images.hasOwnProperty(id)) {
|
||||
var image = images[id];
|
||||
delete image.extras;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function writeSeparateBuffer(gltf, gltfPath) {
|
||||
var buffer = gltf.buffers[Object.keys(gltf.buffers)[0]];
|
||||
var buffer = gltf.buffers[0];
|
||||
var source = buffer.extras._obj2gltf.source;
|
||||
var bufferName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
var bufferUri = bufferName + '.bin';
|
||||
@ -91,8 +94,7 @@ function writeSeparateBuffer(gltf, gltfPath) {
|
||||
|
||||
function writeSeparateTextures(gltf, gltfPath) {
|
||||
var images = gltf.images;
|
||||
return Promise.map(Object.keys(images), function(id) {
|
||||
var image = images[id];
|
||||
return Promise.map(images, function(image) {
|
||||
var extras = image.extras._obj2gltf;
|
||||
var imageUri = image.name + extras.extension;
|
||||
image.uri = imageUri;
|
||||
@ -102,20 +104,19 @@ function writeSeparateTextures(gltf, gltfPath) {
|
||||
}
|
||||
|
||||
function writeEmbeddedBuffer(gltf) {
|
||||
var buffer = gltf.buffers[Object.keys(gltf.buffers)[0]];
|
||||
var buffer = gltf.buffers[0];
|
||||
var source = buffer.extras._obj2gltf.source;
|
||||
buffer.uri = 'data:application/octet-stream;base64,' + source.toString('base64');
|
||||
}
|
||||
|
||||
function writeEmbeddedTextures(gltf) {
|
||||
var images = gltf.images;
|
||||
for (var id in images) {
|
||||
if (images.hasOwnProperty(id)) {
|
||||
var image = images[id];
|
||||
var imagesLength = images.length;
|
||||
for (var i = 0; i < imagesLength; ++i) {
|
||||
var image = images[i];
|
||||
var extras = image.extras._obj2gltf;
|
||||
image.uri = 'data:' + mime.lookup(extras.extension) + ';base64,' + extras.source.toString('base64');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,7 @@
|
||||
"event-stream": "^3.3.4",
|
||||
"fs-extra": "^2.0.0",
|
||||
"gltf-pipeline": "^0.1.0-alpha11",
|
||||
"jpeg-js": "^0.2.0",
|
||||
"mime": "^1.3.4",
|
||||
"pngjs": "^3.0.1",
|
||||
"yargs": "^7.0.1"
|
||||
|
@ -26,7 +26,7 @@ var defaultOptions = obj2gltf.defaults;
|
||||
var checkTransparencyOptions = clone(defaultOptions);
|
||||
checkTransparencyOptions.checkTransparency = true;
|
||||
|
||||
describe('gltf', function() {
|
||||
describe('createGltf', function() {
|
||||
var boxObjData;
|
||||
var duplicateBoxObjData;
|
||||
var groupObjData;
|
||||
@ -69,7 +69,7 @@ describe('gltf', function() {
|
||||
});
|
||||
|
||||
it('simple gltf', function(done) {
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
expect(writeUris(gltf, boxGltfUrl, defaultOptions)
|
||||
.then(function() {
|
||||
expect(gltf).toEqual(boxGltf);
|
||||
@ -77,7 +77,7 @@ describe('gltf', function() {
|
||||
});
|
||||
|
||||
it('multiple nodes, meshes, and primitives', function(done) {
|
||||
var gltf = createGltf(groupObjData);
|
||||
var gltf = createGltf(groupObjData, defaultOptions);
|
||||
|
||||
expect(writeUris(gltf, groupGltfUrl, defaultOptions)
|
||||
.then(function() {
|
||||
@ -99,7 +99,7 @@ describe('gltf', function() {
|
||||
it('sets default material values', function() {
|
||||
boxObjData.materials.Material = new Material();
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var material = gltf.materials.Material;
|
||||
var kmc = material.extensions.KHR_materials_common;
|
||||
var values = kmc.values;
|
||||
@ -118,7 +118,7 @@ describe('gltf', function() {
|
||||
boxObjData.materials.Material = material;
|
||||
boxObjData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
var texture = gltf.textures.texture_cesium;
|
||||
var image = gltf.images.cesium;
|
||||
@ -156,7 +156,7 @@ describe('gltf', function() {
|
||||
material.alpha = 0.4;
|
||||
boxObjData.materials.Material = material;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 0.4]);
|
||||
@ -173,7 +173,7 @@ describe('gltf', function() {
|
||||
|
||||
boxObjData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual('texture_cesium');
|
||||
@ -189,7 +189,7 @@ describe('gltf', function() {
|
||||
|
||||
boxObjData.images[transparentDiffuseTextureUrl] = transparentDiffuseTexture;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toBe('texture_diffuse');
|
||||
@ -204,7 +204,7 @@ describe('gltf', function() {
|
||||
material.specularShininess = 0.1;
|
||||
boxObjData.materials.Material = material;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.technique).toBe('PHONG');
|
||||
@ -221,7 +221,7 @@ describe('gltf', function() {
|
||||
|
||||
boxObjData.images[diffuseTextureUrl] = diffuseTexture;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.technique).toBe('CONSTANT');
|
||||
@ -233,7 +233,7 @@ describe('gltf', function() {
|
||||
material.diffuseTexture = diffuseTextureUrl;
|
||||
boxObjData.materials.Material = material;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
@ -243,7 +243,7 @@ describe('gltf', function() {
|
||||
boxObjData.nodes[0].meshes[0].primitives[0].material = undefined;
|
||||
|
||||
// Creates a material called "default"
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
expect(gltf.materials.default).toBeDefined();
|
||||
var kmc = gltf.materials.default.extensions.KHR_materials_common;
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
@ -253,7 +253,7 @@ describe('gltf', function() {
|
||||
boxObjData.materials = {};
|
||||
|
||||
// Uses the original name of the material
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
|
||||
expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
@ -264,7 +264,7 @@ describe('gltf', function() {
|
||||
boxObjData.nodes.push(duplicateBoxObjData.nodes[0]);
|
||||
boxObjData.nodes[1].meshes[0].normals.length = 0;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
var kmc2 = gltf.materials.Material_constant.extensions.KHR_materials_common;
|
||||
|
||||
@ -277,7 +277,7 @@ describe('gltf', function() {
|
||||
boxObjData.nodes.push(duplicateBoxObjData.nodes[0]);
|
||||
boxObjData.nodes[0].meshes[0].normals.length = 0;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var kmc1 = gltf.materials.Material.extensions.KHR_materials_common;
|
||||
var kmc2 = gltf.materials.Material_shaded.extensions.KHR_materials_common;
|
||||
|
||||
@ -288,7 +288,7 @@ describe('gltf', function() {
|
||||
it('runs without normals', function() {
|
||||
boxObjData.nodes[0].meshes[0].normals.length = 0;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeUndefined();
|
||||
@ -298,7 +298,7 @@ describe('gltf', function() {
|
||||
it('runs without uvs', function() {
|
||||
boxObjData.nodes[0].meshes[0].uvs.length = 0;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeDefined();
|
||||
@ -309,7 +309,7 @@ describe('gltf', function() {
|
||||
boxObjData.nodes[0].meshes[0].normals.length = 0;
|
||||
boxObjData.nodes[0].meshes[0].uvs.length = 0;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var attributes = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0].attributes;
|
||||
expect(attributes.POSITION).toBeDefined();
|
||||
expect(attributes.NORMAL).toBeUndefined();
|
||||
@ -349,7 +349,7 @@ describe('gltf', function() {
|
||||
var indicesLength = mesh.primitives[0].indices.length;
|
||||
var vertexCount = mesh.positions.length / 3;
|
||||
|
||||
var gltf = createGltf(boxObjData);
|
||||
var gltf = createGltf(boxObjData, defaultOptions);
|
||||
var primitive = gltf.meshes[Object.keys(gltf.meshes)[0]].primitives[0];
|
||||
var indicesAccessor = gltf.accessors[primitive.indices];
|
||||
expect(indicesAccessor.count).toBe(indicesLength);
|
@ -4,7 +4,6 @@ var obj2gltf = require('../../lib/obj2gltf');
|
||||
var loadImage = require('../../lib/loadImage');
|
||||
|
||||
var clone = Cesium.clone;
|
||||
var WebGLConstants = Cesium.WebGLConstants;
|
||||
|
||||
var pngImage = 'specs/data/box-complex-material/shininess.png';
|
||||
var jpgImage = 'specs/data/box-complex-material/emission.jpg';
|
||||
@ -15,12 +14,11 @@ var transparentImage = 'specs/data/box-complex-material/diffuse.png';
|
||||
|
||||
var defaultOptions = obj2gltf.defaults;
|
||||
|
||||
describe('image', function() {
|
||||
describe('loadImage', function() {
|
||||
it('loads png image', function(done) {
|
||||
expect(loadImage(pngImage, defaultOptions)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
expect(info.source).toBeDefined();
|
||||
expect(info.extension).toBe('.png');
|
||||
}), done).toResolve();
|
||||
@ -30,7 +28,6 @@ describe('image', function() {
|
||||
expect(loadImage(jpgImage, defaultOptions)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
expect(info.source).toBeDefined();
|
||||
expect(info.extension).toBe('.jpg');
|
||||
}), done).toResolve();
|
||||
@ -40,7 +37,6 @@ describe('image', function() {
|
||||
expect(loadImage(jpegImage, defaultOptions)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
expect(info.source).toBeDefined();
|
||||
expect(info.extension).toBe('.jpeg');
|
||||
}), done).toResolve();
|
||||
@ -50,7 +46,6 @@ describe('image', function() {
|
||||
expect(loadImage(gifImage, defaultOptions)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.format).toBe(WebGLConstants.RGB);
|
||||
expect(info.source).toBeDefined();
|
||||
expect(info.extension).toBe('.gif');
|
||||
}), done).toResolve();
|
||||
@ -60,7 +55,6 @@ describe('image', function() {
|
||||
expect(loadImage(grayscaleImage, defaultOptions)
|
||||
.then(function(info) {
|
||||
expect(info.transparent).toBe(false);
|
||||
expect(info.format).toBe(WebGLConstants.ALPHA);
|
||||
expect(info.source).toBeDefined();
|
||||
expect(info.extension).toBe('.png');
|
||||
}), done).toResolve();
|
@ -9,25 +9,25 @@ function getImagePath(objPath, relativePath) {
|
||||
return path.resolve(path.dirname(objPath), relativePath);
|
||||
}
|
||||
|
||||
describe('mtl', function() {
|
||||
describe('loadMtl', function() {
|
||||
it('loads complex material', function(done) {
|
||||
expect(loadMtl(complexMaterialUrl)
|
||||
.then(function(materials) {
|
||||
var material = materials.Material;
|
||||
expect(material).toBeDefined();
|
||||
expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]);
|
||||
expect(material.emissionColor).toEqual([0.1, 0.1, 0.1, 1.0]);
|
||||
expect(material.emissiveColor).toEqual([0.1, 0.1, 0.1, 1.0]);
|
||||
expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]);
|
||||
expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]);
|
||||
expect(material.specularShininess).toEqual(96.078431);
|
||||
expect(material.alpha).toEqual(0.9);
|
||||
expect(material.ambientTexture).toEqual(getImagePath(complexMaterialUrl, 'ambient.gif'));
|
||||
expect(material.emissionTexture).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg'));
|
||||
expect(material.emissiveTexture).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg'));
|
||||
expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialUrl, 'diffuse.png'));
|
||||
expect(material.specularTexture).toEqual(getImagePath(complexMaterialUrl, 'specular.jpeg'));
|
||||
expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialUrl, 'shininess.png'));
|
||||
expect(material.normalMap).toEqual(getImagePath(complexMaterialUrl, 'bump.png'));
|
||||
expect(material.alphaMap).toEqual(getImagePath(complexMaterialUrl, 'alpha.png'));
|
||||
expect(material.specularShininessTexture).toEqual(getImagePath(complexMaterialUrl, 'shininess.png'));
|
||||
expect(material.normalTexture).toEqual(getImagePath(complexMaterialUrl, 'bump.png'));
|
||||
expect(material.alphaTexture).toEqual(getImagePath(complexMaterialUrl, 'alpha.png'));
|
||||
}), done).toResolve();
|
||||
});
|
||||
|
@ -61,7 +61,7 @@ function getImagePath(objPath, relativePath) {
|
||||
|
||||
var defaultOptions = obj2gltf.defaults;
|
||||
|
||||
describe('obj', function() {
|
||||
describe('loadObj', function() {
|
||||
it('loads obj with positions, normals, and uvs', function(done) {
|
||||
expect(loadObj(objUrl, defaultOptions)
|
||||
.then(function(data) {
|
Loading…
x
Reference in New Issue
Block a user