Exporting animation from Blender 2.8+ to glTF

Hi everyone,

This post will attempt to explain how to export animation from the newer Blender versions (from 2.8 onward) to the glTF file format.

Two different use cases will be discussed:

  1. export multiple animations for a single armature to one and the same glTF file;

    • use a workaround for loading multiple Actors in Panda from one glTF file, so the above can be done for more than one Actor;
  2. individually export multiple animations for a single armature separately from the original mesh geometry – each to its own glTF file – and load all of these together in Panda for the same Actor.

1. Exporting multiple animations for a single armature to one and the same glTF file

Open a viewport for the Dope Sheet editor and change the editing context to Action Editor.
Choose an action from the drop-down list and click the “Push Down” button. This adds the action to a Track in the NLA (Nonlinear Animation) stack as a new animation strip. So make sure to also have a viewport open for the NLA editor.
Do the same for each action.

Now you should see multiple Tracks in the NLA editor, each containing a single strip that is named for the corresponding action.
The important thing here is to give each Track a unique name; this is the name by which you will be able to access the associated animation in Panda. The names of the strips or actions do not seem to matter.

You can now export the animation to glTF. The export option Animation -> Animation -> Group by NLA Track should be checked (the default).

In Panda, you can then create an Actor using this glTF file and access the stored animations like so:

my_actor = Actor(path_to_gltf)

anim_names = my_actor.get_anim_names()
# check the loaded animations
print(anim_names)

anim_name = anim_names[0]
# play the first listed animation
my_actor.play(anim_name)

Use a workaround for loading multiple Actors from one glTF file

Usually, only a single actor is created from a loaded file. Specifically, when passing the model filename into the Actor constructor to instantiate it, only the first Character node found will be used for the actor; other Character nodes will be ignored.

To work around this, code similar to the following can be used:

base = ShowBase()

actor_models = base.load_model(path_to_gltf)

actor_1 = Actor(actor_models.find("**/armature.001"), copy=False)
actor_2 = Actor(actor_models.find("**/armature.002"), copy=False)

# check the loaded animations
print(actor_1.get_anim_names())
print(actor_2.get_anim_names())

The code above is based on the sample found in this GitHub issue comment by @rdb.

2. Individually export multiple animations for a single armature

This is the opposite of case 1., as the intent here is to create a different glTF file for each animation.

Additionally, we don’t want to include the original mesh geometry with every animation, as this could lead to an enormous waste of memory and disk space if the model in question is quite complex.
Another reason to avoid inclusion of the model is to obviate the need for updating all of the animation files each time the model is changed.

We can start by exporting the armature – with the model mesh attached – to a glTF that we can use to instantiate the Actor with. No animation needs to be defined at this point.

Then an animation can be created, after which the model mesh can be (temporarily) detached from the armature, before exporting it to its own glTF file.
But loading a model file that contains only an armature and no geometry at all won’t work.
What we can do, however, is replace the original mesh with the simplest mesh possible: one that consists of just a single vertex. Once this is parented to the armature, the new animated hierarchy can be exported.

After instantiating the Actor with the unanimated model file, all of the required animations can be loaded at once using a call to Actor.load_anims:

my_actor = Actor(path_to_model_file)
my_actor.load_anims({
    "anim_1": path_to_anim_1_file,
    "anim_2": path_to_anim_2_file
})
my_actor.play("anim_2")

In this case, the names given to actions, strips or tracks in Blender don’t even matter, as they are assigned in the call to Actor.load_anims.


If any of the above is incorrect, please let me know, as I am still quite new to animating with Blender.

5 Likes