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!
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.
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.
@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!
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.
@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.
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.
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.
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.