diff --git a/lib/Material.js b/lib/Material.js index f5caa1d..10bfcdd 100644 --- a/lib/Material.js +++ b/lib/Material.js @@ -2,6 +2,13 @@ module.exports = Material; +/** + * A material definition which maps to the .mtl format. + * + * The default value for specularShininess varies depending on the material type, @see loadMtl. + * + * @private + */ function Material() { this.name = ''; this.ambientColor = [0.0, 0.0, 0.0, 1.0]; // Ka diff --git a/lib/createGltf.js b/lib/createGltf.js index a568c4f..9c8b302 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -16,10 +16,10 @@ module.exports = createGltf; * * @param {Object} objData Output of obj.js, containing an array of nodes containing geometry information, materials, and images. * @param {Object} options An object with the following properties: - * @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. + * @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. * @param {Boolean} options.logger A callback function for handling logged messages. Defaults to console.log. * @returns {Object} A glTF asset. * @@ -597,16 +597,17 @@ function convertTraditionalToMetallicRoughness(material) { // Low specular intensity values should produce a rough material even if shininess is high. if (specularIntensity < 0.1) { - roughnessFactor *= specularIntensity; + roughnessFactor *= (1.0 - specularIntensity); } var metallicFactor = 0.0; + material.specularTexture = undefined; // For now just ignore the specular texture material.specularColor = [metallicFactor, metallicFactor, metallicFactor, 1.0]; - material.specularShiness = roughnessFactor; + material.specularShininess = roughnessFactor; } -function createMaterialsCommonMaterial(gltf, images, material, hasNormals, options) { +function createMaterialsCommonMaterial(gltf, images, material, hasNormals) { var materialName = material.name; var ambientImage = getImage(images, material.ambientTexture); @@ -682,7 +683,7 @@ function addMaterial(gltf, images, material, hasNormals, options) { } else if (options.metallicRoughness) { gltfMaterial = createMetallicRoughnessMaterial(gltf, images, material, options); } else if (options.materialsCommon) { - gltfMaterial = createMaterialsCommonMaterial(gltf, images, material, hasNormals, options); + gltfMaterial = createMaterialsCommonMaterial(gltf, images, material, hasNormals); } else { convertTraditionalToMetallicRoughness(material); gltfMaterial = createMetallicRoughnessMaterial(gltf, images, material, options); @@ -693,26 +694,16 @@ function addMaterial(gltf, images, material, hasNormals, options) { return materialIndex; } -function getMaterialIndex(gltf, materialName) { - var materials = gltf.materials; - var length = materials.length; - for (var i = 0; i < length; ++i) { - if (materials[i].name === materialName) { - return i; - } - } - return undefined; -} - function getMaterial(gltf, materials, images, materialName, hasNormals, options) { if (!defined(materialName)) { // Create a default material if the primitive does not specify one materialName = 'default'; } + var i; var material; var materialsLength = materials.length; - for (var i = 0; i < materialsLength; ++i) { + for (i = 0; i < materialsLength; ++i) { if (materials[i].name === materialName) { material = materials[i]; } @@ -723,7 +714,14 @@ function getMaterial(gltf, materials, images, materialName, hasNormals, options) material.name = materialName; } - var materialIndex = getMaterialIndex(gltf, materialName); + var materialIndex; + materialsLength = gltf.materials.length; + for (i = 0; i < materialsLength; ++i) { + if (gltf.materials[i].name === materialName) { + materialIndex = i; + break; + } + } if (!defined(materialIndex)) { materialIndex = addMaterial(gltf, images, material, hasNormals, options); @@ -732,13 +730,14 @@ function getMaterial(gltf, materials, images, materialName, hasNormals, options) return materialIndex; } -function addVertexAttribute(gltf, bufferState, array, components) { +function addVertexAttribute(gltf, bufferState, array, components, name) { var buffer = array.toFloatBuffer(); 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, @@ -756,13 +755,14 @@ function addVertexAttribute(gltf, bufferState, array, components) { return accessorIndex; } -function addIndexArray(gltf, bufferState, array, uint32Indices) { +function addIndexArray(gltf, bufferState, array, uint32Indices, name) { 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 = { + name : name, bufferView : bufferState.indexBufferViewIndex, byteOffset : bufferState.indexBufferByteOffset, componentType : componentType, @@ -803,13 +803,13 @@ function addMesh(gltf, materials, images, bufferState, uint32Indices, mesh, opti var attributes = {}; if (hasPositions) { - attributes.POSITION = addVertexAttribute(gltf, bufferState, mesh.positions, 3); + attributes.POSITION = addVertexAttribute(gltf, bufferState, mesh.positions, 3, mesh.name + '_positions'); } if (hasNormals) { - attributes.NORMAL = addVertexAttribute(gltf, bufferState, mesh.normals, 3); + attributes.NORMAL = addVertexAttribute(gltf, bufferState, mesh.normals, 3, mesh.name + '_normals'); } if (hasUVs) { - attributes.TEXCOORD_0 = addVertexAttribute(gltf, bufferState, mesh.uvs, 2); + attributes.TEXCOORD_0 = addVertexAttribute(gltf, bufferState, mesh.uvs, 2, mesh.name + '_texcoords'); } // Unload resources @@ -822,7 +822,7 @@ 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); + var indexAccessorIndex = addIndexArray(gltf, bufferState, primitive.indices, uint32Indices, mesh.name + '_' + i + '_indices'); primitive.indices = undefined; // Unload resources var materialIndex = getMaterial(gltf, materials, images, primitive.material, hasNormals, options); diff --git a/lib/loadMtl.js b/lib/loadMtl.js index 76aaaa0..4a4c49d 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -9,23 +9,31 @@ module.exports = loadMtl; * Parse an mtl file. * * @param {String} mtlPath Path to the mtl file. + * @param {Object} options An object with the following properties: + * @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. * @returns {Promise} A promise resolving to the materials. * * @private */ -function loadMtl(mtlPath) { +function loadMtl(mtlPath, options) { var material; var values; var value; var mtlDirectory = path.dirname(mtlPath); var materials = []; + var defaultSpecularShininess = 0.0; + if (options.metallicRoughness) { + defaultSpecularShininess = 1.0; // Fully rough + } + function parseLine(line) { line = line.trim(); if (/^newmtl /i.test(line)) { var name = line.substring(7).trim(); material = new Material(); material.name = name; + material.specularShininess = defaultSpecularShininess; materials.push(material); } else if (/^Ka /i.test(line)) { values = line.substring(3).trim().split(' '); diff --git a/lib/loadObj.js b/lib/loadObj.js index 8991289..2f6d2f5 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -337,7 +337,7 @@ function loadMaterials(mtlPaths, objPath, options) { logger('Could not read mtl file at ' + mtlPath + ' because it is outside of the obj directory and the secure flag is true. Using default material instead.'); return; } - return loadMtl(mtlPath) + return loadMtl(mtlPath, options) .then(function(materialsInMtl) { materials = materials.concat(materialsInMtl); }) diff --git a/lib/obj2gltf.js b/lib/obj2gltf.js index a7bb46b..588546d 100644 --- a/lib/obj2gltf.js +++ b/lib/obj2gltf.js @@ -100,7 +100,7 @@ function obj2gltf(objPath, gltfPath, options) { } if (metallicRoughness + specularGlossiness + materialsCommon > 1) { - throw new DeveloperError('Only one material type may be set from [--metallicRoughness, --specularGlossiness, --materialsCommon].'); + return Promise.reject(new RuntimeError('Only one material type may be set from [--metallicRoughness, --specularGlossiness, --materialsCommon].')); } gltfPath = path.join(path.dirname(gltfPath), modelName + extension); diff --git a/lib/writeUris.js b/lib/writeUris.js index 43dee14..96636fc 100644 --- a/lib/writeUris.js +++ b/lib/writeUris.js @@ -7,6 +7,7 @@ var Promise = require('bluebird'); var fsExtraOutputFile = Promise.promisify(fsExtra.outputFile); +var defined = Cesium.defined; var RuntimeError = Cesium.RuntimeError; module.exports = writeUris; @@ -86,16 +87,18 @@ function deleteExtras(gltf) { } } -function cleanup(gltf) { - // Remove empty arrays from top-level items - for (var key in gltf) { - if (gltf.hasOwnProperty(key)) { - var property = gltf[key]; - if (Array.isArray(property) && property.length === 0) { - delete gltf[key]; - } +function removeEmpty(json) { + Object.keys(json).forEach(function(key) { + if (!defined(json[key]) || (Array.isArray(json[key]) && json[key].length === 0)) { + delete json[key]; // Delete values that are undefined or [] + } else if (typeof json[key] === 'object') { + removeEmpty(json[key]); } - } + }); +} + +function cleanup(gltf) { + removeEmpty(gltf); } function writeSeparateBuffer(gltf, resourcesDirectory, name) { diff --git a/specs/data/box-objects-groups-materials/box-objects-groups-materials.gltf b/specs/data/box-objects-groups-materials/box-objects-groups-materials.gltf index 1714d03..1006b4e 100644 --- a/specs/data/box-objects-groups-materials/box-objects-groups-materials.gltf +++ b/specs/data/box-objects-groups-materials/box-objects-groups-materials.gltf @@ -1,9 +1,9 @@ { - "accessors": { - "accessor_0": { - "bufferView": "bufferView_vertex", + "accessors": [ + { + "name": "Blue_positions", + "bufferView": 0, "byteOffset": 0, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -18,10 +18,10 @@ ], "type": "VEC3" }, - "accessor_1": { - "bufferView": "bufferView_vertex", + { + "name": "Blue_normals", + "bufferView": 0, "byteOffset": 288, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -36,10 +36,10 @@ ], "type": "VEC3" }, - "accessor_2": { - "bufferView": "bufferView_vertex", + { + "name": "Blue_texcoords", + "bufferView": 0, "byteOffset": 576, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -52,10 +52,10 @@ ], "type": "VEC2" }, - "accessor_3": { - "bufferView": "bufferView_index", + { + "name": "Blue_0_indices", + "bufferView": 1, "byteOffset": 0, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -66,10 +66,10 @@ ], "type": "SCALAR" }, - "accessor_4": { - "bufferView": "bufferView_index", + { + "name": "Blue_1_indices", + "bufferView": 1, "byteOffset": 36, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -80,10 +80,10 @@ ], "type": "SCALAR" }, - "accessor_5": { - "bufferView": "bufferView_vertex", + { + "name": "Green_positions", + "bufferView": 0, "byteOffset": 768, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -98,10 +98,10 @@ ], "type": "VEC3" }, - "accessor_6": { - "bufferView": "bufferView_vertex", + { + "name": "Green_normals", + "bufferView": 0, "byteOffset": 1056, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -116,10 +116,10 @@ ], "type": "VEC3" }, - "accessor_7": { - "bufferView": "bufferView_vertex", + { + "name": "Green_texcoords", + "bufferView": 0, "byteOffset": 1344, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -132,10 +132,10 @@ ], "type": "VEC2" }, - "accessor_8": { - "bufferView": "bufferView_index", + { + "name": "Green_0_indices", + "bufferView": 1, "byteOffset": 72, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -146,10 +146,10 @@ ], "type": "SCALAR" }, - "accessor_9": { - "bufferView": "bufferView_index", + { + "name": "Green_1_indices", + "bufferView": 1, "byteOffset": 108, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -160,10 +160,10 @@ ], "type": "SCALAR" }, - "accessor_10": { - "bufferView": "bufferView_vertex", + { + "name": "Red_positions", + "bufferView": 0, "byteOffset": 1536, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -178,10 +178,10 @@ ], "type": "VEC3" }, - "accessor_11": { - "bufferView": "bufferView_vertex", + { + "name": "Red_normals", + "bufferView": 0, "byteOffset": 1824, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -196,10 +196,10 @@ ], "type": "VEC3" }, - "accessor_12": { - "bufferView": "bufferView_vertex", + { + "name": "Red_texcoords", + "bufferView": 0, "byteOffset": 2112, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -212,10 +212,10 @@ ], "type": "VEC2" }, - "accessor_13": { - "bufferView": "bufferView_index", + { + "name": "Red_0_indices", + "bufferView": 1, "byteOffset": 144, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -226,10 +226,10 @@ ], "type": "SCALAR" }, - "accessor_14": { - "bufferView": "bufferView_index", + { + "name": "Red_1_indices", + "bufferView": 1, "byteOffset": 180, - "byteStride": 0, "componentType": 5123, "count": 18, "min": [ @@ -240,253 +240,201 @@ ], "type": "SCALAR" } - }, + ], "asset": { "generator": "obj2gltf", - "profile": { - "api": "WebGL", - "version": "1.0" - }, - "version": "1.0" + "version": "2.0" }, - "buffers": { - "buffer": { + "buffers": [ + { + "name": "buffer", "byteLength": 2520, "uri": "data:application/octet-stream;base64,AACAvwAAgL8AAIDAAACAvwAAgD8AAIDAAACAvwAAgD8AAMDAAACAvwAAgL8AAMDAAACAvwAAgL8AAMDAAACAvwAAgD8AAMDAAACAPwAAgD8AAMDAAACAPwAAgL8AAMDAAACAPwAAgL8AAMDAAACAPwAAgD8AAMDAAACAPwAAgD8AAIDAAACAPwAAgL8AAIDAAACAPwAAgL8AAIDAAACAPwAAgD8AAIDAAACAvwAAgD8AAIDAAACAvwAAgL8AAIDAAACAvwAAgL8AAMDAAACAPwAAgL8AAMDAAACAPwAAgL8AAIDAAACAvwAAgL8AAIDAAACAPwAAgD8AAMDAAACAvwAAgD8AAMDAAACAvwAAgD8AAIDAAACAPwAAgD8AAIDAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAQAAAgL8AAIA/AACAQAAAgD8AAIA/AACAQAAAgD8AAIC/AACAQAAAgL8AAIC/AACAQAAAgL8AAIC/AACAQAAAgD8AAIC/AADAQAAAgD8AAIC/AADAQAAAgL8AAIC/AADAQAAAgL8AAIC/AADAQAAAgD8AAIC/AADAQAAAgD8AAIA/AADAQAAAgL8AAIA/AADAQAAAgL8AAIA/AADAQAAAgD8AAIA/AACAQAAAgD8AAIA/AACAQAAAgL8AAIA/AACAQAAAgL8AAIC/AADAQAAAgL8AAIC/AADAQAAAgL8AAIA/AACAQAAAgL8AAIA/AADAQAAAgD8AAIC/AACAQAAAgD8AAIC/AACAQAAAgD8AAIA/AADAQAAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcA" } - }, - "bufferViews": { - "bufferView_vertex": { - "buffer": "buffer", + ], + "bufferViews": [ + { + "name": "bufferView_vertex", + "buffer": 0, "byteLength": 2304, "byteOffset": 0, "target": 34962 }, - "bufferView_index": { - "buffer": "buffer", + { + "name": "bufferView_index", + "buffer": 0, "byteLength": 216, "byteOffset": 2304, "target": 34963 } - }, - "extensionsUsed": [ - "KHR_materials_common" ], - "images": {}, - "materials": { - "Blue": { - "extensions": { - "KHR_materials_common": { - "technique": "PHONG", - "transparent": false, - "doubleSided": false, - "values": { - "ambient": [ - 0, - 0, - 0, - 1 - ], - "diffuse": [ - 0, - 0, - 0.64, - 1 - ], - "emission": [ - 0, - 0, - 0, - 1 - ], - "specular": [ - 0.5, - 0.5, - 0.5, - 1 - ], - "shininess": 96.078431, - "transparency": 1, - "transparent": false, - "doubleSided": false - } - } - } + "materials": [ + { + "name": "Blue", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0, + 0, + 0.64, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.903921569 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false }, - "Green": { - "extensions": { - "KHR_materials_common": { - "technique": "PHONG", - "transparent": false, - "doubleSided": false, - "values": { - "ambient": [ - 0, - 0, - 0, - 1 - ], - "diffuse": [ - 0, - 0.64, - 0, - 1 - ], - "emission": [ - 0, - 0, - 0, - 1 - ], - "specular": [ - 0.5, - 0.5, - 0.5, - 1 - ], - "shininess": 96.078431, - "transparency": 1, - "transparent": false, - "doubleSided": false - } - } - } + { + "name": "Green", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0, + 0.64, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.903921569 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false }, - "Red": { - "extensions": { - "KHR_materials_common": { - "technique": "PHONG", - "transparent": false, - "doubleSided": false, - "values": { - "ambient": [ - 0, - 0, - 0, - 1 - ], - "diffuse": [ - 0.64, - 0, - 0, - 1 - ], - "emission": [ - 0, - 0, - 0, - 1 - ], - "specular": [ - 0.5, - 0.5, - 0.5, - 1 - ], - "shininess": 96.078431, - "transparency": 1, - "transparent": false, - "doubleSided": false - } - } - } + { + "name": "Red", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.64, + 0, + 0, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.903921569 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false } - }, - "meshes": { - "CubeBlue_CubeBlue_Blue": { - "name": "CubeBlue_CubeBlue_Blue", + ], + "meshes": [ + { + "name": "Blue", "primitives": [ { "attributes": { - "POSITION": "accessor_0", - "NORMAL": "accessor_1", - "TEXCOORD_0": "accessor_2" + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 }, - "indices": "accessor_3", - "material": "Blue", + "indices": 3, + "material": 0, "mode": 4 }, { "attributes": { - "POSITION": "accessor_0", - "NORMAL": "accessor_1", - "TEXCOORD_0": "accessor_2" + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 }, - "indices": "accessor_4", - "material": "Green", + "indices": 4, + "material": 1, "mode": 4 } ] }, - "CubeGreen_CubeGreen_Green": { - "name": "CubeGreen_CubeGreen_Green", + { + "name": "Green", "primitives": [ { "attributes": { - "POSITION": "accessor_5", - "NORMAL": "accessor_6", - "TEXCOORD_0": "accessor_7" + "POSITION": 5, + "NORMAL": 6, + "TEXCOORD_0": 7 }, - "indices": "accessor_8", - "material": "Green", + "indices": 8, + "material": 1, "mode": 4 }, { "attributes": { - "POSITION": "accessor_5", - "NORMAL": "accessor_6", - "TEXCOORD_0": "accessor_7" + "POSITION": 5, + "NORMAL": 6, + "TEXCOORD_0": 7 }, - "indices": "accessor_9", - "material": "Red", + "indices": 9, + "material": 2, "mode": 4 } ] }, - "CubeRed_CubeRed_Red": { - "name": "CubeRed_CubeRed_Red", + { + "name": "Red", "primitives": [ { "attributes": { - "POSITION": "accessor_10", - "NORMAL": "accessor_11", - "TEXCOORD_0": "accessor_12" + "POSITION": 10, + "NORMAL": 11, + "TEXCOORD_0": 12 }, - "indices": "accessor_13", - "material": "Red", + "indices": 13, + "material": 2, "mode": 4 }, { "attributes": { - "POSITION": "accessor_10", - "NORMAL": "accessor_11", - "TEXCOORD_0": "accessor_12" + "POSITION": 10, + "NORMAL": 11, + "TEXCOORD_0": 12 }, - "indices": "accessor_14", - "material": "Blue", + "indices": 14, + "material": 0, "mode": 4 } ] } - }, - "nodes": { - "Cube": { + ], + "nodes": [ + { "name": "Cube", - "meshes": [ - "CubeBlue_CubeBlue_Blue", - "CubeGreen_CubeGreen_Green", - "CubeRed_CubeRed_Red" + "children": [ + 1, + 2, + 3 ] + }, + { + "name": "Blue", + "mesh": 0 + }, + { + "name": "Green", + "mesh": 1 + }, + { + "name": "Red", + "mesh": 2 } - }, - "samplers": {}, - "scene": "scene", - "scenes": { - "scene": { + ], + "scene": 0, + "scenes": [ + { "nodes": [ - "Cube" + 0 ] } - }, - "textures": {} + ] } diff --git a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj index 1bb7698..b2fb821 100644 --- a/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj +++ b/specs/data/box-objects-groups-materials/box-objects-groups-materials.obj @@ -36,7 +36,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeBlue_CubeBlue_Blue +g Blue usemtl Blue f 1/1/1 2/2/1 4/3/1 3/4/1 f 3/5/2 4/6/2 8/7/2 7/8/2 @@ -79,7 +79,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeGreen_CubeGreen_Green +g Green usemtl Green f 9/21/7 10/22/7 12/23/7 11/24/7 f 11/25/8 12/26/8 16/27/8 15/28/8 @@ -122,7 +122,7 @@ vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 1.0000 0.0000 -g CubeRed_CubeRed_Red +g Red usemtl Red f 17/41/13 18/42/13 20/43/13 19/44/13 f 19/45/14 20/46/14 24/47/14 23/48/14 diff --git a/specs/data/box/box.gltf b/specs/data/box/box.gltf index 112389c..cc017b1 100644 --- a/specs/data/box/box.gltf +++ b/specs/data/box/box.gltf @@ -1,9 +1,9 @@ { - "accessors": { - "accessor_0": { - "bufferView": "bufferView_vertex", + "accessors": [ + { + "name": "Cube-Mesh_positions", + "bufferView": 0, "byteOffset": 0, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -18,10 +18,10 @@ ], "type": "VEC3" }, - "accessor_1": { - "bufferView": "bufferView_vertex", + { + "name": "Cube-Mesh_normals", + "bufferView": 0, "byteOffset": 288, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -36,10 +36,10 @@ ], "type": "VEC3" }, - "accessor_2": { - "bufferView": "bufferView_vertex", + { + "name": "Cube-Mesh_texcoords", + "bufferView": 0, "byteOffset": 576, - "byteStride": 0, "componentType": 5126, "count": 24, "min": [ @@ -52,10 +52,10 @@ ], "type": "VEC2" }, - "accessor_3": { - "bufferView": "bufferView_index", + { + "name": "Cube-Mesh_0_indices", + "bufferView": 1, "byteOffset": 0, - "byteStride": 0, "componentType": 5123, "count": 36, "min": [ @@ -66,113 +66,85 @@ ], "type": "SCALAR" } - }, + ], "asset": { "generator": "obj2gltf", - "profile": { - "api": "WebGL", - "version": "1.0" - }, - "version": "1.0" + "version": "2.0" }, - "buffers": { - "buffer": { + "buffers": [ + { + "name": "buffer", "byteLength": 840, "uri": "data:application/octet-stream;base64,AACAvwAAgL8AAIA/AACAvwAAgD8AAIA/AACAvwAAgD8AAIC/AACAvwAAgL8AAIC/AACAvwAAgL8AAIC/AACAvwAAgD8AAIC/AACAPwAAgD8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgD8AAIC/AACAPwAAgD8AAIA/AACAPwAAgL8AAIA/AACAPwAAgL8AAIA/AACAPwAAgD8AAIA/AACAvwAAgD8AAIA/AACAvwAAgL8AAIA/AACAvwAAgL8AAIC/AACAPwAAgL8AAIC/AACAPwAAgL8AAIA/AACAvwAAgL8AAIA/AACAPwAAgD8AAIC/AACAvwAAgD8AAIC/AACAvwAAgD8AAIA/AACAPwAAgD8AAIA/AACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAACAvwAAAAAAAAAAAAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AAAAAAAAAAAAAIC/AACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAAAAAAIA/AAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgL8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgD8AAAAAAAAAAAAAAAAAAAAAAACAPwAAgD8AAIA/AACAPwAAAAAAAAAAAAAAAAAAAAAAAIA/AACAPwAAgD8AAIA/AAAAAAAAAAAAAAAAAAABAAIAAAACAAMABAAFAAYABAAGAAcACAAJAAoACAAKAAsADAANAA4ADAAOAA8AEAARABIAEAASABMAFAAVABYAFAAWABcA" } - }, - "bufferViews": { - "bufferView_vertex": { - "buffer": "buffer", + ], + "bufferViews": [ + { + "name": "bufferView_vertex", + "buffer": 0, "byteLength": 768, "byteOffset": 0, "target": 34962 }, - "bufferView_index": { - "buffer": "buffer", + { + "name": "bufferView_index", + "buffer": 0, "byteLength": 72, "byteOffset": 768, "target": 34963 } - }, - "extensionsUsed": [ - "KHR_materials_common" ], - "images": {}, - "materials": { - "Material": { - "extensions": { - "KHR_materials_common": { - "technique": "PHONG", - "transparent": false, - "doubleSided": false, - "values": { - "ambient": [ - 0, - 0, - 0, - 1 - ], - "diffuse": [ - 0.64, - 0.64, - 0.64, - 1 - ], - "emission": [ - 0, - 0, - 0, - 1 - ], - "specular": [ - 0.5, - 0.5, - 0.5, - 1 - ], - "shininess": 96.078431, - "transparency": 1, - "transparent": false, - "doubleSided": false - } - } - } + "materials": [ + { + "name": "Material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.64, + 0.64, + 0.64, + 1 + ], + "metallicFactor": 0, + "roughnessFactor": 0.903921569 + }, + "emissiveFactor": [ + 0, + 0, + 0 + ], + "alphaMode": "OPAQUE", + "doubleSided": false } - }, - "meshes": { - "Cube-Mesh": { + ], + "meshes": [ + { "name": "Cube-Mesh", "primitives": [ { "attributes": { - "POSITION": "accessor_0", - "NORMAL": "accessor_1", - "TEXCOORD_0": "accessor_2" + "POSITION": 0, + "NORMAL": 1, + "TEXCOORD_0": 2 }, - "indices": "accessor_3", - "material": "Material", + "indices": 3, + "material": 0, "mode": 4 } ] } - }, - "nodes": { - "Cube": { + ], + "nodes": [ + { "name": "Cube", - "meshes": [ - "Cube-Mesh" - ] + "mesh": 0 } - }, - "samplers": {}, - "scene": "scene", - "scenes": { - "scene": { + ], + "scene": 0, + "scenes": [ + { "nodes": [ - "Cube" + 0 ] } - }, - "textures": {} -} \ No newline at end of file + ] +} diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 22c8c77..e2ce4b4 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -26,9 +26,16 @@ var defaultOptions = obj2gltf.defaults; var checkTransparencyOptions = clone(defaultOptions); checkTransparencyOptions.checkTransparency = true; +function setDefaultMaterial(objData) { + var originalMaterial = objData.materials[0]; + var defaultMaterial = new Material(); + defaultMaterial.name = originalMaterial.name; + objData.materials[0] = defaultMaterial; + return defaultMaterial; +} + describe('createGltf', function() { var boxObjData; - var duplicateBoxObjData; var groupObjData; var boxGltf; var groupGltf; @@ -41,10 +48,6 @@ describe('createGltf', function() { .then(function(data) { boxObjData = data; }), - loadObj(boxObjUrl, defaultOptions) - .then(function(data) { - duplicateBoxObjData = data; - }), loadObj(groupObjUrl, defaultOptions) .then(function(data) { groupObjData = data; @@ -82,25 +85,69 @@ describe('createGltf', function() { expect(writeUris(gltf, groupGltfUrl, path.dirname(groupGltfUrl), defaultOptions) .then(function() { expect(gltf).toEqual(groupGltf); - expect(Object.keys(gltf.materials).length).toBe(3); - expect(Object.keys(gltf.nodes).length).toBe(1); - expect(Object.keys(gltf.meshes).length).toBe(3); + expect(gltf.materials.length).toBe(3); + expect(gltf.nodes.length).toBe(4); + expect(gltf.nodes[0].mesh).toBeUndefined(); + expect(gltf.nodes[0].children.length).toBe(3); + expect(gltf.meshes.length).toBe(3); // Check for two primitives in each mesh - for (var id in gltf.meshes) { - if (gltf.meshes.hasOwnProperty(id)) { - var mesh = gltf.meshes[id]; - expect(mesh.primitives.length).toBe(2); - } + var length = gltf.meshes.length; + for (var i = 0; i < length; ++i) { + var mesh = gltf.meshes[i]; + expect(mesh.primitives.length).toBe(2); } }), done).toResolve(); }); it('sets default material values', function() { - boxObjData.materials.Material = new Material(); + // Will convert traditional material to metallic-roughness + setDefaultMaterial(boxObjData); var gltf = createGltf(boxObjData, defaultOptions); - var material = gltf.materials.Material; + var material = gltf.materials[0]; + var pbr = material.pbrMetallicRoughness; + expect(pbr.baseColorTexture).toBeUndefined(); + expect(pbr.metallicRoughnessTexture).toBeUndefined(); + expect(pbr.baseColorFactor).toEqual([0.5, 0.5, 0.5, 1.0]); + expect(pbr.metallicFactor).toBe(0.0); // No metallic + expect(pbr.roughnessFactor).toBe(1.0); // Fully rough + expect(material.emissiveTexture).toBe(undefined); + expect(material.normalTexture).toBe(undefined); + expect(material.occlusionTexture).toBe(undefined); + expect(material.emissiveFactor).toEqual([0.0, 0.0, 0.0]); + }); + + it('sets default material values for metallicRoughness', function() { + // No conversion applied when metallicRoughness flag is set + var options = clone(defaultOptions); + options.metallicRoughness = true; + + var defaultMaterial = setDefaultMaterial(boxObjData); + defaultMaterial.specularShininess = 1.0; // This is the default set in loadMtl + + var gltf = createGltf(boxObjData, options); + var material = gltf.materials[0]; + var pbr = material.pbrMetallicRoughness; + expect(pbr.baseColorTexture).toBeUndefined(); + expect(pbr.metallicRoughnessTexture).toBeUndefined(); + expect(pbr.baseColorFactor).toEqual([0.5, 0.5, 0.5, 1.0]); + expect(pbr.metallicFactor).toBe(0.0); // No metallic + expect(pbr.roughnessFactor).toBe(1.0); // Fully rough + expect(material.emissiveTexture).toBe(undefined); + expect(material.normalTexture).toBe(undefined); + expect(material.occlusionTexture).toBe(undefined); + expect(material.emissiveFactor).toEqual([0.0, 0.0, 0.0]); + }); + + it('sets default material values for materialsCommon', function() { + var options = clone(defaultOptions); + options.materialsCommon = true; + + setDefaultMaterial(boxObjData); + + var gltf = createGltf(boxObjData, options); + var material = gltf.materials[0]; var kmc = material.extensions.KHR_materials_common; var values = kmc.values; @@ -110,12 +157,15 @@ describe('createGltf', function() { expect(values.emission).toEqual([0.0, 0.0, 0.0, 1]); expect(values.specular).toEqual([0.0, 0.0, 0.0, 1]); expect(values.shininess).toEqual(0.0); + expect(values.transparency).toBe(1.0); + expect(values.transparent).toBe(false); + expect(values.doubleSided).toBe(false); }); it('sets material for diffuse texture', function() { var material = new Material(); material.diffuseTexture = diffuseTextureUrl; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; boxObjData.images[diffuseTextureUrl] = diffuseTexture; var gltf = createGltf(boxObjData, defaultOptions); @@ -154,7 +204,7 @@ describe('createGltf', function() { it('sets material for alpha less than 1', function() { var material = new Material(); material.alpha = 0.4; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; var gltf = createGltf(boxObjData, defaultOptions); var kmc = gltf.materials.Material.extensions.KHR_materials_common; @@ -169,7 +219,7 @@ describe('createGltf', function() { var material = new Material(); material.diffuseTexture = diffuseTextureUrl; material.alpha = 0.4; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; boxObjData.images[diffuseTextureUrl] = diffuseTexture; @@ -185,7 +235,7 @@ describe('createGltf', function() { it('sets material for transparent diffuse texture', function() { var material = new Material(); material.diffuseTexture = transparentDiffuseTextureUrl; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; boxObjData.images[transparentDiffuseTextureUrl] = transparentDiffuseTexture; @@ -202,7 +252,7 @@ describe('createGltf', function() { var material = new Material(); material.specularColor = [0.1, 0.1, 0.2, 1]; material.specularShininess = 0.1; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; var gltf = createGltf(boxObjData, defaultOptions); var kmc = gltf.materials.Material.extensions.KHR_materials_common; @@ -217,7 +267,7 @@ describe('createGltf', function() { var material = new Material(); material.diffuseTexture = diffuseTextureUrl; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; boxObjData.images[diffuseTextureUrl] = diffuseTexture; @@ -231,10 +281,10 @@ describe('createGltf', function() { it('sets default material when texture is missing', function() { var material = new Material(); material.diffuseTexture = diffuseTextureUrl; - boxObjData.materials.Material = material; + boxObjData.materials[0] = material; var gltf = createGltf(boxObjData, defaultOptions); - var kmc = gltf.materials.Material.extensions.KHR_materials_common; + var kmc = gltf.materials[0].extensions.KHR_materials_common; expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]); }); @@ -244,8 +294,8 @@ describe('createGltf', function() { // Creates a material called "default" var gltf = createGltf(boxObjData, defaultOptions); - expect(gltf.materials.default).toBeDefined(); - var kmc = gltf.materials.default.extensions.KHR_materials_common; + expect(gltf.materials[0].name).toBe('default'); + var kmc = gltf.materials[0].extensions.KHR_materials_common; expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]); }); @@ -254,37 +304,11 @@ describe('createGltf', function() { // Uses the original name of the material var gltf = createGltf(boxObjData, defaultOptions); - var kmc = gltf.materials.Material.extensions.KHR_materials_common; + var kmc = gltf.materials[0].extensions.KHR_materials_common; expect(kmc.values.diffuse).toEqual([0.5, 0.5, 0.5, 1.0]); }); - it('handles material used with and without normals (1)', function() { - // Two meshes - one with normals, and one without - boxObjData.nodes.push(duplicateBoxObjData.nodes[0]); - boxObjData.nodes[1].meshes[0].normals.length = 0; - - var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_constant.extensions.KHR_materials_common; - - expect(kmc1.technique).toBe('PHONG'); - expect(kmc2.technique).toBe('CONSTANT'); - }); - - it('handles material used with and without normals (2)', function() { - // Now test in a different order - boxObjData.nodes.push(duplicateBoxObjData.nodes[0]); - boxObjData.nodes[0].meshes[0].normals.length = 0; - - var gltf = createGltf(boxObjData, defaultOptions); - var kmc1 = gltf.materials.Material.extensions.KHR_materials_common; - var kmc2 = gltf.materials.Material_shaded.extensions.KHR_materials_common; - - expect(kmc1.technique).toBe('CONSTANT'); - expect(kmc2.technique).toBe('PHONG'); - }); - it('runs without normals', function() { boxObjData.nodes[0].meshes[0].normals.length = 0; diff --git a/specs/lib/loadImageSpec.js b/specs/lib/loadImageSpec.js index 047cd44..a1c6185 100644 --- a/specs/lib/loadImageSpec.js +++ b/specs/lib/loadImageSpec.js @@ -1,10 +1,7 @@ 'use strict'; -var Cesium = require('cesium'); var obj2gltf = require('../../lib/obj2gltf'); var loadImage = require('../../lib/loadImage'); -var clone = Cesium.clone; - var pngImage = 'specs/data/box-complex-material/shininess.png'; var jpgImage = 'specs/data/box-complex-material/emission.jpg'; var jpegImage = 'specs/data/box-complex-material/specular.jpeg'; @@ -12,47 +9,58 @@ var gifImage = 'specs/data/box-complex-material/ambient.gif'; var grayscaleImage = 'specs/data/box-complex-material/alpha.png'; var transparentImage = 'specs/data/box-complex-material/diffuse.png'; -var defaultOptions = obj2gltf.defaults; - describe('loadImage', function() { it('loads png image', function(done) { - expect(loadImage(pngImage, defaultOptions) + expect(loadImage(pngImage) .then(function(info) { expect(info.transparent).toBe(false); expect(info.source).toBeDefined(); expect(info.extension).toBe('.png'); + expect(info.path).toBe(pngImage); + expect(info.decoded).toBeUndefined(); + expect(info.width).toBeUndefined(); + expect(info.height).toBeUndefined(); }), done).toResolve(); }); it('loads jpg image', function(done) { - expect(loadImage(jpgImage, defaultOptions) + expect(loadImage(jpgImage) .then(function(info) { expect(info.transparent).toBe(false); expect(info.source).toBeDefined(); expect(info.extension).toBe('.jpg'); + expect(info.decoded).toBeUndefined(); + expect(info.width).toBeUndefined(); + expect(info.height).toBeUndefined(); }), done).toResolve(); }); it('loads jpeg image', function(done) { - expect(loadImage(jpegImage, defaultOptions) + expect(loadImage(jpegImage) .then(function(info) { expect(info.transparent).toBe(false); expect(info.source).toBeDefined(); expect(info.extension).toBe('.jpeg'); + expect(info.decoded).toBeUndefined(); + expect(info.width).toBeUndefined(); + expect(info.height).toBeUndefined(); }), done).toResolve(); }); it('loads gif image', function(done) { - expect(loadImage(gifImage, defaultOptions) + expect(loadImage(gifImage) .then(function(info) { expect(info.transparent).toBe(false); expect(info.source).toBeDefined(); expect(info.extension).toBe('.gif'); + expect(info.decoded).toBeUndefined(); + expect(info.width).toBeUndefined(); + expect(info.height).toBeUndefined(); }), done).toResolve(); }); it('loads grayscale image', function(done) { - expect(loadImage(grayscaleImage, defaultOptions) + expect(loadImage(grayscaleImage) .then(function(info) { expect(info.transparent).toBe(false); expect(info.source).toBeDefined(); @@ -61,19 +69,46 @@ describe('loadImage', function() { }); it('loads image with alpha channel', function(done) { - expect(loadImage(transparentImage, defaultOptions) + expect(loadImage(transparentImage) .then(function(info) { expect(info.transparent).toBe(false); }), done).toResolve(); }); it('loads image with checkTransparency flag', function(done) { - var options = clone(defaultOptions); - options.checkTransparency = true; + var options = { + checkTransparency : true + }; expect(loadImage(transparentImage, options) .then(function(info) { expect(info.transparent).toBe(true); }), done).toResolve(); }); + + it('loads and decodes png', function(done) { + var options = { + decode : true + }; + + expect(loadImage(pngImage, options) + .then(function(info) { + expect(info.decoded).toBeDefined(); + expect(info.width).toBe(211); + expect(info.height).toBe(211); + }), done).toResolve(); + }); + + it('loads and decodes jpeg', function(done) { + var options = { + decode : true + }; + + expect(loadImage(jpegImage, options) + .then(function(info) { + expect(info.decoded).toBeDefined(); + expect(info.width).toBe(211); + expect(info.height).toBe(211); + }), done).toResolve(); + }); }); diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index ddecf09..eca3c9e 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -1,6 +1,7 @@ 'use strict'; var path = require('path'); var loadMtl = require('../../lib/loadMtl'); +var obj2gltf = require('../../lib/obj2gltf'); var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; @@ -9,12 +10,14 @@ function getImagePath(objPath, relativePath) { return path.resolve(path.dirname(objPath), relativePath); } +var defaultOptions = obj2gltf.defaults; + describe('loadMtl', function() { it('loads complex material', function(done) { - expect(loadMtl(complexMaterialUrl) + expect(loadMtl(complexMaterialUrl, defaultOptions) .then(function(materials) { - var material = materials.Material; - expect(material).toBeDefined(); + var material = materials[0]; + expect(material.name).toBe('Material'); expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]); expect(material.emissiveColor).toEqual([0.1, 0.1, 0.1, 1.0]); expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]); @@ -32,12 +35,15 @@ describe('loadMtl', function() { }); it('loads mtl with multiple materials', function(done) { - expect(loadMtl(multipleMaterialsUrl) + expect(loadMtl(multipleMaterialsUrl, defaultOptions) .then(function(materials) { - expect(Object.keys(materials).length).toBe(3); - expect(materials.Red.diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); - expect(materials.Green.diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); - expect(materials.Blue.diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials.length).toBe(3); + expect(materials[0].name).toBe('Blue'); + expect(materials[0].diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials[1].name).toBe('Green'); + expect(materials[1].diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); + expect(materials[2].name).toBe('Red'); + expect(materials[2].diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); }), done).toResolve(); }); }); diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index ca7d2a7..3b0833a 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -77,8 +77,8 @@ describe('loadObj', function() { var meshes = getMeshes(data); var primitives = getPrimitives(data); - expect(Object.keys(images).length).toBe(0); - expect(materials.Material).toBeDefined(); + expect(images.length).toBe(0); + expect(materials.length).toBe(1); expect(nodes.length).toBe(1); expect(meshes.length).toBe(1); expect(primitives.length).toBe(1); @@ -268,17 +268,26 @@ describe('loadObj', function() { expect(loadObj(objMtllibUrl, defaultOptions) .then(function(data) { var materials = data.materials; - expect(Object.keys(materials).length).toBe(3); - expect(materials.Red.diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); - expect(materials.Green.diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); - expect(materials.Blue.diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials.length).toBe(3); + + // .mtl files are loaded in an arbitrary order, so sort for testing purposes + materials.sort(function(a, b){ + return a.name.localeCompare(b.name); + }); + + expect(materials[0].name).toBe('Blue'); + expect(materials[0].diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials[1].name).toBe('Green'); + expect(materials[1].diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); + expect(materials[2].name).toBe('Red'); + expect(materials[2].diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); }), done).toResolve(); }); it('loads obj with missing mtllib', function(done) { expect(loadObj(objMissingMtllibUrl, defaultOptions) .then(function(data) { - expect(data.materials).toEqual({}); + expect(data.materials.length).toBe(0); expect(console.log.calls.argsFor(0)[0].indexOf('Could not read mtl file') >= 0).toBe(true); }), done).toResolve(); }); @@ -287,8 +296,14 @@ describe('loadObj', function() { expect(loadObj(objExternalResourcesUrl, defaultOptions) .then(function(data) { var imagePath = getImagePath(objTexturedUrl, 'cesium.png'); - expect(data.images[imagePath]).toBeDefined(); - expect(data.materials.MaterialTextured.diffuseTexture).toEqual(imagePath); + expect(data.images[0].path).toBe(imagePath); + + var materials = data.materials; + expect(materials.length).toBe(2); + + // .mtl files are loaded in an arbitrary order, so find the "MaterialTextured" material + var materialTextured = materials[0].name === 'MaterialTextured' ? materials[0] : materials[1]; + expect(materialTextured.diffuseTexture).toEqual(imagePath); }), done).toResolve(); }); @@ -299,9 +314,8 @@ describe('loadObj', function() { expect(loadObj(objExternalResourcesUrl, options) .then(function(data) { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); - expect(data.images[imagePath]).toBeUndefined(); - expect(data.materials.MaterialTextured).toBeDefined(); - expect(data.materials.Material).toBeUndefined(); // Not in directory, so not included + expect(data.images.length).toBe(0); // obj references an image file that is outside the input directory + expect(data.materials.length).toBe(1); // obj references 2 materials, one of which is outside the input directory expect(console.log.calls.argsFor(0)[0].indexOf('Could not read mtl file') >= 0).toBe(true); expect(console.log.calls.argsFor(1)[0].indexOf('Could not read image file') >= 0).toBe(true); }), done).toResolve(); @@ -311,8 +325,8 @@ describe('loadObj', function() { expect(loadObj(objTexturedUrl, defaultOptions) .then(function(data) { var imagePath = getImagePath(objTexturedUrl, 'cesium.png'); - expect(data.images[imagePath]).toBeDefined(); - expect(data.materials.Material.diffuseTexture).toEqual(imagePath); + expect(data.images[0].path).toBe(imagePath); + expect(data.materials[0].diffuseTexture).toEqual(imagePath); }), done).toResolve(); }); @@ -320,8 +334,8 @@ describe('loadObj', function() { expect(loadObj(objMissingTextureUrl, defaultOptions) .then(function(data) { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); - expect(data.images[imagePath]).toBeUndefined(); - expect(data.materials.Material.diffuseTexture).toEqual(imagePath); + expect(data.images.length).toBe(0); + expect(data.materials[0].diffuseTexture).toEqual(imagePath); expect(console.log.calls.argsFor(0)[0].indexOf('Could not read image file') >= 0).toBe(true); }), done).toResolve(); }); @@ -330,8 +344,8 @@ describe('loadObj', function() { expect(loadObj(objSubdirectoriesUrl, defaultOptions) .then(function(data) { var imagePath = getImagePath(objSubdirectoriesUrl, path.join('materials', 'images', 'cesium.png')); - expect(data.images[imagePath]).toBeDefined(); - expect(data.materials.Material.diffuseTexture).toEqual(imagePath); + expect(data.images[0].path).toBe(imagePath); + expect(data.materials[0].diffuseTexture).toEqual(imagePath); }), done).toResolve(); }); @@ -339,7 +353,7 @@ describe('loadObj', function() { expect(loadObj(objComplexMaterialUrl, defaultOptions) .then(function(data) { var images = data.images; - expect(Object.keys(images).length).toBe(4); // Only ambient, diffuse, emission, and specular maps are supported by the converter + expect(images.length).toBe(6); }), done).toResolve(); }); diff --git a/specs/lib/obj2gltfSpec.js b/specs/lib/obj2gltfSpec.js index 22a9bdd..8054fbe 100644 --- a/specs/lib/obj2gltfSpec.js +++ b/specs/lib/obj2gltfSpec.js @@ -1,4 +1,5 @@ 'use strict'; +var Cesium = require('Cesium'); var fsExtra = require('fs-extra'); var GltfPipeline = require('gltf-pipeline').Pipeline; var os = require('os'); @@ -7,6 +8,8 @@ var Promise = require('bluebird'); var obj2gltf = require('../../lib/obj2gltf'); var writeUris = require('../../lib/writeUris'); +var RuntimeError = Cesium.RuntimeError; + var objPath = 'specs/data/box-textured/box-textured.obj'; var gltfPath = 'specs/data/box-textured/box-textured.gltf'; var glbPath = 'specs/data/box-textured/box-textured.glb'; @@ -37,7 +40,7 @@ describe('obj2gltf', function() { var options = args[2]; expect(path.normalize(outputPath)).toEqual(path.normalize(gltfPath)); expect(gltf).toBeDefined(); - expect(gltf.images.cesium).toBeDefined(); + expect(gltf.images.length).toBe(1); expect(options).toEqual({ basePath : tempDirectory, createDirectory : false, @@ -131,9 +134,25 @@ describe('obj2gltf', function() { }).toThrowDeveloperError(); }); - it('rejects if gltfPath is undefined', function() { + it('throws if gltfPath is undefined', function() { expect(function() { obj2gltf(objPath, undefined); }).toThrowDeveloperError(); }); + + it('rejects if both bpypassPipeline and binary are true', function(done) { + var options = { + bypassPipeline : true, + binary : true + }; + expect(obj2gltf(objPath, gltfPath, options), done).toRejectWith(RuntimeError); + }); + + it('rejects if more than one material type is set', function(done) { + var options = { + metallicRoughness : true, + specularGlossiness : true + }; + expect(obj2gltf(objPath, gltfPath, options), done).toRejectWith(RuntimeError); + }); });