I was wondering how Blender does some of its internal animation work and how it propagates changes between bones of the same armature/bone hierarchy.
The problem is as follows:
There is an input human rig definition together with bone poses (bone head and quaternion) for animating a character, all in armature space.
I import the rig definition and create a character from that looks like:
The rig looks accordingly like this (rest pose):

When I then try to animate the pose bones I get very strange results when I toggle the object with the armature to pose mode initially, set all poses and then go back to object mode:
# animate the bones
bpy.context.view_layer.objects.active = skeleton_obj
bpy.ops.object.mode_set(mode="POSE")
bone_qis_RS = {} # rotation of each bone at frame i
bone_his_RS = {} # head of each bone at frame i
for frame in range(frame_count):
bpy.context.scene.frame_current = frame
# debug log output
if verbose:
name = skeleton_obj.name
m = f"Animating frame {frame} of skeleton {name}."
print(m, flush=True)
# for each bone bone: get start point & orientation
for bone_name in bone_names:
# read head
... skipped ...
hi_RS = Vector([x, y, z])
bone_his_RS[bone_name] = hi_RS
# read rotation
... skipped ...
bone_qis_RS[bone_name] = q_i_RS
for name in bone_names:
if name == "b_root":
continue
# get pose bone, zero pose & current pose
pose_bone = pose_bones[name]
bone_q_i_RS = bone_qis_RS[name]
bone_head_i_RS = bone_his_RS[name]
pose_bone.matrix = Matrix.LocRotScale(bone_head_i_RS, bone_q_i_RS, None)
pose_bone.keyframe_insert(data_path="rotation_quaternion", frame=frame)
pose_bone.keyframe_insert(data_path="location", frame=frame)
bpy.ops.object.mode_set(mode="OBJECT")
The result looks like:
Only the first pose, the rest pose, looks okay. Everything with frame > 0 is a mess.
However, when I toggle between pose and object mode after each bone update, I get the correct result:
# animate the bones
bone_qis_RS = {} # rotation of each bone at frame i
bone_his_RS = {} # head of each bone at frame i
for frame in range(frame_count):
bpy.context.scene.frame_current = frame
# debug log output
if verbose:
name = skeleton_obj.name
m = f"Animating frame {frame} of skeleton {name}."
print(m, flush=True)
# for each bone bone: get start point & orientation
for bone_name in bone_names:
# read head
... skipped ...
hi_RS = Vector([x, y, z])
bone_his_RS[bone_name] = hi_RS
# read rotation
... skipped ...
bone_qis_RS[bone_name] = q_i_RS
for name in bone_names:
if name == "b_root":
continue
# get pose bone, zero pose & current pose
pose_bone = pose_bones[name]
bone_q_i_RS = bone_qis_RS[name]
bone_head_i_RS = bone_his_RS[name]
bpy.context.view_layer.objects.active = skeleton_obj
bpy.ops.object.mode_set(mode="POSE")
pose_bone.matrix = Matrix.LocRotScale(bone_head_i_RS, bone_q_i_RS, None)
pose_bone.keyframe_insert(data_path="rotation_quaternion", frame=frame)
pose_bone.keyframe_insert(data_path="location", frame=frame)
bpy.ops.object.mode_set(mode="OBJECT")
Though, importing a motion is now considerably slower and takes quite some time. I was wondering what internal updates Blender does and how this can be done more efficiently, but without the mess shown above? Intuitively, importing a motion sequence should be very fast as all the bone poses are already computed and only need to be forwarded to Blender.
How are the bone coordinate systems propagated from parents to their children? Is there an update function or so I can directly call on the bone I changed? When is pose_bone.matrix updated by Blender due to changing the rotation quaternion of a parent?
Any insights into how the bones are updated internally and how to best set their poses are appreciated!
