mirror of
https://github.com/CesiumGS/obj2gltf.git
synced 2024-11-23 08:34:14 -05:00
Fix incompatible byte strides
This commit is contained in:
parent
ab6786e463
commit
aaf44e75dc
@ -116,10 +116,14 @@ var options = {
|
||||
|
||||
console.time('Total');
|
||||
|
||||
obj2gltf(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
console.timeEnd('Total');
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
try {
|
||||
obj2gltf(objPath, gltfPath, options)
|
||||
.then(function() {
|
||||
console.timeEnd('Total');
|
||||
})
|
||||
.catch(function(error) {
|
||||
console.log(error);
|
||||
});
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
var Cesium = require('cesium');
|
||||
var path = require('path');
|
||||
var PNG = require('pngjs').PNG;
|
||||
var getBufferPadded = require('./getBufferPadded');
|
||||
var Material = require('./Material');
|
||||
|
||||
var CesiumMath = Cesium.Math;
|
||||
@ -57,12 +58,14 @@ function createGltf(objData, options) {
|
||||
});
|
||||
|
||||
var bufferState = {
|
||||
vertexBuffers : [],
|
||||
vertexBufferByteOffset : 0,
|
||||
vertexBufferViewIndex : 0,
|
||||
positionBuffers : [],
|
||||
normalBuffers : [],
|
||||
uvBuffers : [],
|
||||
indexBuffers : [],
|
||||
indexBufferByteOffset : 0,
|
||||
indexBufferViewIndex : 1
|
||||
positionAccessors : [],
|
||||
normalAccessors : [],
|
||||
uvAccessors : [],
|
||||
indexAccessors : []
|
||||
};
|
||||
|
||||
var uint32Indices = requiresUint32Indices(nodes);
|
||||
@ -98,49 +101,55 @@ function createGltf(objData, options) {
|
||||
}
|
||||
|
||||
addBuffers(gltf, bufferState);
|
||||
|
||||
return gltf;
|
||||
}
|
||||
|
||||
function addBuffers(gltf, bufferState) {
|
||||
var bufferName = 'buffer';
|
||||
var vertexBufferViewName = 'bufferView_vertex';
|
||||
var indexBufferViewName = 'bufferView_index';
|
||||
function addBufferView(gltf, buffers, accessors, byteStride, target) {
|
||||
var length = buffers.length;
|
||||
if (length === 0) {
|
||||
return;
|
||||
}
|
||||
var bufferViewIndex = gltf.bufferViews.length;
|
||||
var previousBufferView = gltf.bufferViews[bufferViewIndex - 1];
|
||||
var byteOffset = defined(previousBufferView) ? previousBufferView.byteOffset + previousBufferView.byteLength : 0;
|
||||
var byteLength = 0;
|
||||
for (var i = 0; i < length; ++i) {
|
||||
var accessor = gltf.accessors[accessors[i]];
|
||||
accessor.bufferView = bufferViewIndex;
|
||||
accessor.byteOffset = byteLength;
|
||||
byteLength += buffers[i].length;
|
||||
}
|
||||
gltf.bufferViews.push({
|
||||
name : 'bufferView_' + bufferViewIndex,
|
||||
buffer : 0,
|
||||
byteLength : byteLength,
|
||||
byteOffset : byteOffset,
|
||||
byteStride : byteStride,
|
||||
target : target
|
||||
});
|
||||
}
|
||||
|
||||
var vertexBuffers = bufferState.vertexBuffers;
|
||||
var indexBuffers = bufferState.indexBuffers;
|
||||
var vertexBufferByteLength = bufferState.vertexBufferByteOffset;
|
||||
var indexBufferByteLength = bufferState.indexBufferByteOffset;
|
||||
function addBuffers(gltf, bufferState) {
|
||||
// Positions and normals share the same byte stride so they can share the same bufferView
|
||||
var positionsAndNormalsAccessors = bufferState.positionAccessors.concat(bufferState.normalAccessors);
|
||||
var positionsAndNormalsBuffers = bufferState.positionBuffers.concat(bufferState.normalBuffers);
|
||||
addBufferView(gltf, positionsAndNormalsBuffers, positionsAndNormalsAccessors, 12, WebGLConstants.ARRAY_BUFFER);
|
||||
addBufferView(gltf, bufferState.uvBuffers, bufferState.uvAccessors, 8, WebGLConstants.ARRAY_BUFFER);
|
||||
addBufferView(gltf, bufferState.indexBuffers, bufferState.indexAccessors, undefined, WebGLConstants.ELEMENT_ARRAY_BUFFER);
|
||||
|
||||
var buffers = [];
|
||||
buffers = buffers.concat(vertexBuffers, indexBuffers);
|
||||
var buffer = Buffer.concat(buffers);
|
||||
buffers = buffers.concat(bufferState.positionBuffers, bufferState.normalBuffers, bufferState.uvBuffers, bufferState.indexBuffers);
|
||||
var buffer = getBufferPadded(Buffer.concat(buffers));
|
||||
|
||||
gltf.buffers.push({
|
||||
name : bufferName,
|
||||
byteLength : buffer.byteLength,
|
||||
name : 'buffer',
|
||||
byteLength : buffer.length,
|
||||
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) {
|
||||
@ -304,7 +313,7 @@ function encodePng(pixels, width, height, inputChannels, outputChannels) {
|
||||
|
||||
// Constants defined by pngjs
|
||||
var rgbColorType = 2;
|
||||
var rgbaColorType = 4;
|
||||
var rgbaColorType = 6;
|
||||
|
||||
var colorType = outputChannels === 4 ? rgbaColorType : rgbColorType;
|
||||
var inputColorType = inputChannels === 4 ? rgbaColorType : rgbColorType;
|
||||
@ -736,16 +745,13 @@ function getMaterial(gltf, materials, images, materialName, hasNormals, options)
|
||||
return materialIndex;
|
||||
}
|
||||
|
||||
function addVertexAttribute(gltf, bufferState, array, components, name) {
|
||||
var buffer = array.toFloatBuffer();
|
||||
function addVertexAttribute(gltf, array, components, name) {
|
||||
var count = array.length / components;
|
||||
var minMax = array.getMinMax(components);
|
||||
var type = (components === 3 ? 'VEC3' : 'VEC2');
|
||||
|
||||
var accessor = {
|
||||
name : name,
|
||||
bufferView : bufferState.vertexBufferViewIndex,
|
||||
byteOffset : bufferState.vertexBufferByteOffset,
|
||||
componentType : WebGLConstants.FLOAT,
|
||||
count : count,
|
||||
min : minMax.min,
|
||||
@ -753,24 +759,18 @@ function addVertexAttribute(gltf, bufferState, array, components, name) {
|
||||
type : type
|
||||
};
|
||||
|
||||
bufferState.vertexBufferByteOffset += buffer.length;
|
||||
bufferState.vertexBuffers.push(buffer);
|
||||
|
||||
var accessorIndex = gltf.accessors.length;
|
||||
gltf.accessors.push(accessor);
|
||||
return accessorIndex;
|
||||
}
|
||||
|
||||
function addIndexArray(gltf, bufferState, array, uint32Indices, name) {
|
||||
var buffer = uint32Indices ? array.toUint32Buffer() : array.toUint16Buffer();
|
||||
function addIndexArray(gltf, array, uint32Indices, name) {
|
||||
var componentType = uint32Indices ? WebGLConstants.UNSIGNED_INT : WebGLConstants.UNSIGNED_SHORT;
|
||||
var count = array.length;
|
||||
var minMax = array.getMinMax(1);
|
||||
|
||||
var accessor = {
|
||||
name : name,
|
||||
bufferView : bufferState.indexBufferViewIndex,
|
||||
byteOffset : bufferState.indexBufferByteOffset,
|
||||
componentType : componentType,
|
||||
count : count,
|
||||
min : minMax.min,
|
||||
@ -778,9 +778,6 @@ function addIndexArray(gltf, bufferState, array, uint32Indices, name) {
|
||||
type : 'SCALAR'
|
||||
};
|
||||
|
||||
bufferState.indexBufferByteOffset += buffer.length;
|
||||
bufferState.indexBuffers.push(buffer);
|
||||
|
||||
var accessorIndex = gltf.accessors.length;
|
||||
gltf.accessors.push(accessor);
|
||||
return accessorIndex;
|
||||
@ -807,15 +804,25 @@ function addMesh(gltf, materials, images, bufferState, uint32Indices, mesh, opti
|
||||
var hasNormals = mesh.normals.length > 0;
|
||||
var hasUVs = mesh.uvs.length > 0;
|
||||
|
||||
var accessorIndex;
|
||||
var attributes = {};
|
||||
if (hasPositions) {
|
||||
attributes.POSITION = addVertexAttribute(gltf, bufferState, mesh.positions, 3, mesh.name + '_positions');
|
||||
accessorIndex = addVertexAttribute(gltf, mesh.positions, 3, mesh.name + '_positions');
|
||||
attributes.POSITION = accessorIndex;
|
||||
bufferState.positionBuffers.push(mesh.positions.toFloatBuffer());
|
||||
bufferState.positionAccessors.push(accessorIndex);
|
||||
}
|
||||
if (hasNormals) {
|
||||
attributes.NORMAL = addVertexAttribute(gltf, bufferState, mesh.normals, 3, mesh.name + '_normals');
|
||||
accessorIndex = addVertexAttribute(gltf, mesh.normals, 3, mesh.name + '_normals');
|
||||
attributes.NORMAL = accessorIndex;
|
||||
bufferState.normalBuffers.push(mesh.normals.toFloatBuffer());
|
||||
bufferState.normalAccessors.push(accessorIndex);
|
||||
}
|
||||
if (hasUVs) {
|
||||
attributes.TEXCOORD_0 = addVertexAttribute(gltf, bufferState, mesh.uvs, 2, mesh.name + '_texcoords');
|
||||
accessorIndex = addVertexAttribute(gltf, mesh.uvs, 2, mesh.name + '_texcoords');
|
||||
attributes.TEXCOORD_0 = accessorIndex;
|
||||
bufferState.uvBuffers.push(mesh.uvs.toFloatBuffer());
|
||||
bufferState.uvAccessors.push(accessorIndex);
|
||||
}
|
||||
|
||||
// Unload resources
|
||||
@ -828,7 +835,11 @@ function addMesh(gltf, materials, images, bufferState, uint32Indices, mesh, opti
|
||||
var primitivesLength = primitives.length;
|
||||
for (var i = 0; i < primitivesLength; ++i) {
|
||||
var primitive = primitives[i];
|
||||
var indexAccessorIndex = addIndexArray(gltf, bufferState, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices');
|
||||
var indexAccessorIndex = addIndexArray(gltf, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices');
|
||||
var indexBuffer = uint32Indices ? primitive.indices.toUint32Buffer() : primitive.indices.toUint16Buffer();
|
||||
bufferState.indexBuffers.push(indexBuffer);
|
||||
bufferState.indexAccessors.push(indexAccessorIndex);
|
||||
|
||||
primitive.indices = undefined; // Unload resources
|
||||
|
||||
var materialIndex = getMaterial(gltf, materials, images, primitive.material, hasNormals, options);
|
||||
|
19
lib/getBufferPadded.js
Normal file
19
lib/getBufferPadded.js
Normal file
@ -0,0 +1,19 @@
|
||||
'use strict';
|
||||
module.exports = getBufferPadded;
|
||||
|
||||
/**
|
||||
* Pad the buffer to the next 4-byte boundary to ensure proper alignment for the section that follows.
|
||||
*
|
||||
* @param {Buffer} buffer The buffer.
|
||||
* @returns {Buffer} The padded buffer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function getBufferPadded(buffer) {
|
||||
var boundary = 4;
|
||||
var byteLength = buffer.length;
|
||||
var remainder = byteLength % boundary;
|
||||
var padding = (remainder === 0) ? 0 : boundary - remainder;
|
||||
var emptyBuffer = Buffer.alloc(padding);
|
||||
return Buffer.concat([buffer, emptyBuffer]);
|
||||
}
|
34
lib/getJsonBufferPadded.js
Normal file
34
lib/getJsonBufferPadded.js
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
|
||||
var defaultValue = Cesium.defaultValue;
|
||||
var defined = Cesium.defined;
|
||||
|
||||
module.exports = getJsonBufferPadded;
|
||||
|
||||
/**
|
||||
* Convert the JSON object to a padded buffer.
|
||||
*
|
||||
* Pad the JSON with extra whitespace to fit the next 4-byte boundary. This ensures proper alignment
|
||||
* for the section that follows.
|
||||
*
|
||||
* @param {Object} [json] The JSON object.
|
||||
* @returns {Buffer} The padded JSON buffer.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function getJsonBufferPadded(json) {
|
||||
var string = JSON.stringify(json);
|
||||
|
||||
var boundary = 8;
|
||||
var byteLength = Buffer.byteLength(string);
|
||||
var remainder = byteLength % boundary;
|
||||
var padding = (remainder === 0) ? 0 : boundary - remainder;
|
||||
var whitespace = '';
|
||||
for (var i = 0; i < padding; ++i) {
|
||||
whitespace += ' ';
|
||||
}
|
||||
string += whitespace;
|
||||
|
||||
return Buffer.from(string);
|
||||
}
|
67
lib/gltfToGlb.js
Normal file
67
lib/gltfToGlb.js
Normal file
@ -0,0 +1,67 @@
|
||||
'use strict';
|
||||
var Cesium = require('cesium');
|
||||
var getJsonBufferPadded = require('./getJsonBufferPadded');
|
||||
|
||||
var isDataUri = Cesium.isDataUri;
|
||||
|
||||
module.exports = gltfToGlb;
|
||||
|
||||
/**
|
||||
* Convert a glTF to binary glTF.
|
||||
*
|
||||
* @param {Object} gltf A javascript object containing a glTF asset.
|
||||
* @returns {Promise} A promise that resolves to a buffer containing the binary glTF.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function gltfToGlb(gltf) {
|
||||
var buffer = gltf.buffers[0];
|
||||
var binaryBuffer;
|
||||
if (isDataUri(buffer.uri)) {
|
||||
binaryBuffer = dataUriToBuffer(buffer.uri);
|
||||
delete buffer.uri;
|
||||
} else {
|
||||
binaryBuffer = Buffer.alloc(0);
|
||||
}
|
||||
|
||||
// Create padded binary scene string
|
||||
var jsonBuffer = getJsonBufferPadded(gltf);
|
||||
|
||||
// Allocate buffer (Global header) + (JSON chunk header) + (JSON chunk) + (Binary chunk header) + (Binary chunk)
|
||||
var glbLength = 12 + 8 + jsonBuffer.length + 8 + binaryBuffer.length;
|
||||
var glb = Buffer.alloc(glbLength);
|
||||
|
||||
// Write binary glTF header (magic, version, length)
|
||||
var byteOffset = 0;
|
||||
glb.writeUInt32LE(0x46546C67, byteOffset);
|
||||
byteOffset += 4;
|
||||
glb.writeUInt32LE(2, byteOffset);
|
||||
byteOffset += 4;
|
||||
glb.writeUInt32LE(glbLength, byteOffset);
|
||||
byteOffset += 4;
|
||||
|
||||
// Write JSON Chunk header (length, type)
|
||||
glb.writeUInt32LE(jsonBuffer.length, byteOffset);
|
||||
byteOffset += 4;
|
||||
glb.writeUInt32LE(0x4E4F534A, byteOffset); // JSON
|
||||
byteOffset += 4;
|
||||
|
||||
// Write JSON Chunk
|
||||
jsonBuffer.copy(glb, byteOffset);
|
||||
byteOffset += jsonBuffer.length;
|
||||
|
||||
// Write Binary Chunk header (length, type)
|
||||
glb.writeUInt32LE(binaryBuffer.length, byteOffset);
|
||||
byteOffset += 4;
|
||||
glb.writeUInt32LE(0x004E4942, byteOffset); // BIN
|
||||
byteOffset += 4;
|
||||
|
||||
// Write Binary Chunk
|
||||
binaryBuffer.copy(glb, byteOffset);
|
||||
return glb;
|
||||
}
|
||||
|
||||
function dataUriToBuffer(dataUri) {
|
||||
var data = dataUri.slice(dataUri.indexOf(','));
|
||||
return Buffer.from(data, 'base64');
|
||||
}
|
@ -72,21 +72,19 @@ function obj2gltf(objPath, gltfPath, options) {
|
||||
throw new DeveloperError('gltfPath is required');
|
||||
}
|
||||
|
||||
if (metallicRoughness + specularGlossiness + materialsCommon > 1) {
|
||||
throw new DeveloperError('Only one material type may be set from [--metallicRoughness, --specularGlossiness, --materialsCommon].');
|
||||
}
|
||||
|
||||
var extension = path.extname(gltfPath).toLowerCase();
|
||||
var modelName = path.basename(gltfPath, path.extname(gltfPath));
|
||||
if (extension === '.glb') {
|
||||
if (binary || extension === '.glb') {
|
||||
binary = true;
|
||||
}
|
||||
if (binary) {
|
||||
extension = '.glb';
|
||||
}
|
||||
|
||||
gltfPath = path.join(path.dirname(gltfPath), modelName + extension);
|
||||
|
||||
if (metallicRoughness + specularGlossiness + materialsCommon > 1) {
|
||||
return Promise.reject(new RuntimeError('Only one material type may be set from [--metallicRoughness, --specularGlossiness, --materialsCommon].'));
|
||||
}
|
||||
|
||||
var jsonOptions = {
|
||||
spaces : 2
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ var fsExtra = require('fs-extra');
|
||||
var mime = require('mime');
|
||||
var path = require('path');
|
||||
var Promise = require('bluebird');
|
||||
var getBufferPadded = require('./getBufferPadded');
|
||||
|
||||
var defined = Cesium.defined;
|
||||
var RuntimeError = Cesium.RuntimeError;
|
||||
@ -125,19 +126,32 @@ function writeEmbeddedBuffer(gltf) {
|
||||
}
|
||||
|
||||
function writeEmbeddedTextures(gltf) {
|
||||
var bufferSource = gltf.buffers[0].extras._obj2gltf.source;
|
||||
var buffer = gltf.buffers[0];
|
||||
var bufferExtras = buffer.extras._obj2gltf;
|
||||
var bufferSource = bufferExtras.source;
|
||||
var images = gltf.images;
|
||||
var imagesLength = images.length;
|
||||
var sources = [bufferSource];
|
||||
var byteOffset = bufferSource.length;
|
||||
|
||||
for (var i = 0; i < imagesLength; ++i) {
|
||||
var image = images[i];
|
||||
var extras = image.extras._obj2gltf;
|
||||
var imageSource = extras.source;
|
||||
var imageByteLength = imageSource.length;
|
||||
|
||||
image.mimeType = mime.lookup(extras.extension);
|
||||
image.bufferView = gltf.bufferViews.length;
|
||||
gltf.bufferViews.push({
|
||||
buffer : 0,
|
||||
byteOffset : bufferSource.length,
|
||||
byteLength : imageSource.byteLength
|
||||
byteOffset : byteOffset,
|
||||
byteLength : imageByteLength
|
||||
});
|
||||
bufferSource = Buffer.concat([bufferSource, imageSource]);
|
||||
byteOffset += imageByteLength;
|
||||
sources.push(imageSource);
|
||||
}
|
||||
|
||||
var source = getBufferPadded(Buffer.concat(sources));
|
||||
bufferExtras.source = source;
|
||||
buffer.byteLength = source.length;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user