Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Applies parent inverse matrix to the child's local matrix.
import bpy
import mathutils
class Converter:
def __init__(self, ob, logging=False):
self.ob = ob
self.action = ob.animation_data.action
self.rot_mode = ob.rotation_mode
self.logging = logging
if self.logging:
print("\n"*2 + "-"*80)
print("Converting", ob.name)
# apply parent inverse to rest pose
# BUG - assignment to matrix_basis does not work correctly for AXIS_ANGLE
self.parent_inv = ob.matrix_parent_inverse.copy()
if self.rot_mode == "AXIS_ANGLE":
mat = self.build_matrix(ob.location, ob.rotation_axis_angle, ob.scale)
mat = ob.matrix_parent_inverse * mat
loc, rot, scale = mat.decompose()
rot = rot.to_axis_angle()
rot = (rot[1], rot[0][0], rot[0][1], rot[0][2])
ob.location = loc
ob.rotation_axis_angle = rot
ob.scale = scale
else:
ob.matrix_basis = ob.matrix_parent_inverse * ob.matrix_basis
ob.matrix_parent_inverse.identity()
self.vec3_fs = "{: 8.4f} " * 3
self.vec4_fs = "{: 8.4f} " * 4
# gather fcurve channels
self.location = [None] * 3
self.rotation = [None] * (3 if self.rot_mode not in ("QUATERNION", "AXIS_ANGLE") else 4)
self.scale = [None] * 3
self.frames = set()
prop_rot = ("rotation_quaternion" if self.rot_mode == "QUATERNION" else
"rotation_axis_angle" if self.rot_mode == "AXIS_ANGLE" else
"rotation_euler")
prop_to_attr = (
("location", self.location),
( prop_rot, self.rotation),
( "scale", self.scale)
)
for fcu in self.action.fcurves:
for prop, attr in prop_to_attr:
if fcu.data_path == prop:
attr[fcu.array_index] = fcu
for prop, attr in prop_to_attr:
for index, item in enumerate(attr):
# use constant float if no fcurve is present or fcurve is empty
if item is None or len(item.keyframe_points) == 0:
attr[index] = getattr(ob, prop)[index]
# timeline
else:
for kfp in item.keyframe_points:
self.frames.add(kfp.co.x)
def eval_prop(self, prop, frame):
return tuple(elem.evaluate(frame) if isinstance(elem, bpy.types.FCurve) else
elem for elem in prop)
def build_matrix(self, loc, rot, scale):
rot = (mathutils.Quaternion(rot) if self.rot_mode == "QUATERNION" else
mathutils.Quaternion(rot[1:4], rot[0]) if self.rot_mode == "AXIS_ANGLE" else
mathutils.Euler(rot, self.rot_mode))
mat_loc = mathutils.Matrix.Translation(loc)
mat_rot = rot.to_matrix().to_4x4()
mat_sca = mathutils.Matrix.Identity(4)
for i in range(3):
mat_sca[i][i] = scale[i]
return mat_loc * mat_rot * mat_sca
def insert_keyframe(self, prop, frame, value):
for index, elem in enumerate(prop):
if isinstance(elem, bpy.types.FCurve):
elem.keyframe_points.insert(frame, value[index], {'REPLACE', 'FAST'})
def convert(self):
for frame in self.frames:
loc = self.eval_prop(self.location, frame)
rot = self.eval_prop(self.rotation, frame)
scale = self.eval_prop(self.scale, frame)
if self.logging:
rot_fs = self.vec3_fs if self.rot_mode not in ("QUATERNION", "AXIS_ANGLE") else self.vec4_fs
print("frame %03d" % frame)
print("location before:" + self.vec3_fs.format(*loc))
print("rotation before:" + rot_fs.format(*rot))
print("scale before:" + self.vec3_fs.format(*scale))
mat_basis = self.build_matrix(loc, rot, scale)
mat_basis = self.parent_inv * mat_basis
loc, rot, scale = mat_basis.decompose()
if self.rot_mode == "AXIS_ANGLE":
rot = rot.to_axis_angle()
rot = (rot[1], rot[0][0], rot[0][1], rot[0][2])
elif not self.rot_mode == "QUATERNION":
rot = rot.to_euler(self.rot_mode)
if self.logging:
print("location after :" + self.vec3_fs.format(*loc))
print("rotation after :" + rot_fs.format(*rot))
print("scale after :" + self.vec3_fs.format(*scale))
print("-"*40)
self.insert_keyframe(self.location, frame, loc)
self.insert_keyframe(self.rotation, frame, rot)
self.insert_keyframe(self.scale, frame, scale)
for ob in bpy.context.scene.objects:
if ob.parent and ob.animation_data:
converter = Converter(ob, logging=True)
converter.convert()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.