浏览代码

Support glTF 2.0 morph animation

Takahiro 8 年之前
父节点
当前提交
cea5e0c92c
共有 1 个文件被更改,包括 148 次插入10 次删除
  1. 148 10
      examples/js/loaders/GLTF2Loader.js

+ 148 - 10
examples/js/loaders/GLTF2Loader.js

@@ -821,7 +821,8 @@ THREE.GLTF2Loader = ( function () {
 	var PATH_PROPERTIES = {
 		scale: 'scale',
 		translation: 'position',
-		rotation: 'quaternion'
+		rotation: 'quaternion',
+		weights: 'morphTargetInfluences'
 	};
 
 	var INTERPOLATION = {
@@ -2111,6 +2112,94 @@ THREE.GLTF2Loader = ( function () {
 						meshNode = new THREE.Mesh( geometry, material );
 						meshNode.castShadow = true;
 
+						if ( primitive.targets !== undefined ) {
+
+							geometry.morphTargets = [];
+
+							var targets = primitive.targets;
+							var morphAttributes = geometry.morphAttributes;
+
+							for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+								var target = targets[ i ];
+								geometry.morphTargets.push( { name: 'morphTarget' + i } );
+
+								if ( target.POSITION !== undefined ) {
+
+									material.morphTargets = true;
+
+									if ( morphAttributes.position === undefined ) morphAttributes.position = [];
+
+									// Three.js morph formula is
+									//   position
+									//     + weight0 * ( morphTarget0 - position )
+									//     + weight1 * ( morphTarget1 - position )
+									//     ...
+									// while the glTF one is
+									//   position
+									//     + weight0 * morphTarget0
+									//     + weight1 * morphTarget1
+									//     ...
+									// then adding position to morphTarget.
+									// motphTarget should depend on mesh, so cloning attribute.
+
+									var attribute = dependencies.accessors[ target.POSITION ].clone();
+									var position = geometry.attributes.position;
+
+									for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) {
+
+										attribute.array[ j ] += position.array[ j ];
+
+									}
+
+									morphAttributes.position.push( attribute );
+
+								}
+
+								if ( target.NORMAL !== undefined ) {
+
+									material.morphNormals = true;
+
+									if ( morphAttributes.normal === undefined ) morphAttributes.normal = [];
+
+									// see target.POSITION's comment
+
+									var attribute = dependencies.accessors[ target.NORMAL ].clone();
+									var normal = geometry.attributes.normal;
+
+									for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) {
+
+										attribute.array[ j ] += normal.array[ j ];
+
+									}
+
+									morphAttributes.normal.push( attribute );
+
+								}
+
+								// TODO: implement
+								if ( target.TANGENT !== undefined ) {
+
+									console.log( dependencies.accessors[ target.NORMAL ] );
+
+								}
+
+							}
+
+							meshNode.updateMorphTargets();
+
+							if ( mesh.weights !== undefined ) {
+
+								for ( var i = 0, il = mesh.weights.length; i < il; i ++ ) {
+
+									meshNode.morphTargetInfluences[ i ] = mesh.weights[ i ];
+
+								}
+
+							}
+
+						}
+
 					} else if ( primitive.mode === WEBGL_CONSTANTS.LINES ) {
 
 						geometry = new THREE.BufferGeometry();
@@ -2273,6 +2362,7 @@ THREE.GLTF2Loader = ( function () {
 
 						var target = channel.target;
 						var name = target.node || target.id; // NOTE: target.id is deprecated.
+
 						var input = animation.parameters !== undefined ? animation.parameters[ sampler.input ] : sampler.input;
 						var output = animation.parameters !== undefined ? animation.parameters[ sampler.output ] : sampler.output;
 
@@ -2286,22 +2376,70 @@ THREE.GLTF2Loader = ( function () {
 							node.updateMatrix();
 							node.matrixAutoUpdate = true;
 
-							var TypedKeyframeTrack = PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.rotation
-								? THREE.QuaternionKeyframeTrack
-								: THREE.VectorKeyframeTrack;
+							var TypedKeyframeTrack;
+
+							switch ( PATH_PROPERTIES[ target.path ] ) {
+
+								case PATH_PROPERTIES.weights:
+
+									TypedKeyframeTrack = THREE.NumberKeyframeTrack;
+									break;
+
+								case PATH_PROPERTIES.rotation:
+
+									TypedKeyframeTrack = THREE.QuaternionKeyframeTrack;
+									break;
+
+								case PATH_PROPERTIES.position:
+								case PATH_PROPERTIES.scale:
+								default:
+
+									TypedKeyframeTrack = THREE.VectorKeyframeTrack;
+									break;
+
+							}
 
 							var targetName = node.name ? node.name : node.uuid;
 							var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[ sampler.interpolation ] : THREE.InterpolateLinear;
 
+							var targetNames = [];
+
+							if ( PATH_PROPERTIES[ target.path ] === PATH_PROPERTIES.weights ) {
+
+								// node should be THREE.Group here but
+								// PATH_PROPERTIES.weights(morphTargetInfluences) should be
+								// the property of a mesh object under node.
+								// So finding targets here.
+
+								node.traverse( function ( object ) {
+
+									if ( object.isMesh === true && object.material.morphTargets === true ) {
+
+										targetNames.push( object.name ? object.name : object.uuid );
+
+									}
+
+								} );
+
+							} else {
+
+								targetNames.push( targetName );
+
+							}
+
 							// KeyframeTrack.optimize() will modify given 'times' and 'values'
 							// buffers before creating a truncated copy to keep. Because buffers may
 							// be reused by other tracks, make copies here.
-							tracks.push( new TypedKeyframeTrack(
-								targetName + '.' + PATH_PROPERTIES[ target.path ],
-								THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ),
-								THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ),
-								interpolation
-							) );
+							for ( var i = 0, il = targetNames.length; i < il; i ++ ) {
+
+								tracks.push( new TypedKeyframeTrack(
+									targetNames[ i ] + '.' + PATH_PROPERTIES[ target.path ],
+									THREE.AnimationUtils.arraySlice( inputAccessor.array, 0 ),
+									THREE.AnimationUtils.arraySlice( outputAccessor.array, 0 ),
+									interpolation
+								) );
+
+							}
 
 						}