Ver código fonte

GLTFLoader: Clean up

Takahiro 7 anos atrás
pai
commit
f70f91b63f
1 arquivos alterados com 191 adições e 166 exclusões
  1. 191 166
      examples/js/loaders/GLTFLoader.js

+ 191 - 166
examples/js/loaders/GLTFLoader.js

@@ -1161,8 +1161,6 @@ THREE.GLTFLoader = ( function () {
 		morphAttributes.position = [];
 		morphAttributes.normal = [];
 
-		material.morphTargets = true;
-
 		for ( var i = 0, il = targets.length; i < il; i ++ ) {
 
 			var target = targets[ i ];
@@ -1217,8 +1215,6 @@ THREE.GLTFLoader = ( function () {
 
 			if ( target.NORMAL !== undefined ) {
 
-				material.morphNormals = true;
-
 				// see target.POSITION's comment
 
 				normalAttribute = cloneBufferAttribute( accessors[ target.NORMAL ] );
@@ -1345,14 +1341,16 @@ THREE.GLTFLoader = ( function () {
 	}
 
 	/**
-	 * Checks if we can build a single Mesh with MultiMaterial from the primitives.
+	 * Checks if we can build a single Mesh with MultiMaterial from multiple primitives.
 	 * Returns true if all primitives use the same attributes/morphAttributes/mode
 	 * and also have index. Otherwise returns false.
 	 *
-	 * @param {Array<Object>} primitives whose length is > 1
+	 * @param {Array<GLTF.Primitive>} primitives
 	 * @return {Boolean}
 	 */
-	function isCombinable( primitives ) {
+	function isCombinablePrimitives( primitives ) {
+
+		if ( primitives.length < 2 ) return false;
 
 		var primitive0 = primitives[ 0 ];
 		var targets0 = primitive0.targets || [];
@@ -1383,60 +1381,6 @@ THREE.GLTFLoader = ( function () {
 
 	}
 
-	/**
-	 * Builds a single Mesh with MultiMaterial from meshes.
-	 *
-	 * @param {Array<THREE.Object3D>} meshes whose length is > 1
-	 * @param {Array<Object>} primitives whose length is same as the meshes'
-	 * @param {Array<THREE.Material>} materials
-	 * @return {Object3D}
-	 */
-	function combineMeshes( meshes, primitives, materials ) {
-
-		var newGeometry = meshes[ 0 ].geometry.clone();
-		var newMaterials = [];
-		var indices = []
-		var offset = 0;
-
-		var hasMorphTargets = Object.keys( newGeometry.morphAttributes ).length > 0;
-		var hasMorphNormals = hasMorphTargets && ( newGeometry.morphAttributes.normal !== undefined );
-
-		for ( var i = 0, il = meshes.length; i < il; i ++ ) {
-
-			var mesh = meshes[ i ];
-			var primitive = primitives[ i ];
-
-			var index = mesh.geometry.index;
-			var material = materials[ primitive.material ];
-
-			if ( mesh.isSkinnedMesh ) material.skinning = true;
-			if ( hasMorphTargets ) material.morphTargets = true;
-			if ( hasMorphNormals ) material.morphNormals = true;
-
-			newGeometry.addGroup( offset, index.count, i );
-			newMaterials.push( material );
-
-			for ( var j = 0, jl = index.array.length; j < jl; j ++ ) {
-
-				indices.push( index.array[ j ] );
-
-			}
-
-			offset += index.count;
-
-		}
-
-		newGeometry.setIndex( indices );
-
-		var newMesh = new meshes[ 0 ].constructor( newGeometry, newMaterials );
-
-		newMesh.morphTargetDictionary = Object.assign( {}, meshes[ 0 ].morphTargetDictionary );
-		newMesh.name = meshes[ 0 ].name.slice( 0, meshes[ 0 ].name.lastIndexOf( '_' ) );
-
-		return newMesh;
-
-	}
-
 	/* GLTF PARSER */
 
 	function GLTFParser( json, extensions, options ) {
@@ -2098,7 +2042,7 @@ THREE.GLTFLoader = ( function () {
 	 * @param  {GLTF.Primitive} primitiveDef
 	 * @param  {Array<THREE.BufferAttribute>} accessors
 	 */
-	function addPrimitiveAttributes ( geometry, primitiveDef, accessors ) {
+	function addPrimitiveAttributes( geometry, primitiveDef, accessors ) {
 
 		var attributes = primitiveDef.attributes;
 
@@ -2115,7 +2059,31 @@ THREE.GLTFLoader = ( function () {
 
 		}
 
-		if ( primitiveDef.indices !== undefined && !geometry.index ) {
+		// .indicesArray isn't in glTF spec. See .loadGeometries()
+		if ( primitiveDef.indicesArray !== undefined && ! geometry.index ) {
+
+			var indices = [];
+			var offset = 0;
+
+			for ( var i = 0, il = primitiveDef.indicesArray.length; i < il; i ++ ) {
+
+				var accessor = accessors[ primitiveDef.indicesArray[ i ] ];
+
+				for ( var j = 0, jl = accessor.count; j < jl; j ++ ) {
+
+					indices.push( accessor.array[ j ] );
+
+				}
+
+				geometry.addGroup( offset, accessor.count, i );
+
+				offset += accessor.count;
+
+			}
+
+			geometry.setIndex( indices );
+
+		} else if ( primitiveDef.indices !== undefined && ! geometry.index ) {
 
 			geometry.setIndex( accessors[ primitiveDef.indices ] );
 
@@ -2125,6 +2093,11 @@ THREE.GLTFLoader = ( function () {
 
 	/**
 	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
+	 *
+	 * Creates BufferGeometries from primitives.
+	 * If we can build a single BufferGeometry with .groups from multiple primitives, returns one BufferGeometry.
+	 * Otherwise, returns BufferGeometries without .groups as many as primitives.
+	 *
 	 * @param {Array<Object>} primitives
 	 * @return {Promise<Array<THREE.BufferGeometry>>}
 	 */
@@ -2134,6 +2107,26 @@ THREE.GLTFLoader = ( function () {
 		var extensions = this.extensions;
 		var cache = this.primitiveCache;
 
+		if ( isCombinablePrimitives( primitives ) ) {
+
+			// We builds a single BufferGeometry with .groups from multiple primitives
+			// because all primitives share the same attributes/morph/mode and have indices.
+
+			var primitive = Object.assign( {}, primitives[ 0 ] );
+			primitive.indicesArray = [];
+
+			// combines indices in addPrimitiveAttributes() later
+
+			for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+
+				primitive.indicesArray[ i ] = primitives[ i ].indices;
+
+			}
+
+			primitives = [ primitive ];
+
+		}
+
 		return this.getDependencies( 'accessor' ).then( function ( accessors ) {
 
 			var pending = [];
@@ -2177,12 +2170,7 @@ THREE.GLTFLoader = ( function () {
 					var geometryPromise = Promise.resolve( geometry );
 
 					// Cache this geometry
-					cache.push( {
-
-						primitive: primitive,
-						promise: geometryPromise
-
-					} );
+					cache.push( { primitive: primitive, promise: geometryPromise } );
 
 					pending.push( geometryPromise );
 
@@ -2216,190 +2204,226 @@ THREE.GLTFLoader = ( function () {
 
 		] ).then( function ( dependencies ) {
 
-			var group = new THREE.Group();
-
 			var primitives = meshDef.primitives;
+			var meshes = [];
+			var originalMaterials = [];
+
+			for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+
+				originalMaterials[ i ] = primitives[ i ].material === undefined
+					? createDefaultMaterial()
+					: dependencies.materials[ primitives[ i ].material ];
+
+			}
 
 			return scope.loadGeometries( primitives ).then( function ( geometries ) {
 
-				for ( var i = 0, il = primitives.length; i < il; i ++ ) {
+				var isMultiMaterial = geometries.length === 1 && geometries[ 0 ].groups.length > 0;
+
+				for ( var i = 0, il = geometries.length; i < il; i ++ ) {
 
-					var primitive = primitives[ i ];
 					var geometry = geometries[ i ];
+					var primitive = primitives[ i ];
 
-					var material = primitive.material === undefined
-						? createDefaultMaterial()
-						: dependencies.materials[ primitive.material ];
+					// 1. create Mesh
 
-					if ( material.aoMap
-							&& geometry.attributes.uv2 === undefined
-							&& geometry.attributes.uv !== undefined ) {
+					var mesh;
 
-						console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
-						geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
+					var material = isMultiMaterial ? originalMaterials : originalMaterials[ i ]
 
-					}
+					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
+						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
+						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
+						primitive.mode === undefined ) {
 
-					// If the material will be modified later on, clone it now.
-					var useVertexColors = geometry.attributes.color !== undefined;
-					var useFlatShading = geometry.attributes.normal === undefined;
-					var useSkinning = meshDef.isSkinnedMesh === true;
-					var useMorphTargets = primitive.targets !== undefined;
+						// .isSkinnedMesh isn't in glTF spec. See .markDefs()
+						mesh = meshDef.isSkinnedMesh === true
+							? new THREE.SkinnedMesh( geometry, material )
+							: new THREE.Mesh( geometry, material );
 
-					if ( useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
+						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
 
-						if ( material.isGLTFSpecularGlossinessMaterial ) {
+							mesh.drawMode = THREE.TriangleStripDrawMode;
 
-							var specGlossExtension = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ];
-							material = specGlossExtension.cloneMaterial( material );
+						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
+
+							mesh.drawMode = THREE.TriangleFanDrawMode;
+
+						}
+
+					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ||
+						primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ||
+						primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+
+						if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
+
+							mesh = new THREE.LineSegments( geometry, material );
+
+						} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
+
+							mesh = new THREE.Line( geometry, material );
 
 						} else {
 
-							material = material.clone();
+							mesh = new THREE.LineLoop( geometry, material );
 
 						}
 
-					}
+					} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+
+						mesh = new THREE.Points( geometry, material );
 
-					if ( useVertexColors ) {
+					} else {
 
-						material.vertexColors = THREE.VertexColors;
-						material.needsUpdate = true;
+						throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
 
 					}
 
-					if ( useFlatShading ) {
+					if ( primitive.targets !== undefined ) {
 
-						material.flatShading = true;
+						addMorphTargets( mesh, meshDef, primitive, dependencies.accessors );
 
 					}
 
-					var mesh;
+					mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
 
-					if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
-						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
-						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
-						primitive.mode === undefined ) {
+					if ( geometries.length > 1 ) mesh.name += '_' + i;
 
-						if ( useSkinning ) {
+					if ( meshDef.extras !== undefined ) mesh.userData = meshDef.extras;
+					if ( primitive.extras !== undefined ) mesh.geometry.userData = primitive.extras;
 
-							mesh = new THREE.SkinnedMesh( geometry, material );
-							material.skinning = true;
+					meshes.push( mesh );
 
-						} else {
+					// 2. update Material depending on Mesh and BufferGeometry
 
-							mesh = new THREE.Mesh( geometry, material );
+					var materials = isMultiMaterial ? mesh.material : [ mesh.material ];
 
-						}
+					var useVertexColors = geometry.attributes.color !== undefined;
+					var useFlatShading = geometry.attributes.normal === undefined;
+					var useSkinning = mesh.isSkinnedMesh === true;
+					var useMorphTargets = Object.keys( geometry.morphAttributes ).length > 0;
+					var useMorphNormals = useMorphTargets && geometry.morphAttributes.normal !== undefined;
 
-						if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ) {
+					for ( var j = 0, jl = materials.length; j < jl; j ++ ) {
 
-							mesh.drawMode = THREE.TriangleStripDrawMode;
+						var material = materials[ j ];
 
-						} else if ( primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ) {
+						if ( mesh.isPoints ) {
 
-							mesh.drawMode = THREE.TriangleFanDrawMode;
+							var cacheKey = 'PointsMaterial:' + material.uuid;
 
-						}
+							var pointsMaterial = scope.cache.get( cacheKey );
 
-					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ||
-						primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ||
-						primitive.mode === WEBGL_CONSTANTS.LINE_LOOP ) {
+							if ( ! pointsMaterial ) {
 
-						var cacheKey = 'LineBasicMaterial:' + material.uuid;
+								pointsMaterial = new THREE.PointsMaterial();
+								THREE.Material.prototype.copy.call( pointsMaterial, material );
+								pointsMaterial.color.copy( material.color );
+								pointsMaterial.map = material.map;
+								pointsMaterial.lights = false;  // PointsMaterial doesn't support lights yet
 
-						var lineMaterial = scope.cache.get( cacheKey );
+								scope.cache.add( cacheKey, pointsMaterial );
 
-						if ( ! lineMaterial ) {
+							}
 
-							lineMaterial = new THREE.LineBasicMaterial();
-							THREE.Material.prototype.copy.call( lineMaterial, material );
-							lineMaterial.color.copy( material.color );
-							lineMaterial.lights = false;  // LineBasicMaterial doesn't support lights yet
+							material = pointsMaterial;
 
-							scope.cache.add( cacheKey, lineMaterial );
+						} else if ( mesh.isLine ) {
 
-						}
+							var cacheKey = 'LineBasicMaterial:' + material.uuid;
 
-						material = lineMaterial;
+							var lineMaterial = scope.cache.get( cacheKey );
 
-						if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
+							if ( ! lineMaterial ) {
 
-							mesh = new THREE.LineSegments( geometry, material );
+								lineMaterial = new THREE.LineBasicMaterial();
+								THREE.Material.prototype.copy.call( lineMaterial, material );
+								lineMaterial.color.copy( material.color );
+								lineMaterial.lights = false;  // LineBasicMaterial doesn't support lights yet
 
-						} else if ( primitive.mode === WEBGL_CONSTANTS.LINE_STRIP ) {
+								scope.cache.add( cacheKey, lineMaterial );
 
-							mesh = new THREE.Line( geometry, material );
+							}
 
-						} else {
+							material = lineMaterial;
 
-							mesh = new THREE.LineLoop( geometry, material );
+						}
+
+						// If the material will be modified later on, clone it now.
+						if ( useVertexColors || useFlatShading || useSkinning || useMorphTargets ) {
+
+							material = material.isGLTFSpecularGlossinessMaterial
+									? extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].cloneMaterial( materials )
+									: material.clone();
 
 						}
 
-					} else if ( primitive.mode === WEBGL_CONSTANTS.POINTS ) {
+						if ( useSkinning ) {
+
+							material.skinning = true;
 
-						var cacheKey = 'PointsMaterial:' + material.uuid;
+						}
 
-						var pointsMaterial = scope.cache.get( cacheKey );
+						if ( useVertexColors ) {
 
-						if ( ! pointsMaterial ) {
+							material.vertexColors = THREE.VertexColors;
+							material.needsUpdate = true;
 
-							pointsMaterial = new THREE.PointsMaterial();
-							THREE.Material.prototype.copy.call( pointsMaterial, material );
-							pointsMaterial.color.copy( material.color );
-							pointsMaterial.map = material.map;
-							pointsMaterial.lights = false;  // PointsMaterial doesn't support lights yet
+						}
 
-							scope.cache.add( cacheKey, pointsMaterial );
+						if ( useFlatShading ) {
+
+							material.flatShading = true;
 
 						}
 
-						material = pointsMaterial;
+						if ( useMorphTargets ) {
 
-						mesh = new THREE.Points( geometry, material );
+							material.morphTargets = true;
 
-					} else {
+						}
 
-						throw new Error( 'THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode );
+						if ( useMorphNormals ) {
 
-					}
+							material.morphNormals = true;
 
-					mesh.name = meshDef.name || ( 'mesh_' + meshIndex );
+						}
 
-					if ( useMorphTargets ) {
+						materials[ j ] = material;
 
-						addMorphTargets( mesh, meshDef, primitive, dependencies.accessors );
+						// workarounds for mesh and geometry
 
-					}
+						if ( material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined ) {
 
-					if ( meshDef.extras !== undefined ) mesh.userData = meshDef.extras;
-					if ( primitive.extras !== undefined ) mesh.geometry.userData = primitive.extras;
+							console.log( 'THREE.GLTFLoader: Duplicating UVs to support aoMap.' );
+							geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
 
-					// for Specular-Glossiness.
-					if ( material.isGLTFSpecularGlossinessMaterial === true ) {
+						}
 
-						mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
+						if ( material.isGLTFSpecularGlossinessMaterial ) {
 
-					}
+							// for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
+							mesh.onBeforeRender = extensions[ EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS ].refreshUniforms;
 
-					if ( primitives.length > 1 ) {
+						}
 
-						mesh.name += '_' + i;
+					}
 
-						group.add( mesh );
+					mesh.material = isMultiMaterial ? materials : materials[ 0 ];
 
-					} else {
+				}
 
-						return mesh;
+				if ( meshes.length === 1 ) {
 
-					}
+					return meshes[ 0 ];
 
 				}
 
-				if ( isCombinable( primitives ) ) {
+				var group = new THREE.Group();
+
+				for ( var i = 0, il = meshes.length; i < il; i ++ ) {
 
-					return combineMeshes( group.children, primitives, dependencies.materials );
+					group.add( meshes[ i ] );
 
 				}
 
@@ -2642,6 +2666,7 @@ THREE.GLTFLoader = ( function () {
 
 			var node;
 
+			// .isBone isn't in glTF spec. See .markDefs
 			if ( nodeDef.isBone === true ) {
 
 				node = new THREE.Bone();