Three.js: BufferGeometry with morphAttributes does not create mesh with morphTargetInfluences

Created on 4 May 2017  路  6Comments  路  Source: mrdoob/three.js

If I create a buffer geometry and add a position morph attribute, then create a mesh from it, the mesh doesn't have a morphTargetInfluences property so I can't animate the morph.

Workaround is to add a fake morphTarget to the geometry:

    bufferGeometry.morphTargets = [];
    bufferGeometry.morphTargets.push( 0 );

See this pen:
http://codepen.io/looeee/pen/YVxaKL?editors=0010

Bug

All 6 comments

The following version of Mesh .updateMorphTargets() should solve the problem. Unfortunately, at least MMDLoader won't work with this change. MMDLoader assigns to its BufferGeometry output also .morphTargets which is actually a property of Geometry. I think MMDLoader does this because the name of a morph target animation sequence can't be saved in morphAttributes. But i'm not 100% sure about this. Didn't have time to debug MMDLoader so far. The only thing i can say is that the following example has problems with this change: https://threejs.org/examples/webgl_loader_mmd_pose.html

updateMorphTargets: function () {

    var geometry = this.geometry;

    if ( geometry.isBufferGeometry ) {

        var morphAttributes = geometry.morphAttributes;
        var keys = Object.keys( morphAttributes );

        if ( keys.length > 0 ) {

            var morphAttribute = morphAttributes[ keys[ 0 ] ];

            if ( morphAttribute !== undefined ) {

                this.morphTargetInfluences = [];

                for ( var i = 0, l = morphAttribute.length; i < l; i ++ ) {

                    this.morphTargetInfluences.push( 0 );

                }

            }

        }

    } else {

        var morphTargets = geometry.morphTargets;

        if ( morphTargets !== undefined && morphTargets.length > 0 ) {

            this.morphTargetInfluences = [];
            this.morphTargetDictionary = {};

            for ( var m = 0, ml = morphTargets.length; m < ml; m ++ ) {

                this.morphTargetInfluences.push( 0 );
                this.morphTargetDictionary[ morphTargets[ m ].name ] = m;

            }

        }

    }

},

It looks to me as if MMDLoader is using a similar hack - it's essentially doing

 bufferGeometry.morphTargets = [];
 bufferGeometry.morphTargets.push( morph.name );

https://github.com/mrdoob/three.js/blob/dev/examples/js/loaders/MMDLoader.js#L861, where params is morph.name

The problem (or at least one problem!) is that these names are then used to create the mesh.morphTargetDictionary, which webgl_loader_mmd_pose then loops over to create it's morph controls. So you example code above would need to be updated to create the mesh.morphTargetDictionary as well.

Which would be easily solved if BufferAttribute had a .name property, but at the moment it doesn't. Should we add the property?

So you example code above would need to be updated to create the mesh.morphTargetDictionary as well.

Correct!

Which would be easily solved if BufferAttribute had a .name property, but at the moment it doesn't. Should we add the property?

I like the idea because we won't break stuff and it's easy to implement.

@mrdoob, @WestLangley What's your opinion about this?

I've created a PR so we can better see the changes...

@looeee With the new PR the creation of a morph attribute looks like this:

const morphAttribute = new THREE.BufferAttribute( morphPositions, 3 );
morphAttribute.name = 'base'; // optional, .morphTargetDictionary will always be created with default names
geometry.morphAttributes.position.push( morphAttribute );

Sweet! 馃榿

Was this page helpful?
0 / 5 - 0 ratings