obj2gltf/lib/gltf.js

344 lines
11 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');
2016-07-22 14:09:13 -04:00
2016-06-09 13:33:08 -04:00
var defined = Cesium.defined;
var defaultValue = Cesium.defaultValue;
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.
* @returns {Object} A glTF asset with the KHR_materials_common extension.
*
* @private
*/
function createGltf(objData) {
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';
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var gltf = {
accessors : {},
asset : {},
buffers : {},
bufferViews : {},
2017-03-13 15:28:51 -04:00
extensionsUsed : ['KHR_materials_common'],
2016-06-09 13:33:08 -04:00
images : {},
materials : {},
meshes : {},
nodes : {},
samplers : {},
scene : sceneId,
scenes : {},
textures : {}
};
gltf.asset = {
2017-03-13 15:28:51 -04:00
generator : 'obj2gltf',
profile : {
api : 'WebGL',
version : '1.0'
2016-06-09 13:33:08 -04:00
},
2017-03-13 15:28:51 -04:00
version: '1.0'
2016-06-09 13:33:08 -04:00
};
gltf.scenes[sceneId] = {
2017-03-13 15:28:51 -04:00
nodes : []
2016-06-09 13:33:08 -04:00
};
2017-03-13 15:28:51 -04:00
function getImageId(imagePath) {
return path.basename(imagePath, path.extname(imagePath));
}
2016-06-09 13:33:08 -04:00
2017-03-13 15:28:51 -04:00
function getTextureId(imagePath) {
if (!defined(imagePath) || !defined(images[imagePath])) {
return undefined;
}
return 'texture_' + getImageId(imagePath);
}
2016-06-09 13:33:08 -04:00
2017-03-13 15:28:51 -04:00
function createMaterial(material, hasNormals) {
var ambient = defaultValue(defaultValue(getTextureId(material.ambientColorMap), material.ambientColor), [0, 0, 0, 1]);
var diffuse = defaultValue(defaultValue(getTextureId(material.diffuseColorMap), material.diffuseColor), [0.5, 0.5, 0.5, 1]);
var emission = defaultValue(defaultValue(getTextureId(material.emissionColorMap), material.emissionColor), [0, 0, 0, 1]);
var specular = defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]);
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.diffuseColorMap].transparent || (transparency < 1.0);
} else {
diffuse[3] = alpha;
transparent = diffuse[3] < 1.0;
}
2016-06-09 13:33:08 -04:00
2017-03-13 15:28:51 -04:00
var doubleSided = transparent;
2016-06-09 13:33:08 -04:00
2017-03-13 15:28:51 -04:00
if (!hasNormals) {
// Constant technique only factors in ambient and emission sources - set emission to diffuse
emission = diffuse;
diffuse = [0, 0, 0, 1];
2017-03-13 15:28:51 -04:00
}
// It's not completely clear whether transparent and doubleSided belong under values or KHR_materials_common
// Put under both for now to handle both situations.
// https://github.com/KhronosGroup/glTF/tree/master/extensions/Khronos/KHR_materials_common
// https://github.com/KhronosGroup/glTF/issues/632
2017-03-13 15:28:51 -04:00
var technique = hasNormals ? (hasSpecular ? 'PHONG' : 'LAMBERT') : 'CONSTANT';
return {
extensions : {
KHR_materials_common : {
technique : technique,
transparent : transparent,
doubleSided : doubleSided,
2017-03-13 15:28:51 -04:00
values : {
ambient : ambient,
diffuse : diffuse,
emission : emission,
specular : specular,
shininess : shininess,
transparency : transparency,
transparent : transparent,
doubleSided : doubleSided
}
}
}
2015-10-16 17:32:23 -04:00
};
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
if (Object.keys(images).length > 0) {
gltf.samplers[samplerId] = {
magFilter : WebGLConstants.LINEAR,
minFilter : WebGLConstants.LINEAR,
wrapS : WebGLConstants.REPEAT,
wrapT : WebGLConstants.REPEAT
2015-10-16 17:32:23 -04:00
};
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
for (var imagePath in images) {
if (images.hasOwnProperty(imagePath)) {
var image = images[imagePath];
var imageId = getImageId(imagePath);
var textureId = getTextureId(imagePath);
gltf.images[imageId] = {
name : imageId,
extras : {
_obj2gltf : {
source : image.source,
extension : image.extension
}
}
2017-03-13 15:28:51 -04:00
};
gltf.textures[textureId] = {
format : image.format,
internalFormat : image.format,
sampler : samplerId,
source : imageId,
target : WebGLConstants.TEXTURE_2D,
type : WebGLConstants.UNSIGNED_BYTE
};
}
}
var vertexBuffers = [];
2017-03-14 14:40:33 -04:00
var vertexBufferByteOffset = 0;
2017-03-13 15:28:51 -04:00
var indexBuffers = [];
2017-03-14 14:40:33 -04:00
var indexBufferByteOffset = 0;
2017-03-13 15:28:51 -04:00
var accessorCount = 0;
function addVertexAttribute(array, components) {
var count = array.length / components;
var buffer = array.toFloatBuffer();
var minMax = array.getMinMax(components);
var type = (components === 3 ? 'VEC3' : 'VEC2');
var accessor = {
bufferView : vertexBufferViewId,
2017-03-14 14:40:33 -04:00
byteOffset : vertexBufferByteOffset,
2017-03-13 15:28:51 -04:00
byteStride : 0,
2015-10-16 17:32:23 -04:00
componentType : WebGLConstants.FLOAT,
2017-03-13 15:28:51 -04:00
count : count,
min : minMax.min,
max : minMax.max,
type : type
2015-10-16 17:32:23 -04:00
};
2017-03-13 15:28:51 -04:00
2017-03-14 14:40:33 -04:00
vertexBufferByteOffset += buffer.length;
2017-03-13 15:28:51 -04:00
vertexBuffers.push(buffer);
var accessorId = 'accessor_' + accessorCount++;
gltf.accessors[accessorId] = accessor;
return accessorId;
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
function addIndexArray(array, uint32Indices) {
var buffer = uint32Indices ? array.toUint32Buffer() : array.toUint16Buffer();
var componentType = uint32Indices ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT;
var length = array.length;
var minMax = array.getMinMax(1);
var accessor = {
bufferView : indexBufferViewId,
2017-03-14 14:40:33 -04:00
byteOffset : indexBufferByteOffset,
2017-03-13 15:28:51 -04:00
byteStride : 0,
componentType : componentType,
count : length,
min : minMax.min,
max : minMax.max,
type : 'SCALAR'
};
2015-10-16 17:32:23 -04:00
2017-03-14 14:40:33 -04:00
indexBufferByteOffset += buffer.length;
2017-03-13 15:28:51 -04:00
indexBuffers.push(buffer);
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
var accessorId = 'accessor_' + accessorCount++;
gltf.accessors[accessorId] = accessor;
return accessorId;
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
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;
}
}
2015-10-16 17:32:23 -04:00
}
2017-03-13 15:28:51 -04:00
return false;
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2017-03-13 15:28:51 -04:00
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);
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);
}
if (hasNormals) {
attributes.NORMAL = addVertexAttribute(mesh.normals, 3);
}
if (hasUVs) {
attributes.TEXCOORD_0 = addVertexAttribute(mesh.uvs, 2);
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
2017-03-13 15:28:51 -04:00
// Unload resources
mesh.positions = undefined;
mesh.normals = undefined;
mesh.uvs = undefined;
var gltfMeshPrimitives = [];
gltf.meshes[meshId] = {
name : meshId,
primitives : gltfMeshPrimitives
2016-06-09 13:33:08 -04:00
};
2017-03-13 15:28:51 -04:00
// Add primitives to mesh
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);
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 material = defaultValue(materials[materialId], {});
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({
attributes : attributes,
indices : indexAccessorId,
material : materialId,
mode : WebGLConstants.TRIANGLES
});
}
2016-06-09 13:33:08 -04:00
}
}
2017-03-14 14:40:33 -04:00
var buffers = [];
buffers = buffers.concat(vertexBuffers, indexBuffers);
var buffer = Buffer.concat(buffers);
2017-03-13 15:28:51 -04:00
gltf.buffers[bufferId] = {
byteLength : buffer.byteLength,
extras : {
_obj2gltf : {
source : buffer
}
}
2017-03-13 15:28:51 -04:00
};
gltf.bufferViews[vertexBufferViewId] = {
buffer : bufferId,
2017-03-14 14:40:33 -04:00
byteLength : vertexBufferByteOffset,
2017-03-13 15:28:51 -04:00
byteOffset : 0,
target : WebGLConstants.ARRAY_BUFFER
};
gltf.bufferViews[indexBufferViewId] = {
buffer : bufferId,
2017-03-14 14:40:33 -04:00
byteLength : indexBufferByteOffset,
byteOffset : vertexBufferByteOffset,
2017-03-13 15:28:51 -04:00
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
};
2016-07-22 14:09:13 -04:00
return gltf;
2015-10-16 17:32:23 -04:00
}