[SOLVED] Accessing blend keys / morph targets in script

Hey,

I’m trying to figure out how to access blend keys / shape keys / morph targets in PlayCanvas. So far I have only been able to drive them by binding them to bones (in Blender) and playing animations on said bones/mesh. Manipulating the bones directly (outside of an animation) does not seem to trigger the morph target, and I can’t find any other value or variable on the entity that could be used to set it manually in a script.

Being able to access blend keys in script would be super useful for a veriaty of effects such as wind in grass, independant facial expressions, fitting clothes, varying trees etc.

Anyone know if it even is possible to set morph targets manually in PlayCanvas? And if so, how?

Hi @Astra ,

@Elliott or @yaustar should be able to help here, but I also wanted to mention that our model viewer is open source and might hold some clues as well (it has UI for manipulating blendshapes).

That’s at GitHub - playcanvas/model-viewer: glTF 2.0 model viewer.

Thanks!

1 Like

Here’s an example that does it (it creates morph targets, and then applies them from script)
https://playcanvas.github.io/#/graphics/mesh-morph-many

But Basically: From entity, you access render or model component, which ever you use, they both support this. That has a list of MeshInstances. Each MeshInstance has a property morphInstance, and then you can use setWeight to change a weight of a blend target. To get names of those, you can access MorphInstance.morph.targets to get an array of MorphTarget, which has a name.

2 Likes

Thanks a bunch!
that rly helped :smiley:
Also, haven’t seen that example project yet. Rly interesting.

For anyone else looking for this, this is how I ended up setting a specific value for a specific blend key / morph target by name:

let item = this.entity //or whatever entity you want
let keyName = 'key1' //or whatever you named your shape key / blend key
let newValue = 1 //or whatever you want to set your key to

//Find and change morph targets
for(let i = 0; i < item.children.length; i++){
    //if child has a render target proceed
    if(item.children[i].render){
        let meshInst = item.children[i].render.meshInstances;
        for(let j = 0; j < meshInst.length; j++){
            let morphInst = meshInst[j].morphInstance;
            let targets = morphInst.morph.targets;
            //find index of morph target
            for(let k = 0; k < targets.length; k++){
                if(targets[k].name == keyName){
                    //and set the morph target value
                    morphInst.setWeight(k, newValue);
                }
            }
        }
    }
}

This code snippet could prob be a bit more performant, but at least it works (as long as all the entities with render targets are direct children of the “Item” entity.

1 Like

I just checked, and you can use index or a name directly when calling setWeight (assuming morph target had names). So you can call morphInst.setWeight(keyName, newValue);

oh, ok, neat! then I guess the inner for-loop is unnecessary