obj2gltf/lib/createGltf.js

869 lines
28 KiB
JavaScript
Raw Normal View History

2017-03-13 15:28:51 -04:00
'use strict';
2016-07-22 14:09:13 -04:00
var Cesium = require('cesium');
2015-10-16 17:32:23 -04:00
var path = require('path');
2017-04-18 11:56:08 -04:00
var PNG = require('pngjs').PNG;
2017-04-10 17:57:56 -04:00
var Material = require('./Material');
2016-07-22 14:09:13 -04:00
var CesiumMath = Cesium.Math;
2017-05-04 15:39:01 -04:00
var defaultValue = Cesium.defaultValue;
2016-06-09 13:33:08 -04:00
var defined = Cesium.defined;
var WebGLConstants = Cesium.WebGLConstants;
2015-10-16 17:32:23 -04:00
module.exports = createGltf;
2017-03-13 15:28:51 -04:00
/**
* Create a glTF from obj data.
*
* @param {Object} objData Output of obj.js, containing an array of nodes containing geometry information, materials, and images.
2017-04-18 11:56:08 -04:00
* @param {Object} options An object with the following properties:
2017-05-04 17:58:13 -04:00
* @param {Boolean} options.packOcclusion Pack the occlusion texture in the red channel of metallic-roughness texture.
* @param {Boolean} options.metallicRoughness 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 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 The glTF will be saved with the KHR_materials_common extension.
2017-04-18 11:56:08 -04:00
* @param {Boolean} options.logger A callback function for handling logged messages. Defaults to console.log.
* @returns {Object} A glTF asset.
2017-03-13 15:28:51 -04:00
*
* @private
*/
2017-04-18 11:56:08 -04:00
function createGltf(objData, options) {
2017-03-13 15:28:51 -04:00
var nodes = objData.nodes;
var materials = objData.materials;
var images = objData.images;
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var gltf = {
2017-04-18 11:56:08 -04:00
accessors : [],
2016-06-09 13:33:08 -04:00
asset : {},
2017-04-18 11:56:08 -04:00
buffers : [],
bufferViews : [],
2017-05-04 15:39:01 -04:00
extensionsUsed : [],
extensionsRequired : [],
2017-04-18 11:56:08 -04:00
images : [],
materials : [],
meshes : [],
nodes : [],
samplers : [],
scene : 0,
scenes : [],
textures : []
2016-06-09 13:33:08 -04:00
};
gltf.asset = {
2017-03-13 15:28:51 -04:00
generator : 'obj2gltf',
2017-04-18 11:56:08 -04:00
version: '2.0'
2016-06-09 13:33:08 -04:00
};
2017-04-18 11:56:08 -04:00
gltf.scenes.push({
2017-03-13 15:28:51 -04:00
nodes : []
2017-04-18 11:56:08 -04:00
});
var bufferState = {
vertexBuffers : [],
vertexBufferByteOffset : 0,
vertexBufferViewIndex : 0,
indexBuffers : [],
indexBufferByteOffset : 0,
indexBufferViewIndex : 1
2016-06-09 13:33:08 -04:00
};
2017-04-18 11:56:08 -04:00
var uint32Indices = requiresUint32Indices(nodes);
2016-06-09 13:33:08 -04:00
2017-04-18 11:56:08 -04:00
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;
2017-03-13 15:28:51 -04:00
2017-04-18 11:56:08 -04:00
if (meshesLength === 1) {
meshIndex = addMesh(gltf, materials, images, bufferState, uint32Indices, meshes[0], options);
addNode(gltf, node.name, meshIndex);
} else {
// 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);
2017-03-13 15:28:51 -04:00
}
2017-04-18 11:56:08 -04:00
}
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
if (gltf.images.length > 0) {
2017-04-18 11:56:08 -04:00
gltf.samplers.push({
2017-03-13 15:28:51 -04:00
magFilter : WebGLConstants.LINEAR,
minFilter : WebGLConstants.LINEAR,
wrapS : WebGLConstants.REPEAT,
wrapT : WebGLConstants.REPEAT
2017-04-18 11:56:08 -04:00
});
2017-03-13 15:28:51 -04:00
}
2017-04-18 11:56:08 -04:00
addBuffers(gltf, bufferState);
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
2017-03-13 15:28:51 -04:00
}
2015-10-16 17:32:23 -04:00
}
2017-04-18 11:56:08 -04:00
});
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) {
var imagesLength = images.length;
for (var i = 0; i < imagesLength; ++i) {
var image = images[i];
if (image.path === imagePath) {
return image;
}
2016-06-09 13:33:08 -04:00
}
return undefined;
2017-04-18 11:56:08 -04:00
}
2015-10-16 17:32:23 -04:00
function getImageName(image) {
return path.basename(image.path, image.extension);
2017-04-18 11:56:08 -04:00
}
2017-03-13 15:28:51 -04:00
function getTextureName(image) {
return getImageName(image);
2017-04-18 11:56:08 -04:00
}
2017-03-13 15:28:51 -04:00
function addTexture(gltf, image) {
var imageName = getImageName(image);
var textureName = getTextureName(image);
2017-04-18 11:56:08 -04:00
var imageIndex = gltf.images.length;
var textureIndex = gltf.textures.length;
2016-06-09 13:33:08 -04:00
2017-04-18 11:56:08 -04:00
gltf.images.push({
name : imageName,
extras : {
_obj2gltf : {
source : image.source,
extension : image.extension
2017-03-13 15:28:51 -04:00
}
2016-06-09 13:33:08 -04:00
}
2017-04-18 11:56:08 -04:00
});
gltf.textures.push({
name : textureName,
sampler : 0,
source : imageIndex
});
return textureIndex;
}
function getTexture(gltf, image) {
if (!defined(image)) {
return undefined;
}
var textureIndex;
var name = getTextureName(image);
2017-04-18 11:56:08 -04:00
var textures = gltf.textures;
var length = textures.length;
for (var i = 0; i < length; ++i) {
if (textures[i].name === name) {
textureIndex = i;
break;
2017-04-18 11:56:08 -04:00
}
2016-06-09 13:33:08 -04:00
}
2017-04-18 11:56:08 -04:00
if (!defined(textureIndex)) {
textureIndex = addTexture(gltf, image);
2017-04-18 11:56:08 -04:00
}
return textureIndex;
}
2017-03-13 15:28:51 -04:00
2017-04-18 11:56:08 -04:00
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 getEmissiveFactor(material) {
// If ambient color is [1, 1, 1] assume it is a multiplier and instead change to [0, 0, 0]
// Then add the ambient color to the emissive color to get the emissive factor.
var ambientColor = material.ambientColor;
var emissiveColor = material.emissiveColor;
if (ambientColor[0] === 1.0 && ambientColor[1] === 1.0 && ambientColor[2] === 1.0) {
ambientColor = [0.0, 0.0, 0.0, 1.0];
}
return addColors(ambientColor, emissiveColor);
}
2017-04-18 11:56:08 -04:00
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;
}
function getImageChannel(image, index, targetWidth, targetHeight) {
2017-04-18 11:56:08 -04:00
var pixels = image.decoded; // RGBA
var width = image.width;
var height = image.height;
var pixelsLength = width * height;
var channel = Buffer.alloc(pixelsLength);
2017-04-18 11:56:08 -04:00
for (var i = 0; i < pixelsLength; ++i) {
var value = pixels.readUInt8(i * 4 + index);
channel.writeUInt8(value, i);
2017-04-18 11:56:08 -04:00
}
if (width !== targetWidth || height !== targetHeight) {
channel = resizeChannel(channel, width, height, targetWidth, targetHeight);
2017-04-18 11:56:08 -04:00
}
return channel;
2017-04-18 11:56:08 -04:00
}
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 getMinimumDimensions(images, options) {
var i;
var image;
var width = Number.POSITIVE_INFINITY;
var height = Number.POSITIVE_INFINITY;
var length = images.length;
for (i = 0; i < length; ++i) {
image = images[i];
if (defined(image)) {
width = Math.min(image.width, width);
height = Math.min(image.height, height);
}
}
for (i = 0; i < length; ++i) {
image = images[i];
if (defined(image)) {
if (image.width !== width || image.height !== height) {
options.logger('Image ' + image.path + ' will be scaled from ' + image.width + 'x' + image.height + ' to ' + width + 'x' + height + '.');
}
}
2017-04-18 11:56:08 -04:00
}
return [width, height];
}
function encodePng(pixels, width, height, inputChannels, outputChannels) {
var pngInput = {
data : pixels,
width : width,
height : height
};
// Constants defined by pngjs
var rgbColorType = 2;
var rgbaColorType = 4;
var colorType = outputChannels === 4 ? rgbaColorType : rgbColorType;
var inputColorType = inputChannels === 4 ? rgbaColorType : rgbColorType;
var inputHasAlpha = inputChannels === 4;
var pngOptions = {
width : width,
height : height,
colorType : colorType,
inputColorType : inputColorType,
inputHasAlpha : inputHasAlpha
};
return PNG.sync.write(pngInput, pngOptions);
}
function createMetallicRoughnessTexture(gltf, materialName, metallicImage, roughnessImage, occlusionImage, options) {
var packMetallic = defined(metallicImage);
var packRoughness = defined(roughnessImage);
var packOcclusion = defined(occlusionImage) && options.packOcclusion;
if (!packMetallic && !packRoughness) {
2017-04-18 11:56:08 -04:00
return undefined;
}
if (packMetallic && !defined(metallicImage.decoded)) {
options.logger('Could not get decoded image data for ' + metallicImage.path + '. The material will be created without a metallicRoughness texture.');
2017-04-18 11:56:08 -04:00
return undefined;
}
if (packRoughness && !defined(roughnessImage.decoded)) {
options.logger('Could not get decoded image data for ' + roughnessImage.path + '. The material will be created without a metallicRoughness texture.');
return undefined;
}
2017-04-18 11:56:08 -04:00
if (packOcclusion && !defined(occlusionImage.decoded)) {
options.logger('Could not get decoded image data for ' + occlusionImage.path + '. The occlusion texture will not be packed in the metallicRoughness texture.');
return undefined;
2017-04-18 11:56:08 -04:00
}
var dimensions = getMinimumDimensions([metallicImage, roughnessImage, occlusionImage], options);
var width = dimensions[0];
var height = dimensions[1];
2017-04-18 11:56:08 -04:00
var pixelsLength = width * height;
var pixels = Buffer.alloc(pixelsLength * 4, 0xFF); // Initialize with 4 channels, unused channels will be white
if (packMetallic) {
2017-04-18 11:56:08 -04:00
// Write into the B channel
var metallicChannel = getImageChannel(metallicImage, 0, width, height);
2017-04-18 11:56:08 -04:00
writeChannel(pixels, metallicChannel, 2, width, height);
}
if (packRoughness) {
2017-04-18 11:56:08 -04:00
// Write into the G channel
var roughnessChannel = getImageChannel(roughnessImage, 0, width, height);
2017-04-18 11:56:08 -04:00
writeChannel(pixels, roughnessChannel, 1, width, height);
}
if (packOcclusion) {
// Write into the R channel
var occlusionChannel = getImageChannel(occlusionImage, 0, width, height);
writeChannel(pixels, occlusionChannel, 0, width, height);
}
2017-04-18 11:56:08 -04:00
var imageName = materialName + '-' + 'MetallicRoughness';
if (packOcclusion) {
imageName += 'Occlusion';
}
var pngSource = encodePng(pixels, width, height, 4, 3);
var image = {
transparent : false,
source : pngSource,
path : imageName,
extension : '.png'
2017-04-18 11:56:08 -04:00
};
return addTexture(gltf, image);
}
function createSpecularGlossinessTexture(gltf, materialName, specularImage, glossinessImage, options) {
var packSpecular = defined(specularImage);
var packGlossiness = defined(glossinessImage);
if (!packSpecular && !packGlossiness) {
return undefined;
}
if (packSpecular && !defined(specularImage.decoded)) {
options.logger('Could not get decoded image data for ' + specularImage.path + '. The material will be created without a specularGlossiness texture.');
return undefined;
}
if (packGlossiness && !defined(glossinessImage.decoded)) {
options.logger('Could not get decoded image data for ' + glossinessImage.path + '. The material will be created without a specularGlossiness texture.');
return undefined;
}
var dimensions = getMinimumDimensions([specularImage, glossinessImage], options);
var width = dimensions[0];
var height = dimensions[1];
var pixelsLength = width * height;
var pixels = Buffer.alloc(pixelsLength * 4, 0xFF); // Initialize with 4 channels, unused channels will be white
if (packSpecular) {
// Write into the R, G, B channels
var redChannel = getImageChannel(specularImage, 0, width, height);
var greenChannel = getImageChannel(specularImage, 1, width, height);
var blueChannel = getImageChannel(specularImage, 2, width, height);
writeChannel(pixels, redChannel, 0, width, height);
writeChannel(pixels, greenChannel, 1, width, height);
writeChannel(pixels, blueChannel, 2, width, height);
}
if (packGlossiness) {
// Write into the A channel
var glossinessChannel = getImageChannel(glossinessImage, 0, width, height);
writeChannel(pixels, glossinessChannel, 3, width, height);
}
var imageName = materialName + '-' + 'SpecularGlossiness';
var pngSource = encodePng(pixels, width, height, 4, 4);
2017-04-18 11:56:08 -04:00
var image = {
transparent : false,
source : pngSource,
path : imageName,
2017-04-18 11:56:08 -04:00
extension : '.png'
};
return addTexture(gltf, image);
2017-04-18 11:56:08 -04:00
}
function createSpecularGlossinessMaterial(gltf, images, material, options) {
var materialName = material.name;
var emissiveImage = getImage(images, material.emissiveTexture);
var normalImage = getImage(images, material.normalTexture);
var occlusionImage = getImage(images, material.ambientTexture);
var diffuseImage = getImage(images, material.diffuseTexture);
var specularImage = getImage(images, material.specularTexture);
var glossinessImage = getImage(images, material.specularShininessTexture);
var emissiveTexture = getTexture(gltf, emissiveImage);
var normalTexture = getTexture(gltf, normalImage);
var occlusionTexture = getTexture(gltf, occlusionImage);
var diffuseTexture = getTexture(gltf, diffuseImage);
var specularGlossinessTexture = createSpecularGlossinessTexture(gltf, materialName, specularImage, glossinessImage, options);
var emissiveFactor = getEmissiveFactor(material);
var diffuseFactor = material.diffuseColor;
var specularFactor = material.specularColor;
var glossinessFactor = material.specularShininess;
if (defined(emissiveTexture)) {
emissiveFactor = [1.0, 1.0, 1.0];
}
if (defined(diffuseTexture)) {
diffuseFactor = [1.0, 1.0, 1.0, 1.0];
}
if (defined(specularImage)) {
specularFactor = 1.0;
}
if (defined(glossinessImage)) {
glossinessFactor = 1.0;
}
var alpha = material.alpha;
diffuseFactor[3] = alpha;
var transparent = alpha < 1.0;
if (defined(diffuseImage)) {
transparent |= diffuseImage.transparent;
}
var doubleSided = transparent;
var alphaMode = transparent ? 'BLEND' : 'OPAQUE';
2017-05-04 15:39:01 -04:00
gltf.extensionsUsed.push('KHR_materials_pbrSpecularGlossiness');
gltf.extensionsRequired.push('KHR_materials_pbrSpecularGlossiness');
return {
name : materialName,
extensions : {
KHR_materials_pbrSpecularGlossiness: {
diffuseTexture : diffuseTexture,
specularGlossinessTexture : specularGlossinessTexture,
diffuseFactor : diffuseFactor,
specularFactor : specularFactor,
glossinessFactor : glossinessFactor
}
},
emissiveTexture : emissiveTexture,
normalTexture : normalTexture,
occlusionTexture : occlusionTexture,
emissiveFactor : emissiveFactor,
alphaMode : alphaMode,
doubleSided : doubleSided
};
}
2017-04-18 11:56:08 -04:00
function createMetallicRoughnessMaterial(gltf, images, material, options) {
var materialName = material.name;
2017-04-18 11:56:08 -04:00
var emissiveImage = getImage(images, material.emissiveTexture);
var normalImage = getImage(images, material.normalTexture);
var occlusionImage = getImage(images, material.ambientTexture);
var baseColorImage = getImage(images, material.diffuseTexture);
2017-04-18 11:56:08 -04:00
var metallicImage = getImage(images, material.specularTexture);
var roughnessImage = getImage(images, material.specularShininessTexture);
var emissiveTexture = getTexture(gltf, emissiveImage);
var normalTexture = getTexture(gltf, normalImage);
var baseColorTexture = getTexture(gltf, baseColorImage);
var metallicRoughnessTexture = createMetallicRoughnessTexture(gltf, materialName, metallicImage, roughnessImage, occlusionImage, options);
var packOcclusion = defined(occlusionImage) || options.packOcclusion;
var occlusionTexture = packOcclusion ? metallicRoughnessTexture : getTexture(gltf, occlusionImage);
var emissiveFactor = getEmissiveFactor(material);
var baseColorFactor = material.diffuseColor;
var metallicFactor = material.specularColor[0];
var roughnessFactor = material.specularShininess;
2017-04-18 11:56:08 -04:00
if (defined(emissiveTexture)) {
emissiveFactor = [1.0, 1.0, 1.0];
2017-04-18 11:56:08 -04:00
}
if (defined(baseColorTexture)) {
baseColorFactor = [1.0, 1.0, 1.0, 1.0];
2017-04-18 11:56:08 -04:00
}
if (defined(metallicImage)) {
metallicFactor = 1.0;
2017-04-18 11:56:08 -04:00
}
if (defined(roughnessImage)) {
roughnessFactor = 1.0;
2017-04-18 11:56:08 -04:00
}
var alpha = material.alpha;
baseColorFactor[3] = alpha;
var transparent = alpha < 1.0;
if (defined(baseColorImage)) {
transparent |= baseColorImage.transparent;
2017-04-18 11:56:08 -04:00
}
var doubleSided = transparent;
var alphaMode = transparent ? 'BLEND' : 'OPAQUE';
2017-05-04 15:39:01 -04:00
return {
name : materialName,
2017-04-18 11:56:08 -04:00
pbrMetallicRoughness : {
baseColorTexture : baseColorTexture,
metallicRoughnessTexture : metallicRoughnessTexture,
2017-04-18 11:56:08 -04:00
baseColorFactor : baseColorFactor,
metallicFactor : metallicFactor,
roughnessFactor : roughnessFactor
2017-04-18 11:56:08 -04:00
},
emissiveTexture : emissiveTexture,
normalTexture : normalTexture,
occlusionTexture : occlusionTexture,
2017-04-18 11:56:08 -04:00
emissiveFactor : emissiveFactor,
alphaMode : alphaMode,
doubleSided : doubleSided
2017-03-13 15:28:51 -04:00
};
}
function convertTraditionalToMetallicRoughness(material) {
// Translate the blinn-phong model to the pbr metallic-roughness model
// Roughness factor is a combination of specular intensity and shininess
// Metallic factor is 0.0
2017-05-04 15:39:01 -04:00
// This does not convert textures
var specularIntensity = material.specularColor[0];
var specularShininess = material.specularShininess;
// Transform from 0-1000 range to 0-1 range. Then invert.
var roughnessFactor = specularShininess;
roughnessFactor = roughnessFactor / 1000.0;
roughnessFactor = 1.0 - roughnessFactor;
roughnessFactor = CesiumMath.clamp(roughnessFactor, 0.0, 1.0);
// Low specular intensity values should produce a rough material even if shininess is high.
if (specularIntensity < 0.1) {
2017-05-04 17:58:13 -04:00
roughnessFactor *= (1.0 - specularIntensity);
}
var metallicFactor = 0.0;
2017-05-04 17:58:13 -04:00
material.specularTexture = undefined; // For now just ignore the specular texture
material.specularColor = [metallicFactor, metallicFactor, metallicFactor, 1.0];
2017-05-04 17:58:13 -04:00
material.specularShininess = roughnessFactor;
}
2017-05-04 17:58:13 -04:00
function createMaterialsCommonMaterial(gltf, images, material, hasNormals) {
2017-05-04 15:39:01 -04:00
var materialName = material.name;
var ambientImage = getImage(images, material.ambientTexture);
var diffuseImage = getImage(images, material.diffuseTexture);
var emissiveImage = getImage(images, material.emissiveTexture);
var specularImage = getImage(images, material.specularTexture);
var ambient = defaultValue(getTexture(gltf, ambientImage), material.ambientColor);
var diffuse = defaultValue(getTexture(gltf, diffuseImage), material.diffuseColor);
var emission = defaultValue(getTexture(gltf, emissiveImage), material.emissiveColor);
var specular = defaultValue(getTexture(gltf, specularImage), material.specularColor);
var alpha = material.alpha;
var shininess = material.specularShininess;
var hasSpecular = (shininess > 0.0) && (specular[0] > 0.0 || specular[1] > 0.0 || specular[2] > 0.0);
var transparent;
var transparency = 1.0;
if (defined(diffuseImage)) {
transparency = alpha;
transparent = diffuseImage.transparent || (transparency < 1.0);
} else {
diffuse[3] = alpha;
transparent = alpha < 1.0;
}
if (!defined(ambientImage)) {
// If ambient color is [1, 1, 1] assume it is a multiplier and instead change to [0, 0, 0]
if (ambient[0] === 1.0 && ambient[1] === 1.0 && ambient[2] === 1.0) {
ambient = [0.0, 0.0, 0.0, 1.0];
}
}
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';
gltf.extensionsUsed.push('KHR_materials_common');
gltf.extensionsRequired.push('KHR_materials_common');
return {
name : materialName,
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
}
}
}
};
}
function addMaterial(gltf, images, material, hasNormals, options) {
var gltfMaterial;
2017-05-04 15:39:01 -04:00
if (options.specularGlossiness) {
gltfMaterial = createSpecularGlossinessMaterial(gltf, images, material, options);
2017-05-04 15:39:01 -04:00
} else if (options.metallicRoughness) {
gltfMaterial = createMetallicRoughnessMaterial(gltf, images, material, options);
2017-05-04 15:39:01 -04:00
} else if (options.materialsCommon) {
2017-05-04 17:58:13 -04:00
gltfMaterial = createMaterialsCommonMaterial(gltf, images, material, hasNormals);
} else {
convertTraditionalToMetallicRoughness(material);
gltfMaterial = createMetallicRoughnessMaterial(gltf, images, material, options);
}
2017-04-18 11:56:08 -04:00
var materialIndex = gltf.materials.length;
gltf.materials.push(gltfMaterial);
return materialIndex;
}
2017-05-04 15:39:01 -04:00
function getMaterial(gltf, materials, images, materialName, hasNormals, options) {
2017-04-18 11:56:08 -04:00
if (!defined(materialName)) {
// Create a default material if the primitive does not specify one
materialName = 'default';
}
2017-05-04 17:58:13 -04:00
var i;
var material;
var materialsLength = materials.length;
2017-05-04 17:58:13 -04:00
for (i = 0; i < materialsLength; ++i) {
if (materials[i].name === materialName) {
material = materials[i];
2017-04-18 11:56:08 -04:00
}
}
if (!defined(material)) {
material = new Material();
material.name = materialName;
}
2017-05-04 17:58:13 -04:00
var materialIndex;
materialsLength = gltf.materials.length;
for (i = 0; i < materialsLength; ++i) {
if (gltf.materials[i].name === materialName) {
materialIndex = i;
break;
}
}
2017-04-18 11:56:08 -04:00
if (!defined(materialIndex)) {
2017-05-04 15:39:01 -04:00
materialIndex = addMaterial(gltf, images, material, hasNormals, options);
2017-04-18 11:56:08 -04:00
}
return materialIndex;
}
2017-05-04 17:58:13 -04:00
function addVertexAttribute(gltf, bufferState, array, components, name) {
2017-04-18 11:56:08 -04:00
var buffer = array.toFloatBuffer();
var count = array.length / components;
var minMax = array.getMinMax(components);
var type = (components === 3 ? 'VEC3' : 'VEC2');
var accessor = {
2017-05-04 17:58:13 -04:00
name : name,
2017-04-18 11:56:08 -04:00
bufferView : bufferState.vertexBufferViewIndex,
byteOffset : bufferState.vertexBufferByteOffset,
componentType : WebGLConstants.FLOAT,
count : count,
min : minMax.min,
max : minMax.max,
type : type
2017-03-13 15:28:51 -04:00
};
2017-04-18 11:56:08 -04:00
bufferState.vertexBufferByteOffset += buffer.length;
bufferState.vertexBuffers.push(buffer);
var accessorIndex = gltf.accessors.length;
gltf.accessors.push(accessor);
return accessorIndex;
}
2017-05-04 17:58:13 -04:00
function addIndexArray(gltf, bufferState, array, uint32Indices, name) {
2017-04-18 11:56:08 -04:00
var buffer = uint32Indices ? array.toUint32Buffer() : array.toUint16Buffer();
var componentType = uint32Indices ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT;
var count = array.length;
var minMax = array.getMinMax(1);
var accessor = {
2017-05-04 17:58:13 -04:00
name : name,
2017-04-18 11:56:08 -04:00
bufferView : bufferState.indexBufferViewIndex,
byteOffset : bufferState.indexBufferByteOffset,
componentType : componentType,
count : count,
min : minMax.min,
max : minMax.max,
type : 'SCALAR'
2017-03-13 15:28:51 -04:00
};
2017-04-18 11:56:08 -04:00
bufferState.indexBufferByteOffset += buffer.length;
bufferState.indexBuffers.push(buffer);
var accessorIndex = gltf.accessors.length;
gltf.accessors.push(accessor);
return accessorIndex;
}
function requiresUint32Indices(nodes) {
var nodesLength = nodes.length;
for (var i = 0; i < nodesLength; ++i) {
var meshes = nodes[i].meshes;
var meshesLength = meshes.length;
for (var j = 0; j < meshesLength; ++j) {
// Reserve the 65535 index for primitive restart
var vertexCount = meshes[j].positions.length / 3;
if (vertexCount > 65534) {
return true;
}
}
}
return false;
}
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) {
2017-05-04 17:58:13 -04:00
attributes.POSITION = addVertexAttribute(gltf, bufferState, mesh.positions, 3, mesh.name + '_positions');
2017-04-18 11:56:08 -04:00
}
if (hasNormals) {
2017-05-04 17:58:13 -04:00
attributes.NORMAL = addVertexAttribute(gltf, bufferState, mesh.normals, 3, mesh.name + '_normals');
2017-04-18 11:56:08 -04:00
}
if (hasUVs) {
2017-05-04 17:58:13 -04:00
attributes.TEXCOORD_0 = addVertexAttribute(gltf, bufferState, mesh.uvs, 2, mesh.name + '_texcoords');
2017-04-18 11:56:08 -04:00
}
// Unload resources
mesh.positions = undefined;
mesh.normals = undefined;
mesh.uvs = undefined;
var gltfPrimitives = [];
var primitives = mesh.primitives;
var primitivesLength = primitives.length;
for (var i = 0; i < primitivesLength; ++i) {
var primitive = primitives[i];
2017-05-04 17:58:13 -04:00
var indexAccessorIndex = addIndexArray(gltf, bufferState, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices');
2017-04-18 11:56:08 -04:00
primitive.indices = undefined; // Unload resources
2017-05-04 15:39:01 -04:00
var materialIndex = getMaterial(gltf, materials, images, primitive.material, hasNormals, options);
2017-04-18 11:56:08 -04:00
gltfPrimitives.push({
attributes : attributes,
indices : indexAccessorIndex,
material : materialIndex,
mode : WebGLConstants.TRIANGLES
});
}
var gltfMesh = {
name : mesh.name,
primitives : gltfPrimitives
};
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;
2015-10-16 17:32:23 -04:00
}