Merge pull request #163 from AnalyticalGraphicsInc/fixes-from-master

Backport changes from master to the 1.0 branch
This commit is contained in:
likangning93 2018-11-01 09:42:08 -04:00 committed by GitHub
commit c2cb2e9624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 1256 additions and 99 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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;

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -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

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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();
});