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 ????-??-?? ### 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) * Improved parsing of faces with mismatching attributes. [#161](https://github.com/AnalyticalGraphicsInc/obj2gltf/pull/161)
### 2.3.1 2018-10-16 ### 2.3.1 2018-10-16

View File

@ -4,6 +4,7 @@ var getBufferPadded = require('./getBufferPadded');
var getDefaultMaterial = require('./loadMtl').getDefaultMaterial; var getDefaultMaterial = require('./loadMtl').getDefaultMaterial;
var Texture = require('./Texture'); var Texture = require('./Texture');
var defaultValue = Cesium.defaultValue;
var defined = Cesium.defined; var defined = Cesium.defined;
var WebGLConstants = Cesium.WebGLConstants; var WebGLConstants = Cesium.WebGLConstants;
@ -23,6 +24,9 @@ function createGltf(objData, options) {
var materials = objData.materials; var materials = objData.materials;
var name = objData.name; var name = objData.name;
// Split materials used by primitives with different types of attributes
materials = splitIncompatibleMaterials(nodes, materials, options);
var gltf = { var gltf = {
accessors : [], accessors : [],
asset : {}, asset : {},
@ -70,14 +74,14 @@ function createGltf(objData, options) {
var meshIndex; var meshIndex;
if (meshesLength === 1) { 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); addNode(gltf, node.name, meshIndex, undefined);
} else { } else {
// Add meshes as child nodes // Add meshes as child nodes
var parentIndex = addNode(gltf, node.name); var parentIndex = addNode(gltf, node.name);
for (var j = 0; j < meshesLength; ++j) { for (var j = 0; j < meshesLength; ++j) {
var mesh = meshes[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); 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) { function resolveTextures(gltf, material) {
for (var name in material) { for (var name in material) {
if (material.hasOwnProperty(name)) { if (material.hasOwnProperty(name)) {
@ -207,50 +236,101 @@ function resolveTextures(gltf, material) {
} }
} }
function addMaterial(gltf, material) { function addGltfMaterial(gltf, material) {
resolveTextures(gltf, material); resolveTextures(gltf, material);
var materialIndex = gltf.materials.length; var materialIndex = gltf.materials.length;
gltf.materials.push(material); gltf.materials.push(material);
return materialIndex; return materialIndex;
} }
function getMaterial(gltf, materials, materialName, options) { function getMaterialByName(materials, materialName) {
if (!defined(materialName)) {
// Create a default material if the primitive does not specify one
materialName = 'default';
}
var i;
var material;
var materialsLength = materials.length; var materialsLength = materials.length;
for (i = 0; i < materialsLength; ++i) { for (var i = 0; i < materialsLength; ++i) {
if (materials[i].name === materialName) { if (materials[i].name === materialName) {
material = materials[i]; return materials[i];
break;
} }
} }
}
if (!defined(material)) { function getMaterialIndex(materials, materialName) {
material = getDefaultMaterial(options); var materialsLength = materials.length;
material.name = materialName; for (var i = 0; i < materialsLength; ++i) {
if (materials[i].name === materialName) {
return i;
} }
}
}
var materialIndex; function getOrCreateGltfMaterial(gltf, materials, materialName) {
materialsLength = gltf.materials.length; var material = getMaterialByName(materials, materialName);
for (i = 0; i < materialsLength; ++i) { var materialIndex = getMaterialIndex(gltf.materials, materialName);
if (gltf.materials[i].name === materialName) {
materialIndex = i;
break;
}
}
if (!defined(materialIndex)) { if (!defined(materialIndex)) {
materialIndex = addMaterial(gltf, material); materialIndex = addGltfMaterial(gltf, material);
} }
return materialIndex; 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) { function addVertexAttribute(gltf, array, components, name) {
var count = array.length / components; var count = array.length / components;
var minMax = array.getMinMax(components); var minMax = array.getMinMax(components);
@ -309,7 +389,7 @@ function requiresUint32Indices(nodes) {
return false; 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 hasPositions = primitive.positions.length > 0;
var hasNormals = primitive.normals.length > 0; var hasNormals = primitive.normals.length > 0;
var hasUVs = primitive.uvs.length > 0; var hasUVs = primitive.uvs.length > 0;
@ -346,7 +426,7 @@ function addPrimitive(gltf, materials, bufferState, uint32Indices, mesh, primiti
primitive.uvs = undefined; primitive.uvs = undefined;
primitive.indices = undefined; primitive.indices = undefined;
var materialIndex = getMaterial(gltf, materials, primitive.material, options); var materialIndex = getOrCreateGltfMaterial(gltf, materials, primitive.material);
return { return {
attributes : attributes, 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 gltfPrimitives = [];
var primitives = mesh.primitives; var primitives = mesh.primitives;
var primitivesLength = primitives.length; var primitivesLength = primitives.length;
for (var i = 0; i < primitivesLength; ++i) { 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 = { 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 obj2gltf = require('../../lib/obj2gltf');
var createGltf = require('../../lib/createGltf'); var createGltf = require('../../lib/createGltf');
var loadObj = require('../../lib/loadObj'); var loadObj = require('../../lib/loadObj');
var getDefaultMaterial = require('../../lib/loadMtl').getDefaultMaterial;
var clone = Cesium.clone; var clone = Cesium.clone;
var defined = Cesium.defined;
var WebGLConstants = Cesium.WebGLConstants; var WebGLConstants = Cesium.WebGLConstants;
var boxObjPath = 'specs/data/box/box.obj'; var boxObjPath = 'specs/data/box/box.obj';
var groupObjPath = 'specs/data/box-objects-groups-materials/box-objects-groups-materials.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 complexObjPath = 'specs/data/box-complex-material/box-complex-material.obj';
var noMaterialsObjPath = 'specs/data/box-no-materials/box-no-materials.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; var options;
@ -20,6 +23,7 @@ describe('createGltf', function() {
var groupObjData; var groupObjData;
var complexObjData; var complexObjData;
var noMaterialsObjData; var noMaterialsObjData;
var mixedAttributesObjData;
beforeEach(function(done) { beforeEach(function(done) {
options = clone(obj2gltf.defaults); options = clone(obj2gltf.defaults);
@ -42,6 +46,10 @@ describe('createGltf', function() {
loadObj(noMaterialsObjPath, options) loadObj(noMaterialsObjPath, options)
.then(function(data) { .then(function(data) {
noMaterialsObjData = data; noMaterialsObjData = data;
}),
loadObj(mixedAttributesObjPath, options)
.then(function(data) {
mixedAttributesObjData = data;
}) })
]).then(done); ]).then(done);
}); });
@ -163,6 +171,72 @@ describe('createGltf', function() {
expect(attributes.TEXCOORD_0).toBeUndefined(); 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) { function expandObjData(objData, duplicatesLength) {
var primitive = objData.nodes[0].meshes[0].primitives[0]; var primitive = objData.nodes[0].meshes[0].primitives[0];
var indices = primitive.indices; var indices = primitive.indices;