I'm trying to modify Blender to add something I call (informally) "bone inertia". It's a bit like having a soft body attached to a parent bone that's used to drive the child bone. The difference is that I really want to use my own "physics" instead of going through Blender's Soft Body physics.
So far, I've been trying to achieve this using Python. I've got a script that does the following:
- Adds a few properties to bpy.types.PoseBone. I call them "Strength", "Inertia" and "Limit". I animate those properties so that I can turn the behavior on, off or tweak it in the middle of an animation.
- Adds a panel to the bone property tab that exposes those properties (pose mode only).
- Registers a callback to
bpy.app.handlers.frame_change_post
.
In the callback, I check to see if an animation is playing (bpy.context.window.screen.is_animation_playing
). If it is, I scan all objects in the scene and apply my own "bone inertia physics" to every armature found. For each bone, I keep track of some state from the previous frame, eg. the velocity of the bone's tail and some other things. Ultimately, I overwrite each bone's location/orientation by setting the PoseBone's matrix
attribute. If no animation is playing, I don't modify the bones at all. That way, the user can pose the armature as they normally would, but as soon as they hit Play, they see the "bone inertia" applied to what they've done. This workflow is convenient enough for me.
I finally got the bones behaving exactly the way I want, but there's one problem: the mesh doesn't follow the bone locations set in the frame_change_post
callback. I realized that it's because the callback is invoked after the mesh's Armature modifier is already applied.
I'm a C++ programmer so I went ahead and starting hacking on the Blender source code. I added a new Python callback, "armature_update_post", that gets called at the end of BKE_pose_where_is
(right after the armature gets updated). This didn't work very well, probably because BKE_pose_where_is
is called in a background thread, causing timing issues. (For example, in the Python callback, bpy.context.window
was randomly set to None. I didn't investigate it too deeply.)
Now I'm thinking of re-implementing my "bone inertia" physics in C. To do it, I'll have to add new properties to bPoseChannel
, including the state I need to preserve between frames (velocity, etc.), and add some code to BKE_pose_where_is_bone
. I'm not sure what issues I'll encounter using this approach yet. For example, I'm not sure if I can reliably determine whether the animation is playing or not at this point, because it will still be called in a background thread.
Can anyone give me some feedback on what I'm trying to do? Is there a better way to achieve what I want?
Thanks!