diff --git a/CHANGES.md b/CHANGES.md index 70eb28e..191806c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -3,6 +3,16 @@ Change Log ### 1.3.5 ????-??-?? +* Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Improved parsing of faces with mismatching attributes. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Added ability to use the first material in the mtl file when the obj is missing `usemtl`. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of unnormalized input normals. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of materials where the diffuse and ambient texture are the same. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed handling of `usemtl` when appearing before an `o` or `g` token. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading faces that contain less than 3 vertices. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Attempt to load missing materials and textures from within the same directory as the obj. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) +* Fixed loading mtllib paths that contain spaces. [#163](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/163) * Improved handling of materials with alpha. If the alpha value is 0.0 it is now treated as 1.0. [#164](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/164) ### 1.3.4 2018-10-16 @@ -13,6 +23,8 @@ Change Log ### 1.3.3 2018-09-19 * Fixed handling of objs with mismatching attribute layouts. [#154](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/154) +* Fixed parsing mtl textures that contain texture map options. [#151](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/151) +* Fixed normalization of Windows paths when running the converter on Linux. [#151](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/151) ### 1.3.2 2018-06-07 * Fixed greyscale images loading as alpha instead of luminance. [#144](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/144) diff --git a/lib/createGltf.js b/lib/createGltf.js index f183757..c1ef37d 100644 --- a/lib/createGltf.js +++ b/lib/createGltf.js @@ -3,8 +3,9 @@ var Cesium = require('cesium'); var path = require('path'); var Material = require('./Material'); -var defined = Cesium.defined; +var clone = Cesium.clone; var defaultValue = Cesium.defaultValue; +var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; module.exports = createGltf; @@ -27,6 +28,9 @@ function createGltf(objData, options) { var vertexBufferViewId = 'bufferView_vertex'; var indexBufferViewId = 'bufferView_index'; + // Split materials used by primitives with different types of attributes + materials = splitIncompatibleMaterials(nodes, materials); + var gltf = { accessors : {}, asset : {}, @@ -290,23 +294,8 @@ function createGltf(objData, options) { primitive.uvs = undefined; primitive.indices = undefined; - if (!defined(materialId)) { - // Create a default material if the primitive does not specify one - materialId = 'default'; - } - var material = materials[materialId]; - material = defined(material) ? material : new Material(); 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, options); } @@ -350,3 +339,75 @@ function createGltf(objData, options) { return gltf; } + +function primitiveInfoMatch(a, b) { + return a.hasUvs === b.hasUvs && + a.hasNormals === b.hasNormals; +} + +function cloneMaterial(material, removeTextures) { + material = clone(material, true); + if (removeTextures) { + material.ambientTexture = undefined; + material.emissionTexture = undefined; + material.diffuseTexture = undefined; + material.specularTexture = undefined; + material.specularShininessMap = undefined; + material.normalMap = undefined; + material.alphaMap = undefined; + } + return material; +} + +function getSplitMaterialName(originalMaterialName, primitiveInfo, primitiveInfoByMaterial) { + var splitMaterialName = originalMaterialName; + var suffix = 2; + while (defined(primitiveInfoByMaterial[splitMaterialName])) { + if (primitiveInfoMatch(primitiveInfo, primitiveInfoByMaterial[splitMaterialName])) { + break; + } + splitMaterialName = originalMaterialName + '-' + suffix++; + } + return splitMaterialName; +} + +function splitIncompatibleMaterials(nodes, materials) { + 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 splitMaterialName = getSplitMaterialName(originalMaterialName, primitiveInfo, primitiveInfoByMaterial); + primitive.material = splitMaterialName; + primitiveInfoByMaterial[splitMaterialName] = primitiveInfo; + + var splitMaterial = splitMaterials[splitMaterialName]; + if (defined(splitMaterial)) { + continue; + } + + var originalMaterial = materials[originalMaterialName]; + if (defined(originalMaterial)) { + splitMaterial = cloneMaterial(originalMaterial, !hasUvs); + } else { + splitMaterial = new Material(); + } + splitMaterials[splitMaterialName] = splitMaterial; + } + } + } + return splitMaterials; +} diff --git a/lib/loadMtl.js b/lib/loadMtl.js index ac17648..3a04a10 100644 --- a/lib/loadMtl.js +++ b/lib/loadMtl.js @@ -86,10 +86,23 @@ function loadMtl(mtlPath) { return readLines(mtlPath, parseLine) .then(function() { + cleanupMaterials(materials); return materials; }); } +function cleanupMaterials(materials) { + for (var name in materials) { + if (materials.hasOwnProperty(name)) { + var material = materials[name]; + if (material.diffuseTexture === material.ambientTexture) { + // OBJ models are often exported with the same texture in the diffuse and ambient slots but this is usually not desirable + material.ambientTexture = undefined; + } + } + } +} + function correctAlpha(alpha) { // An alpha of 0.0 usually implies a problem in the export, change to 1.0 instead return alpha === 0.0 ? 1.0 : alpha; diff --git a/lib/loadObj.js b/lib/loadObj.js index 8659507..b02bea2 100644 --- a/lib/loadObj.js +++ b/lib/loadObj.js @@ -78,6 +78,7 @@ function loadObj(objPath, options) { var node; var mesh; var primitive; + var activeMaterial; // All nodes seen in the obj var nodes = []; @@ -125,6 +126,7 @@ function loadObj(objPath, options) { function addPrimitive() { primitive = new Primitive(); + primitive.material = activeMaterial; mesh.primitives.push(primitive); // Clear the vertex cache for each new primitive @@ -132,22 +134,42 @@ function loadObj(objPath, options) { vertexCount = 0; } - function useMaterial(name) { - // Look to see if this material has already been used by a primitive in the mesh - var material = getName(name); + function reusePrimitive(callback) { var primitives = mesh.primitives; var primitivesLength = primitives.length; for (var i = 0; i < primitivesLength; ++i) { - if (primitives[i].material === material) { - primitive = primitives[i]; - clearVertexCache(); - vertexCount = primitive.positions.length / 3; - return; + if (primitives[i].material === activeMaterial) { + if (!defined(callback) || callback(primitives[i])) { + primitive = primitives[i]; + clearVertexCache(); + vertexCount = primitive.positions.length / 3; + return; + } } } - // Add a new primitive with this material addPrimitive(); - primitive.material = getName(name); + } + + function useMaterial(name) { + activeMaterial = getName(name); + reusePrimitive(); + } + + function faceAndPrimitiveMatch(uvs, normals, primitive) { + var faceHasUvs = uvs[0].length > 0; + var faceHasNormals = normals[0].length > 0; + var primitiveHasUvs = primitive.uvs.length > 0; + var primitiveHasNormals = primitive.normals.length > 0; + return primitiveHasUvs === faceHasUvs && primitiveHasNormals === faceHasNormals; + } + + function checkPrimitive(uvs, normals) { + var firstFace = primitive.indices.length === 0; + if (!firstFace && !faceAndPrimitiveMatch(uvs, normals, primitive)) { + reusePrimitive(function(primitive) { + return faceAndPrimitiveMatch(uvs, normals, primitive); + }); + } } function getOffset(a, attributeData, components) { @@ -269,16 +291,7 @@ function loadObj(objPath, options) { function addFace(vertices, positions, uvs, normals) { var i; var isWindingCorrect; - - var firstFace = primitive.indices.length === 0; - var faceHasUvs = uvs[0].length > 0; - var faceHasNormals = normals[0].length > 0; - var primitiveHasUvs = primitive.uvs.length > 0; - var primitiveHasNormals = primitive.normals.length > 0; - if (!firstFace && (faceHasUvs !== primitiveHasUvs || faceHasNormals !== primitiveHasNormals)) { - // Discard faces that don't use the same attributes - return; - } + checkPrimitive(uvs, normals); if (vertices.length === 3) { isWindingCorrect = checkWindingCorrect(positions[0], positions[1], positions[2], normals[0]); @@ -333,8 +346,8 @@ function loadObj(objPath, options) { var materialName = line.substring(7).trim(); useMaterial(materialName); } else if (/^mtllib/i.test(line)) { - var paths = line.substring(7).trim().split(' '); - mtlPaths = mtlPaths.concat(paths); + var mtllibLine = line.substring(7).trim(); + mtlPaths = mtlPaths.concat(getMtlPaths(mtllibLine)); } else if ((result = vertexPattern.exec(line)) !== null) { var position = scratchCartesian; position.x = parseFloat(result[1]); @@ -347,10 +360,12 @@ function loadObj(objPath, options) { positions.push(position.y); positions.push(position.z); } else if ((result = normalPattern.exec(line) ) !== null) { - var normal = scratchCartesian; - normal.x = parseFloat(result[1]); - normal.y = parseFloat(result[2]); - normal.z = parseFloat(result[3]); + var normal = Cartesian3.fromElements(parseFloat(result[1]), parseFloat(result[2]), parseFloat(result[3]), scratchNormal); + if (Cartesian3.equals(normal, Cartesian3.ZERO)) { + Cartesian3.clone(Cartesian3.UNIT_Z, normal); + } else { + Cartesian3.normalize(normal, normal); + } if (defined(axisTransform)) { Matrix4.multiplyByPointAsVector(axisTransform, normal, normal); } @@ -375,7 +390,9 @@ function loadObj(objPath, options) { faceUvs.push(result[2]); faceNormals.push(result[3]); } - addFace(faceVertices, facePositions, faceUvs, faceNormals); + if (faceVertices.length > 2) { + addFace(faceVertices, facePositions, faceUvs, faceNormals); + } faceVertices.length = 0; facePositions.length = 0; @@ -398,17 +415,37 @@ function loadObj(objPath, options) { uvs = undefined; // Load materials and images - return finishLoading(nodes, mtlPaths, objPath, options); + return finishLoading(nodes, mtlPaths, objPath, defined(activeMaterial), options); }); } -function finishLoading(nodes, mtlPaths, objPath, options) { +function getMtlPaths(mtllibLine) { + // Handle paths with spaces. E.g. mtllib my material file.mtl + var mtlPaths = []; + var splits = mtllibLine.split(' '); + var length = splits.length; + var startIndex = 0; + for (var i = 0; i < length; ++i) { + if (path.extname(splits[i]) !== '.mtl') { + continue; + } + var mtlPath = splits.slice(startIndex, i + 1).join(' '); + mtlPaths.push(mtlPath); + startIndex = i + 1; + } + return mtlPaths; +} + +function finishLoading(nodes, mtlPaths, objPath, usesMaterials, options) { nodes = cleanNodes(nodes); if (nodes.length === 0) { - return Promise.reject(new RuntimeError(objPath + ' does not have any geometry data')); + throw new RuntimeError(objPath + ' does not have any geometry data'); } - return loadMaterials(mtlPaths, objPath, options) + return loadMtls(mtlPaths, objPath, options) .then(function(materials) { + if (Object.keys(materials).length > 0 && !usesMaterials) { + assignDefaultMaterial(nodes, materials); + } var imagePaths = getImagePaths(materials); return loadImages(imagePaths, objPath, options) .then(function(images) { @@ -426,50 +463,90 @@ function normalizeMtlPath(mtlPath, objDirectory) { return path.normalize(path.join(objDirectory, mtlPath)); } -function outsideDirectory(filePath, objPath) { - return (path.relative(path.dirname(objPath), filePath).indexOf('..') === 0); +function outsideDirectory(file, directory) { + return (path.relative(directory, file).indexOf('..') === 0); } -function loadMaterials(mtlPaths, objPath, options) { - var secure = options.secure; - var logger = options.logger; +function loadMtls(mtlPaths, objPath, options) { var objDirectory = path.dirname(objPath); var materials = {}; + + // Remove duplicates + mtlPaths = mtlPaths.filter(function(value, index, self) { + return self.indexOf(value) === index; + }); + return Promise.map(mtlPaths, function(mtlPath) { mtlPath = normalizeMtlPath(mtlPath, objDirectory); - if (secure && outsideDirectory(mtlPath, objPath)) { - 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; + var shallowPath = path.join(objDirectory, path.basename(mtlPath)); + if (options.secure && outsideDirectory(mtlPath, objDirectory)) { + // Try looking for the .mtl in the same directory as the obj + options.logger('The material file is outside of the obj directory and the secure flag is true. Attempting to read the material file from within the obj directory instead.'); + return loadMtl(shallowPath) + .then(function(materialsInMtl) { + Object.assign(materials, materialsInMtl); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); + }); } return loadMtl(mtlPath) - .then(function(materialsInMtl) { - materials = Object.assign(materials, materialsInMtl); + .catch(function(error) { + // Try looking for the .mtl in the same directory as the obj + options.logger(error.message); + options.logger('Could not read material file at ' + mtlPath + '. Attempting to read the material file from within the obj directory instead.'); + return loadMtl(shallowPath); }) - .catch(function() { - logger('Could not read mtl file at ' + mtlPath + '. Using default material instead.'); + .then(function(materialsInMtl) { + Object.assign(materials, materialsInMtl); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read material file at ' + shallowPath + '. Using default material instead.'); }); }, {concurrency : 10}) - .thenReturn(materials); + .then(function() { + return materials; + }); +} + +function loadImagePath(imagePath, objPath, options) { + var objDirectory = path.dirname(objPath); + var shallowPath = path.join(objDirectory, path.basename(imagePath)); + if (options.secure && outsideDirectory(imagePath, objDirectory)) { + // Try looking for the image in the same directory as the obj + options.logger('Image file is outside of the obj directory and the secure flag is true. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored'); + }); + } + return loadImage(imagePath, options) + .catch(function(error) { + // Try looking for the image in the same directory as the obj + options.logger(error.message); + options.logger('Could not read image file at ' + imagePath + '. Attempting to read the image file from within the obj directory instead.'); + return loadImage(shallowPath, options); + }) + .catch(function(error) { + options.logger(error.message); + options.logger('Could not read image file at ' + shallowPath + '. This image will be ignored.'); + }); } function loadImages(imagePaths, objPath, options) { - var secure = options.secure; - var logger = options.logger; var images = {}; return Promise.map(imagePaths, function(imagePath) { - if (secure && outsideDirectory(imagePath, objPath)) { - logger('Could not read image file at ' + imagePath + ' because it is outside of the obj directory and the secure flag is true. Material will ignore this image.'); - return; - } - return loadImage(imagePath, options) + return loadImagePath(imagePath, objPath, options) .then(function(image) { images[imagePath] = image; - }) - .catch(function() { - logger('Could not read image file at ' + imagePath + '. Material will ignore this image.'); }); }, {concurrency : 10}) - .thenReturn(images); + .then(function() { + return images; + }); } function getImagePaths(materials) { @@ -494,6 +571,23 @@ function getImagePaths(materials) { return Object.keys(imagePaths); } +function assignDefaultMaterial(nodes, materials) { + var defaultMaterial = Object.keys(materials)[0]; + 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]; + primitive.material = defaultValue(primitive.material, defaultMaterial); + } + } + } +} + function removeEmptyMeshes(meshes) { return meshes.filter(function(mesh) { // Remove empty primitives diff --git a/specs/data/box-complex-material-alpha/alpha.png b/specs/data/box-complex-material-alpha/alpha.png new file mode 100644 index 0000000..d04eb82 Binary files /dev/null and b/specs/data/box-complex-material-alpha/alpha.png differ diff --git a/specs/data/box-complex-material-alpha/ambient.gif b/specs/data/box-complex-material-alpha/ambient.gif new file mode 100644 index 0000000..1276823 Binary files /dev/null and b/specs/data/box-complex-material-alpha/ambient.gif differ diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl new file mode 100644 index 0000000..5c598ff --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.200000 0.200000 0.200000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.100000 0.100000 0.100000 +Ni 1.000000 +d 0.900000 +Tr 0.100000 +map_Ka ambient.gif +map_Ke emission.jpg +map_Kd diffuse.png +map_Ks specular.jpeg +map_Ns shininess.png +map_Bump bump.png +map_d alpha.png +illum 2 diff --git a/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj new file mode 100644 index 0000000..15d7f4a --- /dev/null +++ b/specs/data/box-complex-material-alpha/box-complex-material-alpha.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-complex-material-alpha.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +usemtl Material +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-complex-material-alpha/bump.png b/specs/data/box-complex-material-alpha/bump.png new file mode 100644 index 0000000..16ec3a9 Binary files /dev/null and b/specs/data/box-complex-material-alpha/bump.png differ diff --git a/specs/data/box-complex-material-alpha/diffuse.png b/specs/data/box-complex-material-alpha/diffuse.png new file mode 100644 index 0000000..8704966 Binary files /dev/null and b/specs/data/box-complex-material-alpha/diffuse.png differ diff --git a/specs/data/box-complex-material-alpha/emission.jpg b/specs/data/box-complex-material-alpha/emission.jpg new file mode 100644 index 0000000..e5c85a9 Binary files /dev/null and b/specs/data/box-complex-material-alpha/emission.jpg differ diff --git a/specs/data/box-complex-material-alpha/shininess.png b/specs/data/box-complex-material-alpha/shininess.png new file mode 100644 index 0000000..bfcf1a2 Binary files /dev/null and b/specs/data/box-complex-material-alpha/shininess.png differ diff --git a/specs/data/box-complex-material-alpha/specular.jpeg b/specs/data/box-complex-material-alpha/specular.jpeg new file mode 100644 index 0000000..661bf98 Binary files /dev/null and b/specs/data/box-complex-material-alpha/specular.jpeg differ diff --git a/specs/data/box-complex-material/box-complex-material.mtl b/specs/data/box-complex-material/box-complex-material.mtl index 5c598ff..1ab3601 100644 --- a/specs/data/box-complex-material/box-complex-material.mtl +++ b/specs/data/box-complex-material/box-complex-material.mtl @@ -16,5 +16,4 @@ map_Kd diffuse.png map_Ks specular.jpeg map_Ns shininess.png map_Bump bump.png -map_d alpha.png illum 2 diff --git a/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl new file mode 100644 index 0000000..cb21081 --- /dev/null +++ b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl @@ -0,0 +1,14 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd cesium.png +map_Ka cesium.png diff --git a/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj new file mode 100644 index 0000000..5d52f28 --- /dev/null +++ b/specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib box-diffuse-ambient-same.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +usemtl Material +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-diffuse-ambient-same/cesium.png b/specs/data/box-diffuse-ambient-same/cesium.png new file mode 100644 index 0000000..3b8baee Binary files /dev/null and b/specs/data/box-diffuse-ambient-same/cesium.png differ diff --git a/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl b/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl new file mode 100644 index 0000000..2e2d3a5 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box-external-resources-in-root.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl MaterialTextured +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd ../box-textured/cesium.png diff --git a/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj b/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj new file mode 100644 index 0000000..6b17fc5 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box-external-resources-in-root.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend' +# www.blender.org +mtllib box-external-resources-in-root.mtl +mtllib ../box/box.mtl +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +usemtl MaterialTextured +f 3/1/1 7/2/1 5/3/1 1/4/1 +f 1/9/3 2/10/3 4/11/3 3/12/3 +f 3/1/5 4/6/5 8/17/5 7/18/5 +usemtl Material +f 8/5/2 4/6/2 2/7/2 6/8/2 +f 7/13/4 8/14/4 6/15/4 5/16/4 +f 5/19/6 6/20/6 2/7/6 1/4/6 diff --git a/specs/data/box-external-resources-in-root/box.mtl b/specs/data/box-external-resources-in-root/box.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-external-resources-in-root/box.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-external-resources-in-root/cesium.png b/specs/data/box-external-resources-in-root/cesium.png new file mode 100644 index 0000000..3b8baee Binary files /dev/null and b/specs/data/box-external-resources-in-root/cesium.png differ diff --git a/specs/data/box-missing-usemtl/box-missing-usemtl.mtl b/specs/data/box-missing-usemtl/box-missing-usemtl.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-missing-usemtl/box-missing-usemtl.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-missing-usemtl/box-missing-usemtl.obj b/specs/data/box-missing-usemtl/box-missing-usemtl.obj new file mode 100644 index 0000000..4fc5eee --- /dev/null +++ b/specs/data/box-missing-usemtl/box-missing-usemtl.obj @@ -0,0 +1,45 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-missing-usemtl.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl new file mode 100644 index 0000000..d81606d --- /dev/null +++ b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd cesium.png diff --git a/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj new file mode 100644 index 0000000..2762b61 --- /dev/null +++ b/specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj @@ -0,0 +1,67 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-mixed-attributes-2.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +# Using default material +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 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +usemtl Material +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 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +usemtl Missing +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 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 +o CubeCopy +usemtl Material +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 +f 7/9 8/10 6/11 5/12 +f 5/13 6/14 2/15 1/16 +f 3//5 7//5 5//5 1//5 +f 8//6 4//6 2//6 6//6 diff --git a/specs/data/box-mixed-attributes-2/cesium.png b/specs/data/box-mixed-attributes-2/cesium.png new file mode 100644 index 0000000..3b8baee Binary files /dev/null and b/specs/data/box-mixed-attributes-2/cesium.png differ diff --git a/specs/data/box-mtllib-spaces/box mtllib blue.mtl b/specs/data/box-mtllib-spaces/box mtllib blue.mtl new file mode 100644 index 0000000..d3fe863 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib blue.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Blue +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib green.mtl b/specs/data/box-mtllib-spaces/box mtllib green.mtl new file mode 100644 index 0000000..89abba9 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib green.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Green +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.640000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib red.mtl b/specs/data/box-mtllib-spaces/box mtllib red.mtl new file mode 100644 index 0000000..3721d86 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib red.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'box-multiple-materials.blend' +# Material Count: 1 + +newmtl Red +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-mtllib-spaces/box mtllib.obj b/specs/data/box-mtllib-spaces/box mtllib.obj new file mode 100644 index 0000000..fe03d89 --- /dev/null +++ b/specs/data/box-mtllib-spaces/box mtllib.obj @@ -0,0 +1,50 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-multiple-materials.blend' +# www.blender.org +mtllib box mtllib red.mtl +mtllib box mtllib green.mtl box mtllib blue.mtl +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn -1.0000 0.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +vn 0.0000 0.0000 1.0000 +usemtl Red +f 3/1/1 7/2/1 5/3/1 1/4/1 +usemtl Green +f 1/9/3 2/10/3 4/11/3 3/12/3 +usemtl Blue +f 3/1/5 4/6/5 8/17/5 7/18/5 +usemtl Red +f 8/5/2 4/6/2 2/7/2 6/8/2 +usemtl Green +f 7/13/4 8/14/4 6/15/4 5/16/4 +usemtl Blue +f 5/19/6 6/20/6 2/7/6 1/4/6 diff --git a/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl new file mode 100644 index 0000000..2f9b11a --- /dev/null +++ b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.mtl @@ -0,0 +1,32 @@ +# Blender MTL File: 'box-objects.blend' +# Material Count: 3 + +newmtl Blue +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.000000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Green +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.000000 0.640000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 + +newmtl Red +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj new file mode 100644 index 0000000..aed6a84 --- /dev/null +++ b/specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj @@ -0,0 +1,133 @@ +# Blender v2.78 (sub 0) OBJ File: 'box-objects.blend' +# www.blender.org +mtllib box-objects-groups-materials-2.mtl +usemtl Blue +o Cube +v -1.000000 -1.000000 -4.000000 +v -1.000000 1.000000 -4.000000 +v -1.000000 -1.000000 -6.000000 +v -1.000000 1.000000 -6.000000 +v 1.000000 -1.000000 -4.000000 +v 1.000000 1.000000 -4.000000 +v 1.000000 -1.000000 -6.000000 +v 1.000000 1.000000 -6.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +usemtl Green +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 +v 4.000000 -1.000000 1.000000 +v 4.000000 1.000000 1.000000 +v 4.000000 -1.000000 -1.000000 +v 4.000000 1.000000 -1.000000 +v 6.000000 -1.000000 1.000000 +v 6.000000 1.000000 1.000000 +v 6.000000 -1.000000 -1.000000 +v 6.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +usemtl Green +g 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 +f 15/29/9 16/30/9 14/31/9 13/32/9 +usemtl Red +f 13/33/10 14/34/10 10/35/10 9/36/10 +f 11/25/11 15/37/11 13/38/11 9/36/11 +f 16/39/12 12/26/12 10/35/12 14/40/12 +usemtl Red +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 Red +f 21/53/16 22/54/16 18/55/16 17/56/16 +f 19/45/17 23/57/17 21/58/17 17/56/17 +f 24/59/18 20/46/18 18/55/18 22/60/18 +usemtl Blue +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 +f 23/49/15 24/50/15 22/51/15 21/52/15 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-resources-in-root/box-resources-in-root.mtl b/specs/data/box-resources-in-root/box-resources-in-root.mtl new file mode 100644 index 0000000..7bc1846 --- /dev/null +++ b/specs/data/box-resources-in-root/box-resources-in-root.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.000000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd resources/textures/cesium.png diff --git a/specs/data/box-resources-in-root/box-resources-in-root.obj b/specs/data/box-resources-in-root/box-resources-in-root.obj new file mode 100644 index 0000000..1e9d945 --- /dev/null +++ b/specs/data/box-resources-in-root/box-resources-in-root.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib resources/box-resources-in-root.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +usemtl Material +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-resources-in-root/cesium.png b/specs/data/box-resources-in-root/cesium.png new file mode 100644 index 0000000..3b8baee Binary files /dev/null and b/specs/data/box-resources-in-root/cesium.png differ diff --git a/specs/data/box-texture-options/ambient.gif b/specs/data/box-texture-options/ambient.gif new file mode 100644 index 0000000..1276823 Binary files /dev/null and b/specs/data/box-texture-options/ambient.gif differ diff --git a/specs/data/box-texture-options/box-texture-options.mtl b/specs/data/box-texture-options/box-texture-options.mtl new file mode 100644 index 0000000..3434533 --- /dev/null +++ b/specs/data/box-texture-options/box-texture-options.mtl @@ -0,0 +1,19 @@ +# Blender MTL File: 'box.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.200000 0.200000 0.200000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.100000 0.100000 0.100000 +Ni 1.000000 +d 0.900000 +Tr 0.100000 +map_Ka -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 ambient.gif +map_Ke -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 emission.jpg +map_Kd -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 diffuse.png +map_Ks -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 specular.jpeg +map_Ns -s 1.0 1.0 1.0 -o 0.0 0.0 0.0 shininess.png +map_Bump -bm 0.2 bump.png +illum 2 diff --git a/specs/data/box-texture-options/box-texture-options.obj b/specs/data/box-texture-options/box-texture-options.obj new file mode 100644 index 0000000..3207f00 --- /dev/null +++ b/specs/data/box-texture-options/box-texture-options.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: 'box.blend' +# www.blender.org +mtllib box-texture-options.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn -1.0000 0.0000 0.0000 +vn 0.0000 0.0000 -1.0000 +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 +usemtl Material +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box-texture-options/bump.png b/specs/data/box-texture-options/bump.png new file mode 100644 index 0000000..16ec3a9 Binary files /dev/null and b/specs/data/box-texture-options/bump.png differ diff --git a/specs/data/box-texture-options/diffuse.png b/specs/data/box-texture-options/diffuse.png new file mode 100644 index 0000000..8704966 Binary files /dev/null and b/specs/data/box-texture-options/diffuse.png differ diff --git a/specs/data/box-texture-options/emission.jpg b/specs/data/box-texture-options/emission.jpg new file mode 100644 index 0000000..e5c85a9 Binary files /dev/null and b/specs/data/box-texture-options/emission.jpg differ diff --git a/specs/data/box-texture-options/shininess.png b/specs/data/box-texture-options/shininess.png new file mode 100644 index 0000000..bfcf1a2 Binary files /dev/null and b/specs/data/box-texture-options/shininess.png differ diff --git a/specs/data/box-texture-options/specular.jpeg b/specs/data/box-texture-options/specular.jpeg new file mode 100644 index 0000000..661bf98 Binary files /dev/null and b/specs/data/box-texture-options/specular.jpeg differ diff --git a/specs/data/box-unnormalized/box-unnormalized.mtl b/specs/data/box-unnormalized/box-unnormalized.mtl new file mode 100644 index 0000000..4f8d129 --- /dev/null +++ b/specs/data/box-unnormalized/box-unnormalized.mtl @@ -0,0 +1,12 @@ +# Blender MTL File: 'None' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 0.100000 0.000000 0.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.100000 +Ni 1.000000 +d 1.000000 +illum 2 diff --git a/specs/data/box-unnormalized/box-unnormalized.obj b/specs/data/box-unnormalized/box-unnormalized.obj new file mode 100644 index 0000000..f52251e --- /dev/null +++ b/specs/data/box-unnormalized/box-unnormalized.obj @@ -0,0 +1,46 @@ +# Blender v2.78 (sub 0) OBJ File: '' +# www.blender.org +mtllib box-unnormalized.mtl +o Cube +v -1.000000 -1.000000 1.000000 +v -1.000000 1.000000 1.000000 +v -1.000000 -1.000000 -1.000000 +v -1.000000 1.000000 -1.000000 +v 1.000000 -1.000000 1.000000 +v 1.000000 1.000000 1.000000 +v 1.000000 -1.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 0.0000 0.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 1.0000 +vt 1.0000 0.0000 +vt 1.0000 1.0000 +vt 0.0000 0.0000 +vt 0.0000 1.0000 +vn 0.0000 0.0000 0.0000 +vn 0.0000 0.0000 0.5 +vn 1.0000 0.1000 0.0000 +vn 0.0000 0.0000 -0.9 +vn 0.0000 1.0000 0.0000 +vn 10.0000 -10.0000 5.0000 +usemtl Material +s off +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 +f 7/9/3 8/10/3 6/11/3 5/12/3 +f 5/13/4 6/14/4 2/15/4 1/16/4 +f 3/5/5 7/17/5 5/18/5 1/16/5 +f 8/19/6 4/6/6 2/15/6 6/20/6 diff --git a/specs/data/box/box.mtl b/specs/data/box/box.mtl index a304b43..4f8d129 100644 --- a/specs/data/box/box.mtl +++ b/specs/data/box/box.mtl @@ -3,10 +3,10 @@ newmtl Material Ns 96.078431 -Ka 0.000000 0.000000 0.000000 +Ka 0.100000 0.000000 0.000000 Kd 0.640000 0.640000 0.640000 Ks 0.500000 0.500000 0.500000 -Ke 0.000000 0.000000 0.000000 +Ke 0.000000 0.000000 0.100000 Ni 1.000000 d 1.000000 illum 2 diff --git a/specs/lib/createGltfSpec.js b/specs/lib/createGltfSpec.js index 87ea212..329d12f 100644 --- a/specs/lib/createGltfSpec.js +++ b/specs/lib/createGltfSpec.js @@ -8,10 +8,12 @@ var loadObj = require('../../lib/loadObj'); var Material = require('../../lib/Material'); var clone = Cesium.clone; +var defined = Cesium.defined; var WebGLConstants = Cesium.WebGLConstants; var boxObjUrl = 'specs/data/box/box.obj'; var groupObjUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; +var mixedAttributesObjUrl = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj'; var diffuseTextureUrl = 'specs/data/box-textured/cesium.png'; var transparentDiffuseTextureUrl = 'specs/data/box-complex-material/diffuse.png'; @@ -23,6 +25,7 @@ describe('createGltf', function() { var boxObjData; var duplicateBoxObjData; var groupObjData; + var mixedAttributesObjData; var diffuseTexture; var transparentDiffuseTexture; @@ -40,6 +43,10 @@ describe('createGltf', function() { .then(function(data) { groupObjData = data; }), + loadObj(mixedAttributesObjUrl, defaultOptions) + .then(function(data) { + mixedAttributesObjData = data; + }), loadImage(diffuseTextureUrl, defaultOptions) .then(function(image) { diffuseTexture = image; @@ -258,8 +265,8 @@ describe('createGltf', function() { boxObjData.nodes[1].meshes[0].primitives[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; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('PHONG'); expect(kmc2.technique).toBe('CONSTANT'); @@ -271,8 +278,8 @@ describe('createGltf', function() { boxObjData.nodes[0].meshes[0].primitives[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; + var kmc1 = gltf.materials['Material'].extensions.KHR_materials_common; + var kmc2 = gltf.materials['Material-2'].extensions.KHR_materials_common; expect(kmc1.technique).toBe('CONSTANT'); expect(kmc2.technique).toBe('PHONG'); @@ -309,6 +316,57 @@ describe('createGltf', function() { expect(attributes.TEXCOORD_0).toBeUndefined(); }); + function getMaterialValue(material, property) { + return material.extensions.KHR_materials_common.values[property]; + } + + it('splits incompatible materials', function() { + var gltf = createGltf(mixedAttributesObjData, defaultOptions); + var meshes = gltf.meshes; + var materials = gltf.materials; + var materialNames = Object.keys(materials).sort(); + + // Expect three copies of each material for + // * positions/normals/uvs + // * positions/normals + // * positions/uvs + expect(materialNames).toEqual([ + 'Material', + 'Material-2', + 'Material-3', + 'Missing', + 'Missing-2', + 'Missing-3', + 'default', + 'default-2', + 'default-3' + ]); + + expect(getMaterialValue(materials['Material'], 'diffuse')).toBe('texture_cesium'); + expect(getMaterialValue(materials['Material'], 'emission')).toEqual([0.0, 0.0, 0.1, 1.0]); + expect(getMaterialValue(materials['Material-2'], 'diffuse')).toEqual([0.0, 0.0, 0.0, 1.0]); + expect(getMaterialValue(materials['Material-2'], 'emission')).toBe('texture_cesium'); + expect(getMaterialValue(materials['Material-3'], 'diffuse')).toEqual([0.64, 0.64, 0.64, 1.0]); + expect(getMaterialValue(materials['Material-3'], 'emission')).toEqual([0.0, 0.0, 0.1, 1.0]); + + // Test that primitives without uvs reference materials without textures + for (var meshName in meshes) { + if (meshes.hasOwnProperty(meshName)) { + var mesh = meshes[meshName]; + var primitives = mesh.primitives; + var primitivesLength = primitives.length; + for (var i = 0; i < primitivesLength; ++i) { + var primitive = primitives[i]; + var material = materials[primitive.material]; + if (!defined(primitive.attributes.TEXCOORD_0)) { + expect(typeof getMaterialValue(material, 'diffuse') === 'string').toBe(false); + expect(typeof getMaterialValue(material, 'emission') === 'string').toBe(false); + } + } + } + } + }); + function expandObjData(objData, duplicatesLength) { var primitive = objData.nodes[0].meshes[0].primitives[0]; var indices = primitive.indices; diff --git a/specs/lib/loadMtlSpec.js b/specs/lib/loadMtlSpec.js index 53fc226..ae579d1 100644 --- a/specs/lib/loadMtlSpec.js +++ b/specs/lib/loadMtlSpec.js @@ -2,8 +2,10 @@ var path = require('path'); var loadMtl = require('../../lib/loadMtl'); -var complexMaterialUrl = 'specs/data/box-complex-material/box-complex-material.mtl'; +var complexMaterialAlphaUrl = 'specs/data/box-complex-material-alpha/box-complex-material-alpha.mtl'; +var diffuseAmbientSameMaterialUrl = 'specs/data/box-diffuse-ambient-same/box-diffuse-ambient-same.mtl'; var multipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.mtl'; +var texturedWithOptionsMaterialUrl = 'specs/data/box-texture-options/box-texture-options.mtl'; var transparentMaterialUrl = 'specs/data/box-transparent/box-transparent.mtl'; function getImagePath(objPath, relativePath) { @@ -12,7 +14,7 @@ function getImagePath(objPath, relativePath) { describe('loadMtl', function() { it('loads complex material', function(done) { - expect(loadMtl(complexMaterialUrl) + expect(loadMtl(complexMaterialAlphaUrl) .then(function(materials) { var material = materials.Material; expect(material).toBeDefined(); @@ -22,13 +24,13 @@ describe('loadMtl', function() { expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); expect(material.specularShininess).toEqual(96.078431); expect(material.alpha).toEqual(0.9); - expect(material.ambientTexture).toEqual(getImagePath(complexMaterialUrl, 'ambient.gif')); - expect(material.emissionTexture).toEqual(getImagePath(complexMaterialUrl, 'emission.jpg')); - expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialUrl, 'diffuse.png')); - expect(material.specularTexture).toEqual(getImagePath(complexMaterialUrl, 'specular.jpeg')); - expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialUrl, 'shininess.png')); - expect(material.normalMap).toEqual(getImagePath(complexMaterialUrl, 'bump.png')); - expect(material.alphaMap).toEqual(getImagePath(complexMaterialUrl, 'alpha.png')); + expect(material.ambientTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(complexMaterialAlphaUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'bump.png')); + expect(material.alphaMap).toEqual(getImagePath(complexMaterialAlphaUrl, 'alpha.png')); }), done).toResolve(); }); @@ -42,6 +44,36 @@ describe('loadMtl', function() { }), done).toResolve(); }); + it('loads mtl with textures having options', function(done) { + expect(loadMtl(texturedWithOptionsMaterialUrl) + .then(function(materials) { + var material = materials.Material; + expect(material).toBeDefined(); + expect(material.ambientColor).toEqual([0.2, 0.2, 0.2, 1.0]); + expect(material.emissionColor).toEqual([0.1, 0.1, 0.1, 1.0]); + expect(material.diffuseColor).toEqual([0.64, 0.64, 0.64, 1.0]); + expect(material.specularColor).toEqual([0.5, 0.5, 0.5, 1.0]); + expect(material.specularShininess).toEqual(96.078431); + expect(material.alpha).toEqual(0.9); + expect(material.ambientTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'ambient.gif')); + expect(material.emissionTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'emission.jpg')); + expect(material.diffuseTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'diffuse.png')); + expect(material.specularTexture).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'specular.jpeg')); + expect(material.specularShininessMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'shininess.png')); + expect(material.normalMap).toEqual(getImagePath(texturedWithOptionsMaterialUrl, 'bump.png')); + }), done).toResolve(); + }); + + it('ambient texture is ignored if it is the same as the diffuse texture', function(done) { + expect(loadMtl(diffuseAmbientSameMaterialUrl) + .then(function(materials) { + expect(Object.keys(materials).length).toBe(1); + var material = materials['Material']; + expect(material.diffuseTexture).toBeDefined(); + expect(material.ambientTexture).toBeUndefined(); + }), done).toResolve(); + }); + it('alpha of 0.0 is treated as 1.0', function(done) { expect(loadMtl(transparentMaterialUrl) .then(function(materials) { diff --git a/specs/lib/loadObjSpec.js b/specs/lib/loadObjSpec.js index f635878..7d3bc0d 100644 --- a/specs/lib/loadObjSpec.js +++ b/specs/lib/loadObjSpec.js @@ -7,6 +7,7 @@ var obj2gltf = require('../../lib/obj2gltf'); var Cartesian3 = Cesium.Cartesian3; var clone = Cesium.clone; +var CesiumMath = Cesium.Math; var RuntimeError = Cesium.RuntimeError; var objUrl = 'specs/data/box/box.obj'; @@ -19,14 +20,21 @@ var objTrianglesUrl = 'specs/data/box-triangles/box-triangles.obj'; var objObjectsUrl = 'specs/data/box-objects/box-objects.obj'; var objGroupsUrl = 'specs/data/box-groups/box-groups.obj'; var objObjectsGroupsUrl = 'specs/data/box-objects-groups/box-objects-groups.obj'; +var objObjectsGroupsMaterialsUrl = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj'; +var objObjectsGroupsMaterials2Url = 'specs/data/box-objects-groups-materials-2/box-objects-groups-materials-2.obj'; var objConcaveUrl = 'specs/data/concave/concave.obj'; +var objUnnormalizedUrl = 'specs/data/box-unnormalized/box-unnormalized.obj'; var objUsemtlUrl = 'specs/data/box-usemtl/box-usemtl.obj'; var objNoMaterialsUrl = 'specs/data/box-no-materials/box-no-materials.obj'; var objMultipleMaterialsUrl = 'specs/data/box-multiple-materials/box-multiple-materials.obj'; var objUncleanedUrl = 'specs/data/box-uncleaned/box-uncleaned.obj'; var objMtllibUrl = 'specs/data/box-mtllib/box-mtllib.obj'; +var objMtllibSpacesUrl = 'specs/data/box-mtllib-spaces/box mtllib.obj'; var objMissingMtllibUrl = 'specs/data/box-missing-mtllib/box-missing-mtllib.obj'; +var objMissingUsemtlUrl = 'specs/data/box-missing-usemtl/box-missing-usemtl.obj'; var objExternalResourcesUrl = 'specs/data/box-external-resources/box-external-resources.obj'; +var objResourcesInRootUrl = 'specs/data/box-resources-in-root/box-resources-in-root.obj'; +var objExternalResourcesInRootUrl = 'specs/data/box-external-resources-in-root/box-external-resources-in-root.obj'; var objTexturedUrl = 'specs/data/box-textured/box-textured.obj'; var objMissingTextureUrl = 'specs/data/box-missing-texture/box-missing-texture.obj'; var objSubdirectoriesUrl = 'specs/data/box-subdirectories/box-textured.obj'; @@ -110,6 +118,23 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('normalizes normals', function(done) { + expect(loadObj(objUnnormalizedUrl, defaultOptions) + .then(function(data) { + var scratchNormal = new Cesium.Cartesian3(); + var primitive = getPrimitives(data)[0]; + var normals = primitive.normals; + var normalsLength = normals.length / 3; + for (var i = 0; i < normalsLength; ++i) { + var normalX = normals.get(i * 3); + var normalY = normals.get(i * 3 + 1); + var normalZ = normals.get(i * 3 + 2); + var normal = Cartesian3.fromElements(normalX, normalY, normalZ, scratchNormal); + expect(Cartesian3.magnitude(normal)).toEqualEpsilon(1.0, CesiumMath.EPSILON5); + } + }), done).toResolve(); + }); + it('loads obj with uvs', function(done) { expect(loadObj(objUvsUrl, defaultOptions) .then(function(data) { @@ -198,6 +223,41 @@ describe('loadObj', function() { }), done).toResolve(); }); + function loadsObjWithObjectsGroupsAndMaterials(data) { + var nodes = data.nodes; + expect(nodes.length).toBe(1); + expect(nodes[0].name).toBe('Cube'); + var meshes = getMeshes(data); + expect(meshes.length).toBe(3); + expect(meshes[0].name).toBe('Blue'); + expect(meshes[1].name).toBe('Green'); + expect(meshes[2].name).toBe('Red'); + var primitives = getPrimitives(data); + expect(primitives.length).toBe(6); + expect(primitives[0].material).toBe('Blue'); + expect(primitives[1].material).toBe('Green'); + expect(primitives[2].material).toBe('Green'); + expect(primitives[3].material).toBe('Red'); + expect(primitives[4].material).toBe('Red'); + expect(primitives[5].material).toBe('Blue'); + } + + it('loads obj with objects, groups, and materials', function(done) { + expect(loadObj(objObjectsGroupsMaterialsUrl, defaultOptions) + .then(function(data) { + loadsObjWithObjectsGroupsAndMaterials(data); + }), done).toResolve(); + }); + + it('loads obj with objects, groups, and materials (2)', function(done) { + // The usemtl lines are placed in an unordered fashion but + // should produce the same result as the previous test + expect(loadObj(objObjectsGroupsMaterials2Url, defaultOptions) + .then(function(data) { + loadsObjWithObjectsGroupsAndMaterials(data); + }), done).toResolve(); + }); + it('loads obj with concave face containing 5 vertices', function(done) { expect(loadObj(objConcaveUrl, defaultOptions) .then(function(data) { @@ -293,11 +353,37 @@ describe('loadObj', function() { }), done).toResolve(); }); + it('loads obj with mtllib paths with spaces', function(done) { + expect(loadObj(objMtllibSpacesUrl, defaultOptions) + .then(function(data) { + var materials = data.materials; + expect(Object.keys(materials).length).toBe(3); + expect(materials['Blue'].diffuseColor).toEqual([0.0, 0.0, 0.64, 1.0]); + expect(materials['Green'].diffuseColor).toEqual([0.0, 0.64, 0.0, 1.0]); + expect(materials['Red'].diffuseColor).toEqual([0.64, 0.0, 0.0, 1.0]); + }), done).toResolve(); + }); + it('loads obj with missing mtllib', function(done) { - expect(loadObj(objMissingMtllibUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingMtllibUrl, options) .then(function(data) { expect(data.materials).toEqual({}); - expect(console.log.calls.argsFor(0)[0].indexOf('Could not read mtl file') >= 0).toBe(true); + expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('Attempting to read the material file from within the obj directory instead.') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(3)[0].indexOf('Could not read material file') >= 0).toBe(true); + }), done).toResolve(); + }); + + it('loads obj with missing usemtl', function(done) { + expect(loadObj(objMissingUsemtlUrl, defaultOptions) + .then(function(data) { + expect(Object.keys(data.materials).length).toBe(1); + expect(data.nodes[0].meshes[0].primitives[0].material).toBe('Material'); }), done).toResolve(); }); @@ -312,6 +398,8 @@ describe('loadObj', function() { it('does not load resources outside of the obj directory when secure is true', function(done) { var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; options.secure = true; expect(loadObj(objExternalResourcesUrl, options) @@ -320,8 +408,30 @@ describe('loadObj', function() { expect(data.images[imagePath]).toBeUndefined(); expect(data.materials.MaterialTextured).toBeDefined(); expect(data.materials.Material).toBeUndefined(); // Not in directory, so not included - 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); + expect(spy.calls.argsFor(0)[0].indexOf('The material file is outside of the obj directory and the secure flag is true. Attempting to read the material file from within the obj directory instead.') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('Could not read material file') >= 0).toBe(true); + }), done).toResolve(); + }); + + it('loads resources from root directory when the .mtl path does not exist', function(done) { + expect(loadObj(objResourcesInRootUrl, defaultOptions) + .then(function(data) { + var material = data.materials['Material']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); + }), done).toResolve(); + }); + + it('loads resources from root directory when the .mtl path is outside of the obj directory and secure is true', function(done) { + var options = clone(defaultOptions); + options.secure = true; + expect(loadObj(objExternalResourcesInRootUrl, options) + .then(function(data) { + expect(Object.keys(data.materials).length).toBe(2); + var material = data.materials['MaterialTextured']; + var image = data.images[material.diffuseTexture]; + expect(image.source).toBeDefined(); }), done).toResolve(); }); @@ -335,12 +445,19 @@ describe('loadObj', function() { }); it('loads obj with missing texture', function(done) { - expect(loadObj(objMissingTextureUrl, defaultOptions) + var options = clone(defaultOptions); + var spy = jasmine.createSpy('logger'); + options.logger = spy; + + expect(loadObj(objMissingTextureUrl, options) .then(function(data) { var imagePath = getImagePath(objMissingTextureUrl, 'cesium.png'); expect(data.images[imagePath]).toBeUndefined(); expect(data.materials.Material.diffuseTexture).toEqual(imagePath); - expect(console.log.calls.argsFor(0)[0].indexOf('Could not read image file') >= 0).toBe(true); + expect(spy.calls.argsFor(0)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(1)[0].indexOf('Attempting to read the image file from within the obj directory instead.') >= 0).toBe(true); + expect(spy.calls.argsFor(2)[0].indexOf('ENOENT') >= 0).toBe(true); + expect(spy.calls.argsFor(3)[0].indexOf('Could not read image file') >= 0).toBe(true); }), done).toResolve(); }); @@ -417,11 +534,15 @@ describe('loadObj', function() { }), done).toResolve(); }); - it('discards faces that don\'t use the same attributes as other faces in the primitive', function(done) { + it('separates faces that don\'t use the same attributes as other faces in the primitive', function(done) { expect(loadObj(objMixedAttributesUrl, defaultOptions) .then(function(data) { - var primitive = getPrimitives(data)[0]; - expect(primitive.indices.length).toBe(18); // 3 faces removed + var primitives = getPrimitives(data); + expect(primitives.length).toBe(4); + expect(primitives[0].indices.length).toBe(18); // 6 faces + expect(primitives[1].indices.length).toBe(6); // 2 faces + expect(primitives[2].indices.length).toBe(6); // 2 faces + expect(primitives[3].indices.length).toBe(6); // 2 faces }), done).toResolve(); });