diff --git a/lib/createGltf.js b/lib/createGltf.js index b5f3f89..27e9b62 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -4,6 +4,7 @@ var getBufferPadded = require('./getBufferPadded'); var getDefaultMaterial = require('./loadMtl').getDefaultMaterial; var Texture = require('./Texture'); +var defaultValue = Cesium.defaultValue; var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; @@ -23,6 +24,9 @@ function createGltf(objData, options) { var materials = objData.materials; var name = objData.name; + // Split materials used by primitives with different types of attributes + materials = splitIncompatibleMaterials(nodes, materials, options); + var gltf = { accessors : [], asset : {}, @@ -70,14 +74,14 @@ function createGltf(objData, options) { var meshIndex; if (meshesLength === 1) { - meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, meshes[0], options); + meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, meshes[0]); addNode(gltf, node.name, meshIndex, undefined); } 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, bufferState, uint32Indices, mesh, options); + meshIndex = addMesh(gltf, materials, bufferState, uint32Indices, mesh); addNode(gltf, mesh.name, meshIndex, parentIndex); } } @@ -194,6 +198,31 @@ function getTexture(gltf, texture) { }; } +function cloneMaterial(material, removeTextures) { + if (material === null || typeof material !== 'object') { + return material; + } else if (material instanceof Texture) { + if (removeTextures) { + return undefined; + } + return material; + } else if (Array.isArray(material)) { + var length = material.length; + var clonedArray = new Array(length); + for (var i = 0; i < length; ++i) { + clonedArray[i] = cloneMaterial(material[i], removeTextures); + } + return clonedArray; + } + var clonedObject = {}; + for (var name in material) { + if (material.hasOwnProperty(name)) { + clonedObject[name] = cloneMaterial(material[name], removeTextures); + } + } + return clonedObject; +} + function resolveTextures(gltf, material) { for (var name in material) { if (material.hasOwnProperty(name)) { @@ -214,35 +243,27 @@ function addMaterial(gltf, material) { return materialIndex; } -function getMaterial(gltf, materials, materialName, options) { - if (!defined(materialName)) { - // Create a default material if the primitive does not specify one - materialName = 'default'; - } - - var i; - var material; +function getMaterialByName(materials, materialName) { var materialsLength = materials.length; - for (i = 0; i < materialsLength; ++i) { + for (var i = 0; i < materialsLength; ++i) { if (materials[i].name === materialName) { - material = materials[i]; - break; + return materials[i]; } } +} - if (!defined(material)) { - material = getDefaultMaterial(options); - material.name = materialName; - } - - var materialIndex; - materialsLength = gltf.materials.length; - for (i = 0; i < materialsLength; ++i) { - if (gltf.materials[i].name === materialName) { - materialIndex = i; - break; +function getMaterialIndex(materials, materialName) { + var materialsLength = materials.length; + for (var i = 0; i < materialsLength; ++i) { + if (materials[i].name === materialName) { + return i; } } +} + +function getMaterial(gltf, materials, materialName) { + var material = getMaterialByName(materials, materialName); + var materialIndex = getMaterialIndex(gltf.materials, materialName); if (!defined(materialIndex)) { materialIndex = addMaterial(gltf, material); @@ -251,6 +272,61 @@ function getMaterial(gltf, materials, materialName, options) { return materialIndex; } +function primitiveInfoMatch(a, b) { + return a.hasUvs === b.hasUvs && + a.hasNormals === b.hasNormals; +} + +function splitIncompatibleMaterials(nodes, materials, options) { + var splitMaterials = []; + var primitiveInfoByMaterial = {}; + 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) { + var primitives = meshes[j].primitives; + var primitivesLength = primitives.length; + for (var k = 0; k < primitivesLength; ++k) { + var primitive = primitives[k]; + var hasUvs = primitive.uvs.length > 0; + var hasNormals = primitive.normals.length > 0; + var primitiveInfo = { + hasUvs : hasUvs, + hasNormals : hasNormals + }; + var originalMaterialName = defaultValue(primitive.material, 'default'); + var materialName = originalMaterialName; + var suffix = 2; + while (defined(primitiveInfoByMaterial[materialName])) { + if (primitiveInfoMatch(primitiveInfo, primitiveInfoByMaterial[materialName])) { + break; + } + materialName = originalMaterialName + '-' + suffix++; + } + + primitive.material = materialName; + primitiveInfoByMaterial[materialName] = primitiveInfo; + + var material = getMaterialByName(splitMaterials, materialName); + if (defined(material)) { + continue; + } + + material = getMaterialByName(materials, originalMaterialName); + if (defined(material)) { + material = cloneMaterial(material, !hasUvs); + } else { + material = getDefaultMaterial(options); + } + material.name = materialName; + splitMaterials.push(material); + } + } + } + return splitMaterials; +} + function addVertexAttribute(gltf, array, components, name) { var count = array.length / components; var minMax = array.getMinMax(components); @@ -309,7 +385,7 @@ function requiresUint32Indices(nodes) { return false; } -function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index, options) { +function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitive, index) { var hasPositions = primitive.positions.length > 0; var hasNormals = primitive.normals.length > 0; var hasUVs = primitive.uvs.length > 0; @@ -346,7 +422,7 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti primitive.uvs = undefined; primitive.indices = undefined; - var materialIndex = getMaterial(gltf, materials, primitive.material, options); + var materialIndex = getMaterial(gltf, materials, primitive.material); return { attributes : attributes, @@ -356,12 +432,12 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti }; } -function addMesh(gltf, materials, bufferState, uint32Indices, mesh, options) { +function addMesh(gltf, materials, bufferState, uint32Indices, mesh) { var gltfPrimitives = []; var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i, options)); + gltfPrimitives.push(addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primitives[i], i)); } var gltfMesh = {