Ver Fonte

ColladaLoader2: Better skeleton parsing

Mugen87 há 8 anos atrás
pai
commit
4426fe7488
1 ficheiros alterados com 81 adições e 51 exclusões
  1. 81 51
      examples/js/loaders/ColladaLoader2.js

+ 81 - 51
examples/js/loaders/ColladaLoader2.js

@@ -2203,7 +2203,8 @@ THREE.ColladaLoader.prototype = {
 
 			var data = {
 				id: parseId( xml.getAttribute( 'url' ) ),
-				materials: {}
+				materials: {},
+				skeletons: []
 			};
 
 			for ( var i = 0; i < xml.childNodes.length; i ++ ) {
@@ -2228,8 +2229,7 @@ THREE.ColladaLoader.prototype = {
 						break;
 
 					case 'skeleton':
-						if ( data.skeleton !== undefined ) console.warn( 'THREE.ColladaLoader: The loader only supports one skeleton per instance_controller.' );
-						data.skeleton = parseId( child.textContent );
+						data.skeletons.push( parseId( child.textContent ) );
 						break;
 
 					default:
@@ -2243,67 +2243,43 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
-		function getSkeleton( root, controller ) {
+		function getSkeleton( skeletons, joints, skinnedMesh ) {
 
 			var boneData = [];
-			var missingBoneData = [];
 			var sortedBoneData = [];
 
-			var joints = controller.skin.joints;
+			var i, j, data;
 
-			// setup bone data from visual scene
-
-			root.traverse( function( object ) {
-
-				if ( object.isBone === true ) {
+			// a skeleton can have multiple root bones. collada expresses this
+			// situtation with multiple "skeleton" tags per controller instance
 
-					var boneInverse;
-
-					for ( var i = 0; i < joints.length; i ++ ) {
-
-						var joint = joints[ i ];
+			for ( i = 0; i < skeletons.length; i ++ ) {
 
-						if ( joint.name === object.name ) {
+				var skeleton = skeletons[ i ];
+				var root = getNode( skeleton );
 
-							boneInverse = joint.boneInverse;
-							break;
+				// bone hierarchy is a child of the skinned mesh
 
-						}
+				skinnedMesh.add( root );
 
-					}
+				// setup bone data for a single bone hierarchy
 
-					if ( boneInverse === undefined ) {
+				buildBoneHierarchy( root, joints, boneData );
 
-						// Unfortunately, there can be joints in the visual scene that are not part of the
-						// corresponding controller. In this case, we have to create a dummy boneInverse matrix
-						// for the respective bone. This bone won't affect any vertices, because there are no skin indices
-						// and weights defined for it. But we still have to add the bone to the sorted bone list in order to
-						// ensure a correct animation of the model.
-
-						missingBoneData.push( { bone: object, boneInverse: new THREE.Matrix4() } );
-						console.warn( 'THREE.ColladaLoader: Missing data for bone: %s.', object.name );
-
-					} else {
-
-						boneData.push( { bone: object, boneInverse: boneInverse } );
-
-					}
-
-				}
-
-			} );
+			}
 
 			// sort bone data (the order is defined in the corresponding controller)
 
-			for ( var i = 0; i < joints.length; i ++ ) {
+			for ( i = 0; i < joints.length; i ++ ) {
 
-				for ( var j = 0; j < boneData.length; j ++ ) {
+				for ( j = 0; j < boneData.length; j ++ ) {
 
-					var data = boneData[ j ];
+					data = boneData[ j ];
 
 					if ( data.bone.name === joints[ i ].name ) {
 
 						sortedBoneData[ i ] = data;
+						data.processed = true;
 						break;
 
 					}
@@ -2312,11 +2288,18 @@ THREE.ColladaLoader.prototype = {
 
 			}
 
-			// add missing bone data at the end of the list
+			// add unprocessed bone data at the end of the list
+
+			for ( i = 0; i < boneData.length; i ++ ) {
 
-			for ( var i = 0; i < missingBoneData.length; i ++ ) {
+				data = boneData[ i ];
 
-				sortedBoneData.push( missingBoneData[ i ] );
+				if ( data.processed === false ) {
+
+					sortedBoneData.push( data );
+					data.processed = true;
+
+				}
 
 			}
 
@@ -2325,9 +2308,9 @@ THREE.ColladaLoader.prototype = {
 			var bones = [];
 			var boneInverses = [];
 
-			for ( var i = 0; i < sortedBoneData.length; i ++ ) {
+			for ( i = 0; i < sortedBoneData.length; i ++ ) {
 
-				var data = sortedBoneData[ i ];
+				data = sortedBoneData[ i ];
 
 				bones.push( data.bone );
 				boneInverses.push( data.boneInverse );
@@ -2338,6 +2321,52 @@ THREE.ColladaLoader.prototype = {
 
 		}
 
+		function buildBoneHierarchy( root, joints, boneData ) {
+
+			// setup bone data from visual scene
+
+			root.traverse( function( object ) {
+
+				if ( object.isBone === true ) {
+
+					var boneInverse;
+
+					// retrieve the boneInverse from the controller data
+
+					for ( var i = 0; i < joints.length; i ++ ) {
+
+						var joint = joints[ i ];
+
+						if ( joint.name === object.name ) {
+
+							boneInverse = joint.boneInverse;
+							break;
+
+						}
+
+					}
+
+					if ( boneInverse === undefined ) {
+
+						// Unfortunately, there can be joints in the visual scene that are not part of the
+						// corresponding controller. In this case, we have to create a dummy boneInverse matrix
+						// for the respective bone. This bone won't affect any vertices, because there are no skin indices
+						// and weights defined for it. But we still have to add the bone to the sorted bone list in order to
+						// ensure a correct animation of the model.
+
+						 boneInverse = new THREE.Matrix4();
+						 console.warn( 'THREE.ColladaLoader: Missing data for bone: %s.', object.name );
+
+					}
+
+					boneData.push( { bone: object, boneInverse: boneInverse, processed: false } );
+
+				}
+
+			} );
+
+		}
+
 		function buildNode( data ) {
 
 			var objects = [];
@@ -2371,12 +2400,13 @@ THREE.ColladaLoader.prototype = {
 				var materials = resolveMaterialBinding( geometry.materialKeys, instance.materials );
 				var object = getObject( geometry, materials );
 
-				var node = getNode( instance.skeleton );
-				var skeleton = getSkeleton( node, controller );
+				var skeletons = instance.skeletons;
+				var joints = controller.skin.joints;
+
+				var skeleton = getSkeleton( skeletons, joints, object );
 
 				object.bind( skeleton, controller.skin.bindMatrix );
 				object.normalizeSkinWeights();
-				object.add( node ); // bone hierarchy is a child of the skinned mesh
 
 				objects.push( object );