Welcome to Part 2 of my Blender -> WebGL hot reloading experiment. In part 1 we explored hot reloading wavefront OBJ models. This time around we gave skinned models with skeletal animations a try.
Hot reloading skeletal animation data from Blender to WebGL
I’ll be talking about the birds eye view details, but you can find the full source on GitHub. Feel free to fork and re-purpose the source code for your own implementation.
In Part 1 all we needed to export from Blender was an OBJ file. This functionality is built into Blender, so we just had to trigger an export whenever our Blender file was saved. We then parsed the OBJ data, sent it to the frontend, and boom - finished.
Handling skinned models was a bit trickier - let’s take a look at why.
Exporting Skinned Models
I use Blender’s COLLADA exporter which has thus far been rock solid for me. But there are a few drawbacks to it that left me a little stranded while starting on this experiment.
In Blender, animations are organized into groups of keyframes called “actions”
Blender has actions, which organize your animations into different named sets of animation keyframes. (Really actions are composed of fcurves, but it’s fine to ignore that detail unless you’re looking for extremely precise motions).
In your Blender file you might have dozens of actions, but when exporting to COLLADA using the native Blender exporter only one action can be exported.
This is an issue because you want to have access to all of your actions when you’re animating your model in your engine, not just whichever one you happen to have open.
In the past my solution was to manually copy all keyframes into one
combined action and then export that
combined action, but for automated hot reloading of new actions I needed a more automated process.
I started off trying to write a script to combine all actions into one
combined action so that by the time I exported the
combined action to COLLADA everything would be all together. This was essentially going to be an automated version of my old manual process.
I ran into some early troubles with this due to inexperience with the Blender API, and I’m really glad that I did.
Had I gone down that road I would’ve had a big collection of keyframes all under one action and no way to tell which keyframes belonged to which action without manually writing down the exact orders of the keyframes.
For example, I would have had to know that the
idle animation was keyframes number 0-11,
walk was 12-20 etc. Anytime I added new keyframes I would have needed to update this mapping.
After more experimentation I ended up leaning towards a solution of just exporting the keyframed bone pose matrices for all actions into a JSON file.
Then I could send that JSON down to the client, and the client would be able to reproduce all animations by accessing the bone pose matrices for any of the actions in the file.
We write all of our armature animation data to a JSON file
So after quite a bit of stumbling I made blender-actions-to-json a little Python script that exports all of an armature’s actions to a JSON file.
With actions out of the way, I needed to handle IKs (inverse kinematics).
Exporting IKs as FKs
I’ve written about converting IKs to FKs before while in Blender, but I now needed a way to do it from the command line.
After trying out a few approaches I settled on writing a script that would install my IK -> FK conversion addon and then run it. Installing an addon comes down to moving the addon file into Blender’s directory so this is fast.
The end process
The final steps were:
Start a Node.js process that watches for Blender file changes
Whenever a Blender File changes, spawn a Blender child process that converts the IK’d model into an FK model, and then exports both the FK armature actions into an action file and the model data COLLADA file.
Parsed and then send the action file and COLLADA file data to the client as JSON over an open websocket connection.
Client overwrites the vertex data GPU buffers with the new data that it received over websocket.
Hot reloading achieved!
Using data from blender-actions-to-json we overwrite the old animation
Future Performance Enhancements
In this experiment’s code we’re technically sending over some joint data twice since both the
actions.json file and the COLLADA file hold joint data.
We don’t use any of the joint data in the COLLADA file, so a quick performance enhancement might be to delete the COLLADA keyframes before sending the model to the client. This could be accomplished by adding a flag to
collada-dae-parser to not process or output any keyframes.
But yeah, that’s certainly for another day.
Thanks for reading! You should hit that little tweet button down there!
Cya next time,