obj2gltf/lib/gltf.js

341 lines
10 KiB
JavaScript
Raw Normal View History

2015-10-16 17:32:23 -04:00
"use strict";
var fs = require('fs-extra');
2015-10-16 17:32:23 -04:00
var path = require('path');
2016-06-09 13:33:08 -04:00
var Cesium = require('cesium');
var defined = Cesium.defined;
var defaultValue = Cesium.defaultValue;
var WebGLConstants = Cesium.WebGLConstants;
2015-10-16 17:32:23 -04:00
module.exports = createGltf;
function createGltf(data, inputPath, modelName, done) {
2015-10-16 17:32:23 -04:00
var vertexCount = data.vertexCount;
var vertexArray = data.vertexArray;
var positionMin = data.positionMin;
var positionMax = data.positionMax;
var hasUVs = data.hasUVs;
2016-06-09 13:33:08 -04:00
var hasNormals = data.hasNormals;
2015-10-16 17:32:23 -04:00
var materialGroups = data.materialGroups;
var materials = data.materials;
2016-06-09 13:33:08 -04:00
var images = data.images;
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var i, j, name;
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var sizeOfFloat32 = 4;
var sizeOfUint32 = 4;
var sizeOfUint16 = 2;
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var indexComponentType;
var indexComponentSize;
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
// Reserve the 65535 index for primitive restart
if (vertexCount < 65535) {
indexComponentType = WebGLConstants.UNSIGNED_SHORT;
indexComponentSize = sizeOfUint16;
} else {
indexComponentType = WebGLConstants.UNSIGNED_INT;
indexComponentSize = sizeOfUint32;
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
// Create primitives
var primitives = [];
var indexArrayLength = 0;
var indexArray;
var indexCount;
for (name in materialGroups) {
if (materialGroups.hasOwnProperty(name)) {
indexArray = materialGroups[name];
2015-10-16 17:32:23 -04:00
indexCount = indexArray.length;
2016-06-09 13:33:08 -04:00
primitives.push({
indexArray : indexArray,
indexOffset : indexArrayLength,
indexCount : indexCount,
material : name
});
indexArrayLength += indexCount;
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
// Create buffer to store vertex and index data
var indexArrayByteLength = indexArrayLength * indexComponentSize;
var vertexArrayLength = vertexArray.length; // In floats
var vertexArrayByteLength = vertexArrayLength * sizeOfFloat32;
var bufferByteLength = vertexArrayByteLength + indexArrayByteLength;
var buffer = new Buffer(bufferByteLength);
// Write vertex data
var byteOffset = 0;
for (i = 0; i < vertexArrayLength; ++i) {
buffer.writeFloatLE(vertexArray[i], byteOffset);
byteOffset += sizeOfFloat32;
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
// Write index data
var primitivesLength = primitives.length;
for (i = 0; i < primitivesLength; ++i) {
indexArray = primitives[i].indexArray;
indexCount = indexArray.length;
for (j = 0; j < indexCount; ++j) {
if (indexComponentSize === sizeOfUint16) {
buffer.writeUInt16LE(indexArray[j], byteOffset);
} else {
buffer.writeUInt32LE(indexArray[j], byteOffset);
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
byteOffset += indexComponentSize;
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var positionByteOffset = 0;
var normalByteOffset = 0;
var uvByteOffset = 0;
var vertexByteStride = 0;
if (hasNormals && hasUVs) {
normalByteOffset = sizeOfFloat32 * 3;
uvByteOffset = sizeOfFloat32 * 6;
vertexByteStride = sizeOfFloat32 * 8;
} else if (hasNormals && !hasUVs) {
normalByteOffset = sizeOfFloat32 * 3;
vertexByteStride = sizeOfFloat32 * 6;
} else if (!hasNormals && hasUVs) {
uvByteOffset = sizeOfFloat32 * 3;
vertexByteStride = sizeOfFloat32 * 5;
} else if (!hasNormals && !hasUVs) {
vertexByteStride = sizeOfFloat32 * 3;
}
2015-10-16 17:32:23 -04:00
var bufferId = modelName + '_buffer';
2016-06-09 13:33:08 -04:00
var bufferViewVertexId = 'bufferView_vertex';
var bufferViewIndexId = 'bufferView_index';
var accessorPositionId = 'accessor_position';
var accessorUVId = 'accessor_uv';
var accessorNormalId = 'accessor_normal';
var meshId = 'mesh_' + modelName;
var sceneId = 'scene_' + modelName;
var nodeId = 'node_' + modelName;
var samplerId = 'sampler_0';
function getAccessorIndexId(i) {
return 'accessor_index_' + i;
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
function getMaterialId(material) {
return 'material_' + material;
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
function getTextureId(image) {
if (!defined(image)) {
return undefined;
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
return 'texture_' + path.basename(image).substr(0, image.lastIndexOf('.'));
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
function getImageId(image) {
return path.basename(image, path.extname(image));
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var gltf = {
accessors : {},
asset : {},
buffers : {},
bufferViews : {},
images : {},
materials : {},
meshes : {},
nodes : {},
samplers : {},
scene : sceneId,
scenes : {},
textures : {}
};
gltf.asset = {
"generator": "OBJ2GLTF",
"premultipliedAlpha": true,
"profile": {
"api": "WebGL",
"version": "1.0"
},
"version": 1
};
gltf.scenes[sceneId] = {
nodes : [nodeId]
};
gltf.nodes[nodeId] = {
children : [],
matrix : [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1],
meshes : [meshId],
name : modelName
};
gltf.samplers[samplerId] = {}; // Use default values
var bufferSeparate = false;
var bufferUri;
if (buffer.length > 201326580) {
// toString fails for buffers larger than ~192MB. Instead save the buffer to a .bin file.
// Source: https://github.com/nodejs/node/issues/4266
bufferSeparate = true;
bufferUri = modelName + '.bin';
} else {
bufferUri = 'data:application/octet-stream;base64,' + buffer.toString('base64');
}
2016-06-09 13:33:08 -04:00
gltf.buffers[bufferId] = {
byteLength : bufferByteLength,
type : 'arraybuffer',
uri : bufferUri
};
gltf.bufferViews[bufferViewVertexId] = {
buffer : bufferId,
byteLength : vertexArrayByteLength,
byteOffset : 0,
target : WebGLConstants.ARRAY_BUFFER
};
gltf.bufferViews[bufferViewIndexId] = {
buffer : bufferId,
byteLength : indexArrayByteLength,
byteOffset : vertexArrayByteLength,
target : WebGLConstants.ELEMENT_ARRAY_BUFFER
};
for (i = 0; i < primitivesLength; ++i) {
var primitive = primitives[i];
gltf.accessors[getAccessorIndexId(i)] = {
bufferView : bufferViewIndexId,
byteOffset : primitive.indexOffset * indexComponentSize,
byteStride : 0,
componentType : indexComponentType,
count : primitive.indexCount,
type : 'SCALAR'
2015-10-16 17:32:23 -04:00
};
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
gltf.accessors[accessorPositionId] = {
bufferView : bufferViewVertexId,
byteOffset : positionByteOffset,
byteStride : vertexByteStride,
componentType : WebGLConstants.FLOAT,
count : vertexCount,
min : positionMin,
max : positionMax,
type : 'VEC3'
};
if (hasNormals) {
gltf.accessors[accessorNormalId] = {
2015-10-16 17:32:23 -04:00
bufferView : bufferViewVertexId,
2016-06-09 13:33:08 -04:00
byteOffset : normalByteOffset,
2015-10-16 17:32:23 -04:00
byteStride : vertexByteStride,
componentType : WebGLConstants.FLOAT,
count : vertexCount,
type : 'VEC3'
};
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
if (hasUVs) {
gltf.accessors[accessorUVId] = {
2015-10-16 17:32:23 -04:00
bufferView : bufferViewVertexId,
2016-06-09 13:33:08 -04:00
byteOffset : uvByteOffset,
2015-10-16 17:32:23 -04:00
byteStride : vertexByteStride,
componentType : WebGLConstants.FLOAT,
count : vertexCount,
2016-06-09 13:33:08 -04:00
type : 'VEC2'
2015-10-16 17:32:23 -04:00
};
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var gltfPrimitives = [];
gltf.meshes[meshId] = {
name : modelName,
primitives : gltfPrimitives
};
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
var gltfAttributes = {};
gltfAttributes.POSITION = accessorPositionId;
if (hasNormals) {
2015-10-16 17:32:23 -04:00
gltfAttributes.NORMAL = accessorNormalId;
2016-06-09 13:33:08 -04:00
}
if (hasUVs) {
gltfAttributes.TEXCOORD_0 = accessorUVId;
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
for (i = 0; i < primitivesLength; ++i) {
gltfPrimitives.push({
attributes : gltfAttributes,
indices : getAccessorIndexId(i),
material : getMaterialId(primitives[i].material),
mode : WebGLConstants.TRIANGLES
});
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
for (name in materials) {
if (materials.hasOwnProperty(name)) {
var material = materials[name];
var materialId = getMaterialId(name);
2015-10-16 17:32:23 -04:00
var values = {
ambient : defaultValue(defaultValue(getTextureId(material.ambientColorMap), material.ambientColor), [0, 0, 0, 1]),
diffuse : defaultValue(defaultValue(getTextureId(material.diffuseColorMap), material.diffuseColor), [0, 0, 0, 1]),
emission : defaultValue(defaultValue(getTextureId(material.emissionColorMap), material.emissionColor), [0, 0, 0, 1]),
2016-06-09 13:33:08 -04:00
specular : defaultValue(defaultValue(getTextureId(material.specularColorMap), material.specularColor), [0, 0, 0, 1]),
shininess : defaultValue(material.specularShininess, 0.0)
2015-10-16 17:32:23 -04:00
};
gltf.materials[materialId] = {
2016-06-09 13:33:08 -04:00
name: name,
values: values
2015-10-16 17:32:23 -04:00
};
}
2016-06-09 13:33:08 -04:00
}
2015-10-16 17:32:23 -04:00
2016-06-09 13:33:08 -04:00
for (name in images) {
if (images.hasOwnProperty(name)) {
var image = images[name];
var imageId = getImageId(name);
var textureId = getTextureId(name);
var format;
var channels = image.channels;
switch (channels) {
case 1:
format = WebGLConstants.ALPHA;
break;
case 2:
format = WebGLConstants.LUMINANCE_ALPHA;
break;
case 3:
format = WebGLConstants.RGB;
break;
case 4:
format = WebGLConstants.RGBA;
break;
2015-10-16 17:32:23 -04:00
}
2016-06-09 13:33:08 -04:00
gltf.images[imageId] = {
uri : image.uri
};
gltf.textures[textureId] = {
format : format,
internalFormat : format,
sampler : samplerId,
source : imageId,
target : WebGLConstants.TEXTURE_2D,
type : WebGLConstants.UNSIGNED_BYTE
};
}
}
if (bufferSeparate) {
var bufferPath = path.join(inputPath, modelName + '.bin');
fs.writeFile(bufferPath, buffer, function(err) {
if (err) {
throw err;
}
done(gltf);
2016-06-22 10:12:00 -04:00
});
} else {
done(gltf);
}
2015-10-16 17:32:23 -04:00
}