提交 61260bc6 编写于 作者: J Jonne Nauha 提交者: Mr.doob

OBJLoader: MultiMaterial and geometry group support (#8691)

* ObjLoader: Implement support for multiple materials inside a geometry/object.
- Tracks material declaration occuring mid face declaration
- If object has multiple materials MultiMaterial is creted and geometry groups are created.
- This approach is better than splitting into separate objects: 1) we are creating as many objects the file defines (correctness) 2) The full geometry would be duplicated into
  two objects, afaik this uploads the geometry multiple times to the GPU (perf, i had test asset that had >400 submeshes with 1-6 geometry groups each).

Fixes #8681 #8640
Updates #8203 (fixes OPs 2. point)

* OBJLoader: Code cleanup and three.js style formatting.
上级 30319442
......@@ -89,24 +89,91 @@ THREE.OBJLoader.prototype = {
}
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize();
}
this.object = {
name : name || '',
fromDeclaration : ( fromDeclaration !== false ),
geometry : {
vertices : [],
normals : [],
uvs : []
},
material : {
name : '',
smooth : true
materials : [],
smooth : true,
startMaterial : function( name, libraries ) {
var previous = this._finalize( false );
var material = {
index : this.materials.length,
name : name || '',
mtllib : ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
smooth : ( previous !== undefined ? previous.smooth : this.smooth ),
groupStart : ( previous !== undefined ? previous.groupEnd : 0 ),
groupEnd : -1,
groupCount : -1
};
this.materials.push( material );
return material;
},
fromDeclaration : ( fromDeclaration !== false )
currentMaterial : function() {
if ( this.materials.length > 0 ) {
return this.materials[ this.materials.length - 1 ];
}
return undefined;
},
_finalize : function( end ) {
var lastMultiMaterial = this.currentMaterial();
if ( lastMultiMaterial && lastMultiMaterial.groupEnd === -1 ) {
lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
}
// Guarantee at least one empty material, this makes the creation later more straight forward.
if ( end !== false && this.materials.length === 0 ) {
this.materials.push({
name : '',
smooth : this.smooth
});
}
return lastMultiMaterial;
}
};
this.objects.push( this.object );
},
finalize : function() {
if ( this.object && typeof this.object._finalize === 'function' ) {
this.object._finalize();
}
},
parseVertexIndex: function ( value, len ) {
var index = parseInt( value, 10 );
......@@ -464,7 +531,7 @@ THREE.OBJLoader.prototype = {
// material
state.object.material.name = line.substring( 7 ).trim();
state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
} else if ( this.regexp.material_library_pattern.test( line ) ) {
......@@ -476,8 +543,22 @@ THREE.OBJLoader.prototype = {
// smooth shading
// @todo Handle files that have varying smooth values for a set of faces inside one geometry,
// but does not define a usemtl for each face set.
// This should be detected and a dummy material created (later MultiMaterial and geometry groups).
// This requires some care to not create extra material on each smooth value for "normal" obj files.
// where explicit usemtl defines geometry groups.
// Example asset: examples/models/obj/cerberus/Cerberus.obj
var value = result[ 1 ].trim().toLowerCase();
state.object.material.smooth = ( value === '1' || value === 'on' );
state.object.smooth = ( value === '1' || value === 'on' );
var material = state.object.currentMaterial();
if ( material ) {
material.smooth = state.object.smooth;
}
} else {
......@@ -490,6 +571,8 @@ THREE.OBJLoader.prototype = {
}
state.finalize();
var container = new THREE.Group();
container.materialLibraries = [].concat( state.materialLibraries );
......@@ -497,6 +580,7 @@ THREE.OBJLoader.prototype = {
var object = state.objects[ i ];
var geometry = object.geometry;
var materials = object.materials;
var isLine = ( geometry.type === 'Line' );
// Skip o/g line declarations that did not follow with any faces
......@@ -522,33 +606,64 @@ THREE.OBJLoader.prototype = {
}
var material;
// Create materials
var createdMaterials = [];
for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
var sourceMaterial = materials[mi];
var material = undefined;
if ( this.materials !== null ) {
if ( this.materials !== null ) {
material = this.materials.create( object.material.name );
material = this.materials.create( sourceMaterial.name );
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
// mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
var materialLine = new THREE.LineBasicMaterial();
materialLine.copy( material );
material = materialLine;
var materialLine = new THREE.LineBasicMaterial();
materialLine.copy( material );
material = materialLine;
}
}
}
if ( ! material ) {
if ( ! material ) {
material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
material.name = sourceMaterial.name;
material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
material.name = object.material.name;
}
material.shading = sourceMaterial.smooth ? THREE.SmoothShading : THREE.FlatShading;
createdMaterials.push(material);
}
material.shading = object.material.smooth ? THREE.SmoothShading : THREE.FlatShading;
// Create mesh
var mesh;
if ( createdMaterials.length > 1 ) {
for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
var sourceMaterial = materials[mi];
buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
}
var multiMaterial = new THREE.MultiMaterial( createdMaterials );
mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, multiMaterial ) : new THREE.Line( buffergeometry, multiMaterial ) );
} else {
mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.Line( buffergeometry, createdMaterials[ 0 ] ) );
}
var mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, material ) : new THREE.Line( buffergeometry, material ) );
mesh.name = object.name;
container.add( mesh );
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册