FBX Mesh animation Limitations

In my project I need to morph a mesh in different ways and one option is to pre-animate the mesh as an animated fbx. However, i remember in a previous PlayCanvas project trying to use such things as physics affectors etc in Cinema 4D to animate the meshes vertices, but when I brought the mesh into Playcanvas the animation did nothing/did not play.

So, could someone clarify - In Playcanvas, is the only way to animate vertices within an fbx animation by using bones/skinning OR should it be possible that I can manually move vertices from frame to frame in my 3D Software and export that? (Either manually or using physics effectors etc)

Thanks

AFAIK, itā€™s bones and morph targets.

for moprh targets you should be able to move vertices in your modelling app any way you want ā€¦ but youā€™d need to save those changes as morph targets ā€¦ so you might need to collapse your effect stack to morph targets.
When using morphing, you do not animate vertices from frame to frame as you mention above. You simply create multiple copies of the mesh (with moved vertices) and then interpolate between those vertex changes at runtime.

3 Likes

Anyone know how to implement morph targets in Playcanvas. I can see some explanation [here](MorphTarget | PlayCanvas API Reference but there are no examples of usage that I can find.

For example, lets say my fbx contains some poses exported from Cinema4d called Pose.0 and Pose.1.

How do I blend between those 2 poses in Playcanvas/code?

Thanks

Here are two engine examples showing the whole process. These do not use fbx / glb, but construct the targets in code as well, and then drive the weights to interpolate between them.

http://playcanvas.github.io/#/graphics/mesh-morph
http://playcanvas.github.io/#/graphics/mesh-morph-many

Our viewer on the other hand supports loading morph targets from the glb file, including the animation ā€¦ and then the animation drives the morphing.

Example: PlayCanvas glTF Viewer

source code:

1 Like

Thanks for this. The pre built fbx/gITF animations is what I need (not manual mesh manipulation) but with 1400 lines in that example of code its quite hard to pick through and find just the parts that are relevant to morphing the model. When I do a search for morph in that code there are 77 occurrences(!?) and it all looks very complicated and convoluted.

Is there not just a very simple example or project that exists to morph between two targets?

I was kind of hoping for something simple like thisā€¦

myMorphs=GetMorphTargets(myModel);
myMorphs[0].strength=100;
myMorphs[1].strength=0;

:slight_smile:

the engine examples are pretty simple, if you ignore the part where morph targets are generated and only look at the code where they are used.

basically you need to get a mesh instances from your loaded glb. If it has morph instance on it, you can set its weight:
morphInstances[m].setWeight(0, someValue);

maybe something like this could be used to create entity from glb container and find all morph instance (copy & paste from engine example TS files, so might need some cleanup.

        // create a hierarchy of entities with render components
        const entity = assets.statue.resource.instantiateRenderEntity();
        app.root.addChild(entity);

        const renders: Array<pc.RenderComponent> = entity.findComponents("render");
        renders.forEach((render) => {
            const meshInstances = render.meshInstances;
            for (let i = 0; i < meshInstances.length; i++) {
                const meshInstance = meshInstances[i];
                if (meshInstance.morphInstance) {
                    meshInstance.morphInstance.setWeight(0, 0.5);`
                }
            }
        });
1 Like

Okay, but when I import my fbx into Playcanvas, the glb meta data reads that it just contains one mesh instance. Is it supposed to say that it contains more than one?

image

all you need is one. And that mesh instance has morph instance with multiple targets, so you can set their weights on that morph instance.

you can download the glb from the editor and drag & drop it to the viewer ā€¦ if it has morph targets, you get sliders on left you can use to control them.

1 Like

Great. Yes that worked! I can see it in the viewer by just right clicking>open in viewer and the morphing works correctly. Now to try to figure out the codeā€¦

1 Like

Iā€™m having some problem with this code that I donā€™t understandā€¦

What is the render and what is the meshInstance?..Is the meshInstance each pose within the animation?

Is findComponents(ā€œrenderā€); an in built component not displayed in the editor components list?

Thanks

render is a new component that is smilar to the model, but has some additional benefits ā€¦ see the API here:
https://developer.playcanvas.com/en/api/pc.RenderComponent.html

mesh instances on it is the equivalent to mesh instances on the model component that you are perhaps familiar with?

mesh instance is basically a mesh with a material that gets rendered.
and each of these mesh instances has a morphInstance that you can use to modify the morph weights of that mesh.

Okay Thanks. When I drag in a model to the editor by default it comes with a model component already attached so it might be best to use that for my purposes (It also seems to have the ability to have morphInstances). However, when I try the following code Iā€™m getting a - cannot read property ā€˜lengthā€™ of undefined error on the morphInstances array. Know why? The manual says that the model component supports a morphIntances array.

 myModel =this.entity.findComponent("model");
    morphInstances =myModel.morphInstances;
    
    for (i = 0; i < this.morphInstances.length; i++) 
        {
            console.log("FOUND MORPH!!");
            this.morphInstances[i].setWeight(0, 0.5);   
        }

Iā€™m not sure what the problem here is.

Try the rendering component to compare. In the Editor settings under Asset Settings, enable ā€œImport Hierarchyā€ which wonā€™t generate a model file, but a template file to use.

When I add a render component I cant seem to add the glb file to the component. It doesnā€™t allow me to add glbs or fbxā€™s unlike the model component. (I enabled the import Hierarchy option)

If I use model component, it looks like this line is basically returning undefinedā€¦

var myModel =this.entity.findComponent("model");

ā€¦but how?

@Grimmy Example with a simple morph target model and model component: https://playcanvas.com/editor/scene/1185433

Effectively:

        var meshInstances = self.entity.model.meshInstances;
        for (let i = 0; i < meshInstances.length; i++) {
            const meshInstance = meshInstances[i];
            if (meshInstance.morphInstance) {
                meshInstance.morphInstance.setWeight(0, 0.5);
            }
        }
1 Like

Thatā€™s the page for the Model resource. The Model Component API is ModelComponent | PlayCanvas API Reference

Great that works. The main things I take away from that are:
-I access the model component using this.entity.model rather than doing a findComponent(ā€œmodelā€)
-Morph instances are accessed on each of the of each Mesh instances, not the model itself.

However, I want to isolate each of the morph instances into an array. I tried the following code but I get cannot read length of undefined on the morphInstances array. Is it not possible to put each morph into an array?:

 var myModel =this.entity.model;
    
     var meshInstances =myModel.meshInstances;
     for (i = 0; i < meshInstances.length; i++) 
        {
            console.log("FOUND MESH!!");
            
            var morphInstances =meshInstances[i].morphInstances;
    
            for (k = 0; k < morphInstances.length; k++) 
                {
                    console.log("FOUND MORPH!!");
                   morphInstances[k].setWeight(0, 0.5);   
                }
        }

The property morphInstances doesnā€™t exist on the meshInstance object hence itā€™s null.

Each meshInstnace only has one morphInstance so you would want something like this if you want it all in an array:

        var morphs = [];
        var meshInstances = self.entity.model.meshInstances;
        for (let i = 0; i < meshInstances.length; i++) {
            const meshInstance = meshInstances[i];
            if (meshInstance.morphInstance) {
                morphs.push(meshInstance.morphInstance);
            }
        }