From cea5e0c92ca5042649b87dce90b4e34eea7ff68e Mon Sep 17 00:00:00 2001 From: Takahiro Date: Wed, 17 May 2017 09:11:26 +0900 Subject: [PATCH 1/6] Support glTF 2.0 morph animation --- examples/js/loaders/GLTF2Loader.js | 158 +++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 10 deletions(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 05b48eb83adac4..f2e7d39db92b88 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/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 + ) ); + + } } From 5622169499f72768399f7430f6cd3ce0f0b50f72 Mon Sep 17 00:00:00 2001 From: Takahiro Date: Wed, 17 May 2017 09:17:23 +0900 Subject: [PATCH 2/6] Remove unnecessary lines in GLTF2Loader --- examples/js/loaders/GLTF2Loader.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index f2e7d39db92b88..38cf655e2bd172 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -2180,8 +2180,6 @@ THREE.GLTF2Loader = ( function () { // TODO: implement if ( target.TANGENT !== undefined ) { - console.log( dependencies.accessors[ target.NORMAL ] ); - } } @@ -2362,7 +2360,6 @@ 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; From 4bb17ded64beacd63268d544e2cb6bca69c5e89c Mon Sep 17 00:00:00 2001 From: Takahiro Date: Wed, 17 May 2017 11:39:40 +0900 Subject: [PATCH 3/6] Fix typo --- examples/js/loaders/GLTF2Loader.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 38cf655e2bd172..1d767abf5d60de 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -2141,7 +2141,7 @@ THREE.GLTF2Loader = ( function () { // + weight1 * morphTarget1 // ... // then adding position to morphTarget. - // motphTarget should depend on mesh, so cloning attribute. + // morphTarget should depend on mesh, so cloning attribute. var attribute = dependencies.accessors[ target.POSITION ].clone(); var position = geometry.attributes.position; From 6fc2b00d53930530278e66def6740e82b96f6062 Mon Sep 17 00:00:00 2001 From: Takahiro Date: Wed, 17 May 2017 11:42:01 +0900 Subject: [PATCH 4/6] Update comment for attribute cloning --- examples/js/loaders/GLTF2Loader.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 1d767abf5d60de..be7b542c025891 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -2141,7 +2141,8 @@ THREE.GLTF2Loader = ( function () { // + weight1 * morphTarget1 // ... // then adding position to morphTarget. - // morphTarget should depend on mesh, so cloning attribute. + // So morphTarget value will depend on mesh's position, then cloning attribute + // for the case if attribute is shared among two or more meshes. var attribute = dependencies.accessors[ target.POSITION ].clone(); var position = geometry.attributes.position; From 7139fe91897698040859da68fcfbf6a36e201f6f Mon Sep 17 00:00:00 2001 From: Takahiro Date: Thu, 18 May 2017 07:52:14 +0900 Subject: [PATCH 5/6] Set morph BufferAttribute name instead of pushing name to morphTargets --- examples/js/loaders/GLTF2Loader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 27fbce05908c51..20a7cd9da123f0 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -2158,15 +2158,13 @@ THREE.GLTF2Loader = ( function () { 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 } ); + var attributeName = 'morphTarget' + i; if ( target.POSITION !== undefined ) { @@ -2189,6 +2187,7 @@ THREE.GLTF2Loader = ( function () { // for the case if attribute is shared among two or more meshes. var attribute = dependencies.accessors[ target.POSITION ].clone(); + attribute.name = attributeName; var position = geometry.attributes.position; for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) { @@ -2210,6 +2209,7 @@ THREE.GLTF2Loader = ( function () { // see target.POSITION's comment var attribute = dependencies.accessors[ target.NORMAL ].clone(); + attribute.name = attributeName; var normal = geometry.attributes.normal; for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) { From ee87cc5c239c4286f558d5be1c0b30871bec78a2 Mon Sep 17 00:00:00 2001 From: Takahiro Date: Thu, 18 May 2017 10:32:45 +0900 Subject: [PATCH 6/6] Update morph of GLTF2Loader --- examples/js/loaders/GLTF2Loader.js | 43 +++++++++++++++++++----------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/examples/js/loaders/GLTF2Loader.js b/examples/js/loaders/GLTF2Loader.js index 20a7cd9da123f0..f146bd61364362 100644 --- a/examples/js/loaders/GLTF2Loader.js +++ b/examples/js/loaders/GLTF2Loader.js @@ -2161,16 +2161,19 @@ THREE.GLTF2Loader = ( function () { var targets = primitive.targets; var morphAttributes = geometry.morphAttributes; + morphAttributes.position = []; + morphAttributes.normal = []; + + material.morphTargets = true; + for ( var i = 0, il = targets.length; i < il; i ++ ) { var target = targets[ i ]; var attributeName = 'morphTarget' + i; - if ( target.POSITION !== undefined ) { - - material.morphTargets = true; + var positionAttribute, normalAttribute; - if ( morphAttributes.position === undefined ) morphAttributes.position = []; + if ( target.POSITION !== undefined ) { // Three.js morph formula is // position @@ -2186,17 +2189,20 @@ THREE.GLTF2Loader = ( function () { // So morphTarget value will depend on mesh's position, then cloning attribute // for the case if attribute is shared among two or more meshes. - var attribute = dependencies.accessors[ target.POSITION ].clone(); - attribute.name = attributeName; + positionAttribute = dependencies.accessors[ target.POSITION ].clone(); var position = geometry.attributes.position; - for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) { + for ( var j = 0, jl = positionAttribute.array.length; j < jl; j ++ ) { - attribute.array[ j ] += position.array[ j ]; + positionAttribute.array[ j ] += position.array[ j ]; } - morphAttributes.position.push( attribute ); + } else { + + // Copying the original position not to affect the final position. + // See the formula above. + positionAttribute = geometry.attributes.position.clone(); } @@ -2204,21 +2210,20 @@ THREE.GLTF2Loader = ( function () { material.morphNormals = true; - if ( morphAttributes.normal === undefined ) morphAttributes.normal = []; - // see target.POSITION's comment - var attribute = dependencies.accessors[ target.NORMAL ].clone(); - attribute.name = attributeName; + normalAttribute = dependencies.accessors[ target.NORMAL ].clone(); var normal = geometry.attributes.normal; - for ( var j = 0, jl = attribute.array.length; j < jl; j ++ ) { + for ( var j = 0, jl = normalAttribute.array.length; j < jl; j ++ ) { - attribute.array[ j ] += normal.array[ j ]; + normalAttribute.array[ j ] += normal.array[ j ]; } - morphAttributes.normal.push( attribute ); + } else { + + normalAttribute = geometry.attributes.normal.clone(); } @@ -2227,6 +2232,12 @@ THREE.GLTF2Loader = ( function () { } + positionAttribute.name = attributeName; + normalAttribute.name = attributeName; + + morphAttributes.position.push( positionAttribute ); + morphAttributes.normal.push( normalAttribute ); + } meshNode.updateMorphTargets();