Morph target animation not exported when exporting with animations ONLY

Hi there,

I have an object that has a morph target and I need it animated (done with MAYA).
Well, if I just select the object and export it with the “bake animations” option, it works as expected.
But if I do the same but exporting ONLY the animations (I have the mesh exported as glTF apart, so I can use it in the scene or not at will), it doesn’t work. It doesn’t even appear in the glTF text at all.
Which could be the problem?
Thanks!

Hello! Can you provide us with a playground so we can look in further?

1 Like

I am not a coding guy, but I can send the offending glTFs.

Anim_06_Muelle.gltf is the glTF that was exported with the geometry. Please search for the 11th animation, that’s the one with the morph target activated. As you can see at the final of the animation, the spring rotates and extends and then compress again to initial state. So the morph target animation is there correctly.

On the other hand, Anim_06_Muelle_AnimOnly.gltf was exported with “animations only”, so there is no geomtry but the animations should be there. If you go again to animation number 11, there is the animation for SOME channels, BUT not for the morph target.

Anim_06_Muelle.zip (197.8 KB)
Anim_06_Muelle_AnimOnly.zip (2.7 KB)

So what’s the problem here?

You exported these gltfs with Maya or Babylon?

With MAYA (I stated that in the first post), and the export options are the same in both, the only change is to export “animations only” in the second one.

I’m not sure if we’ll be able to help you with a Maya problem :no_mouth: Have you tried asking on a Maya forum?

I thought this forum was about everything…
I’ll ask there too.

Hey @joie just to confirm, you are using the Babylon Exporters for Maya in GLTF mode ?

if so could you provide a repro and the version of Maya + the exporters you are relying on ?

Yes, I’m using the exporter in glTF mode.
The MAYA version is 2018.5 and the exporter is v20220518.1

Thanks a lot, maybe @PatrickRyan has some tips, or we ll need to dig in the issue with @bghgary

@jole Can you provide the maya scene you are using to export?

@joie, the specification for glTF files defines morph targets as an extension of the mesh concept. This means that for a morph target scalar animation to exist, the mesh that uses the scalar must be present in the file. As you can see from this side-by-side, when you export the file without the mesh, the morph target extras is not included in the file, so no animation of the scalar attached to the morph target will be included:

On the left is the glTF that has the mesh included, and you can see on lines 75-77 that there is an entry in extras with the name of your morph target scalar. On the right is the file with no mesh and you can see that the meshes section of the file where the morph target would be defined is missing. The file skips from nodes right to accessors.

So if you need to separate your mesh morph target from the animation you can accomplish this with a rig trick. If you create a locator in Maya and connect its Y position (or really any other TRS value, but I always use translation since it’s easy to see) to drive the morph target value through the connection editor, you can animate the locator position to change the morph. When you export animations only, include this locator in your export and you will get the animation you need. Then in your scene, just set the morph target value equal to the locator position.y.

This is a bit of a workaround for the glTF specification, but we used this trick a lot in some old HoloLens projects and it worked well if you kept the number of locator nulls to a small number. I know I’ve also used this on some Babylon demos as well, but I don’t remember them off the top of my head which ones they were.

That’s a good explanation on the issue @PatrickRyan
My main issue was that, originally, the morph target was driven by an attribute (it’s the spring of a lift door, so it morphs when opening or closing), but it didn’t export at all.
So I had to bake the animation when the door animation was finished and approved.
And it worked…, when exporting the mesh, but not when exporting just the animation.
Now you say I can let the morph target be driven again by an attribute, although this time the attribute is not a custom one but the translation in one axis…, and it will work again.
With that method I guess I’ll be able to avoid the bake thing, since it is completely destructive.
I will try later on, because that forces me to rebuil the original rig and re export aaaaaaaaall my animated scenes! :open_mouth:
P.S.: Just to comment to my colleage @paleRider it would be easier to just find the offending lines in the code from the mesh file and just copy them to the animation only one and it will work? Would it be that easy?

As you say “then”, I assume that should be done in the babylonJS code, isn’t it?

Anyway, I created the locator, connected the animation to its Y translation, then connected the Y translation to the blendShape node’s weight, and added the locator to my export.

So, the locator’s Y translation animation was indeed exported, but it wasn’t connected in any way to the morph target weight.

Aside from that, I would wanted to ask if all this is a known bug of the exporter (os it doesn’t export morph target animations when exporting animation only glTFs) and it will be fixed in future releases…, or it is intended behaviour.

Thanks in advance for your information.

@joie, you are correct. In the Babylon scene in code, you need to make the value of the morph target parameter equal to the position of the locator axis you animated. This needs to be done per frame so it needs to be in a function on an observable like onBeforeRenderObservable or onBeforeAnimationsObservable depending on when you want it to update.

For your question about morph targets not being exported if there is no mesh exported, this is not a bug and it is not a future feature. As I mentioned above we are limited by the glTF 2.0 specification in that to have a morph target animation, you are extending the mesh concept. If you have no mesh, there is no way to save morph target data. The format is out of our control as it is an open source format with input from a working group made of representatives of many different companies in the industry like AutoDesk, Google, Microsoft, IKEA, Target, Wayfair, NVidia, and many more. There would have to be buy in and a lot of support for a change to the specification at the core level as it would be a breaking change that could have large ripple effects.

If you really need to separate animations from morph targets, you will need to use a pipeline like I described above if you want to stick with glTF. This also means that your glTF files won’t be portable to other renderers as is because they are dependent on some code in Babylon to function. If you want more compatibility, you can strip out any node or skeletal animation from the file and leave in the morph target animation since the glTF isn’t going to be a lot bigger with the animation data in it. But if you truly need to separate it because you want to swap animations on the fly, then you will need to use a stand in abstract mesh like a locator to hold the value animations.

1 Like

Thank you very much for the information @PatrickRyan

My colleage @paleRider will find that explanation very helpful to acomplish the whole thing.

Cheers.

3 Likes

HI there, @carolhmj, @sebavan, @bghgary, @PatrickRyan, and everybody following this thread:

Time has come to join to my team-mate @joie, in order to get rid of this from the “code-side” of things.

We let us know about our advances on next week.

Best regards.

2 Likes

Hi there, @PatrickRyan :

First, I want to apologize for the long delay in this response; but we’ve being having a hard time tackling several aspects of this development, which have kept me apart from this one.

Once again on this subject of the morphtargets being driven by locators, I want to ask you about the API to gain access to the aforementioned in your explanation: “value of the morph target”. We need also to know about how the animated property of the morphtarget must be named/accessed.

Following your instructions, at the moment our (pseudo)code is this:

//animación de Morphtargets (Blendshapes) via Observables
_scene.onBeforeRenderObservable.add(function () {
    if (_scene.getNodeByName("AscensorCompleto1:PuertaRellano3:Muelle_geo")) {
        var locator = _scene.getNodeByName("Muelle_Blendshapes_helper");
	    var morphtarget = _scene.getNodeByName("AscensorCompleto1:PuertaRellano3:Muelle_geo").morphTargetManager.getTarget(0);
        if(locator && morphtarget){
	        morphtarget.MORPHTARGET_PROPERTY_HERE=locator.position.x;
	    }
    }
});

I suppose node.morphTargetManager.getTarget(0) retrieves the correct value as I have only one target in use for that node mesh, but of course that is only a supposition as I don´t know how what’s the API to set the morphtarget intended property, whatever it is.

Thanks for your time.

I can help a little with your pseudo-code example:

//animación de Morphtargets (Blendshapes) via Observables
_scene.onBeforeRenderObservable.add(function () {
    if (_scene.getNodeByName("AscensorCompleto1:PuertaRellano3:Muelle_geo")) {
        var locator = _scene.getNodeByName("Muelle_Blendshapes_helper");
	    var morphtarget = undefined;
 	    for (let morph of _scene.getNodeByName("AscensorCompleto1:PuertaRellano3:Muelle_geo").morphTargetManager._targets.data) {
            if (morph.name === "AscensorCompleto1:PuertaRellano3:Muelle2:Estirar") {
                morphtarget = morph;
            }
        }
	    if(locator && morphtarget){
	        morphtarget.influence=locator.position.x;
	    }
    }
});

Granted, this is not optimal as you won’t want to have to query the scene to get your object and find the influence that matches your morph target name every frame, so I am assuming you will handle that outside an observable that fires every frame. But basically, if you know there is only one morph target on a mesh, you can access it simply with mesh.morphTargetManager.getTarget(key); with key being 0. Then setting an influence is as simple as morphtarget.influence = N

More information on morph targets can be found at Morph Targets | Babylon.js Documentation (babylonjs.com) and this is an example of using morph targets from a glTF file. You can see in the PG code that there are three meshes, each with one morph target. In this example, I am animating the morph target influences with a Babylon.js animation, but you can just as easily use your locator position to drive that influence. Hope this helps, but let me know if you have questions.

3 Likes

Thanks for helping on this, @PatrickRyan.

It’s now working as expected.

Nevertheless, we have one more question in order to solve all this stuff with morphtargets in our project, this time related to shaders, but that’s the subject of this other thread, so we’re in it.

Could you please take a look at it?

Thanks for your time.

1 Like