-
-
Save p2or/0e3904ee7f01a7702e304c700ee82396 to your computer and use it in GitHub Desktop.
Applies parent inverse matrix to the child's local matrix #Blender
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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