Merge pull request #162 from AnalyticalGraphicsInc/material-duplication

Duplicate materials with mismatching attributes
This commit is contained in:
likangning93 2018-11-02 09:55:59 -04:00 committed by GitHub
commit ac03e9f8ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 266 additions and 31 deletions

View File

@ -2,7 +2,8 @@ Change Log
==========
### 2.3.2 ????-??-??
* Improved handling of primitives with different attributes using the same material. Materials are now duplicated. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162)
* Fixed a bug where primitives without texture coordinates could use materials containing textures. Those textures are now removed. [#162](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/162)
* Improved parsing of faces with mismatching attributes. [#161](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/161)
### 2.3.1 2018-10-16

View File

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

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

@ -4,14 +4,17 @@ var Promise = require('bluebird');
var obj2gltf = require('../../lib/obj2gltf');
var createGltf = require('../../lib/createGltf');
var loadObj = require('../../lib/loadObj');
var getDefaultMaterial = require('../../lib/loadMtl').getDefaultMaterial;
var clone = Cesium.clone;
var defined = Cesium.defined;
var WebGLConstants = Cesium.WebGLConstants;
var boxObjPath = 'specs/data/box/box.obj';
var groupObjPath = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.obj';
var complexObjPath = 'specs/data/box-complex-material/box-complex-material.obj';
var noMaterialsObjPath = 'specs/data/box-no-materials/box-no-materials.obj';
var mixedAttributesObjPath = 'specs/data/box-mixed-attributes-2/box-mixed-attributes-2.obj';
var options;
@ -20,6 +23,7 @@ describe('createGltf', function() {
var groupObjData;
var complexObjData;
var noMaterialsObjData;
var mixedAttributesObjData;
beforeEach(function(done) {
options = clone(obj2gltf.defaults);
@ -42,6 +46,10 @@ describe('createGltf', function() {
loadObj(noMaterialsObjPath, options)
.then(function(data) {
noMaterialsObjData = data;
}),
loadObj(mixedAttributesObjPath, options)
.then(function(data) {
mixedAttributesObjData = data;
})
]).then(done);
});
@ -163,6 +171,72 @@ describe('createGltf', function() {
expect(attributes.TEXCOORD_0).toBeUndefined();
});
it('splits incompatible materials', function() {
var gltf = createGltf(mixedAttributesObjData, options);
var materials = gltf.materials;
var meshes = gltf.meshes;
var referenceMaterial = mixedAttributesObjData.materials[0];
delete referenceMaterial.name;
referenceMaterial.pbrMetallicRoughness.baseColorTexture = {
index : 0
};
var referenceMaterialNoTextures = clone(referenceMaterial, true);
referenceMaterialNoTextures.pbrMetallicRoughness.baseColorTexture = undefined;
var defaultMaterial = getDefaultMaterial(options);
delete defaultMaterial.name;
var materialNames = materials.map(function(material) {
var name = material.name;
delete material.name;
return name;
});
// Expect three copies of each material for
// * positions/normals/uvs
// * positions/normals
// * positions/uvs
expect(materialNames).toEqual([
'default',
'default-2',
'default-3',
'Material',
'Material-2',
'Material-3',
'Missing',
'Missing-2',
'Missing-3'
]);
expect(materials.length).toBe(9);
expect(materials[0]).toEqual(defaultMaterial);
expect(materials[1]).toEqual(defaultMaterial);
expect(materials[2]).toEqual(defaultMaterial);
expect(materials[3]).toEqual(referenceMaterial);
expect(materials[4]).toEqual(referenceMaterial);
expect(materials[5]).toEqual(referenceMaterialNoTextures);
expect(materials[6]).toEqual(defaultMaterial);
expect(materials[7]).toEqual(defaultMaterial);
expect(materials[8]).toEqual(defaultMaterial);
// Test that primitives without uvs reference materials without textures
var meshesLength = meshes.length;
for (var i = 0; i < meshesLength; ++i) {
var mesh = meshes[i];
var primitives = mesh.primitives;
var primitivesLength = primitives.length;
for (var j = 0; j < primitivesLength; ++j) {
var primitive = primitives[j];
var material = materials[primitive.material];
if (!defined(primitive.attributes.TEXCOORD_0)) {
expect(material.pbrMetallicRoughness.baseColorTexture).toBeUndefined();
}
}
}
});
function expandObjData(objData, duplicatesLength) {
var primitive = objData.nodes[0].meshes[0].primitives[0];
var indices = primitive.indices;