Skip to content

Commit

Permalink
Fixed bone rotation on import
Browse files Browse the repository at this point in the history
  • Loading branch information
Psycrow101 committed Feb 19, 2024
1 parent 347085c commit 4ce9caa
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 28 deletions.
53 changes: 37 additions & 16 deletions io_scene_bfbb_anm/export_bfbb_anm.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import bpy
from dataclasses import dataclass
from mathutils import Quaternion, Vector
from . anm import Anm, AnmKeyframe


Expand All @@ -10,11 +12,24 @@ def missing_action(self, context):
self.layout.label(text='No action for active armature. Nothing to export')


def get_bone_locrot(bone):
mat = bone.matrix.copy()
if bone.parent:
mat = bone.parent.matrix.inverted_safe() @ mat
return mat.to_translation(), mat.to_quaternion()
@dataclass
class PoseBoneTransform:
pos: Vector
rot: Quaternion

def calc_kf(self, mat):
kf_pos = self.pos + mat.to_translation()
kf_rot = mat.inverted_safe().to_quaternion().rotation_difference(self.rot)
return kf_pos, kf_rot


def get_bone_transform(pose_bone):
pos = pose_bone.location.copy()
if pose_bone.rotation_mode == 'QUATERNION':
rot = pose_bone.rotation_quaternion.copy()
else:
rot = pose_bone.rotation_euler.to_quaternion()
return PoseBoneTransform(pos, rot)


def get_action_range(arm_obj, act):
Expand Down Expand Up @@ -44,7 +59,7 @@ def create_anm(context, arm_obj, act, fps, flags):

old_frame = context.scene.frame_current
frame_start, frame_end = get_action_range(arm_obj, act)
bone_locrots = {}
bone_transforms = {}

if frame_start is None:
return None
Expand All @@ -56,24 +71,30 @@ def create_anm(context, arm_obj, act, fps, flags):
context.scene.frame_set(frame)
context.view_layer.update()

for bone_id, bone in enumerate(arm_obj.pose.bones):
loc, rot = get_bone_locrot(bone)
for bone_id, pose_bone in enumerate(arm_obj.pose.bones):
if frame == frame_start:
bone_locrots[bone_id] = [(loc, rot)]
bone_transforms[bone_id] = [get_bone_transform(pose_bone)]
else:
bone_locrots[bone_id].append((loc, rot))
bone_transforms[bone_id].append(get_bone_transform(pose_bone))

offsets.append([])
times.append((frame - frame_start) / fps)

times.append((frame_end - frame_start + 1) / fps)

for bone_id, trans in sorted(bone_locrots.items()):
last_locrot = None
for time_id, (loc, rot) in enumerate(trans):
if last_locrot is None or loc != last_locrot[0] or rot != last_locrot[1]:
keyframes.append(AnmKeyframe(time_id, loc, rot))
last_locrot = (loc, rot)
for bone_id, transforms in sorted(bone_transforms.items()):
bone = arm_obj.data.bones[bone_id]

loc_mat = bone.matrix_local.copy()
if bone.parent:
loc_mat = bone.parent.matrix_local.inverted_safe() @ loc_mat

last_trans = None
for time_id, trans in enumerate(transforms):
if last_trans is None or trans != last_trans:
kf_pos, kf_rot = trans.calc_kf(loc_mat)
keyframes.append(AnmKeyframe(time_id, kf_pos, kf_rot))
last_trans = trans
offsets[time_id].append(len(keyframes) - 1)

context.scene.frame_set(old_frame)
Expand Down
44 changes: 32 additions & 12 deletions io_scene_bfbb_anm/import_bfbb_anm.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,12 @@ def set_keyframe(curves, frame, values):
def create_action(arm_obj, anm, fps):
act = bpy.data.actions.new('action')
curves_loc, curves_rot = [], []
loc_mats, prev_rots = {}, {}

for bone in arm_obj.pose.bones:
g = act.groups.new(name=bone.name)
cl = [act.fcurves.new(data_path=(POSEDATA_PREFIX % bone.name) + 'location', index=i) for i in range(3)]
cr = [act.fcurves.new(data_path=(POSEDATA_PREFIX % bone.name) + 'rotation_quaternion', index=i) for i in range(4)]
for pose_bone in arm_obj.pose.bones:
g = act.groups.new(name=pose_bone.name)
cl = [act.fcurves.new(data_path=(POSEDATA_PREFIX % pose_bone.name) + 'location', index=i) for i in range(3)]
cr = [act.fcurves.new(data_path=(POSEDATA_PREFIX % pose_bone.name) + 'rotation_quaternion', index=i) for i in range(4)]

for c in cl:
c.group = g
Expand All @@ -42,7 +43,16 @@ def create_action(arm_obj, anm, fps):

curves_loc.append(cl)
curves_rot.append(cr)
bone.rotation_mode = 'QUATERNION'
pose_bone.rotation_mode = 'QUATERNION'
pose_bone.location = (0, 0, 0)
pose_bone.rotation_quaternion = (1, 0, 0, 0)

bone = arm_obj.data.bones.get(pose_bone.name)
loc_mat = bone.matrix_local.copy()
if bone.parent:
loc_mat = bone.parent.matrix_local.inverted_safe() @ loc_mat
loc_mats[pose_bone] = loc_mat
prev_rots[pose_bone] = None

set_kfs = []

Expand All @@ -51,7 +61,7 @@ def create_action(arm_obj, anm, fps):
arm_bones_num = anm_bones_num

for off in anm.offsets:
for bone_id, bone in enumerate(arm_obj.pose.bones[:arm_bones_num]):
for bone_id, pose_bone in enumerate(arm_obj.pose.bones[:arm_bones_num]):
kf_id = off[bone_id]

if kf_id in set_kfs:
Expand All @@ -61,12 +71,22 @@ def create_action(arm_obj, anm, fps):
kf = anm.keyframes[kf_id]
time = anm.times[kf.time_id]

mat = Matrix.Translation(kf.loc) @ kf.rot.to_matrix().to_4x4()
if bone.parent:
mat = bone.parent.matrix @ mat
bone.matrix = mat
set_keyframe(curves_loc[bone_id], time * fps, bone.location)
set_keyframe(curves_rot[bone_id], time * fps, bone.rotation_quaternion)
loc_mat = loc_mats[pose_bone]
loc_pos = loc_mat.to_translation()
loc_rot = loc_mat.to_quaternion()

rot = loc_rot.rotation_difference(kf.rot)

prev_rot = prev_rots[pose_bone]
if prev_rot:
alt_rot = rot.copy()
alt_rot.negate()
if rot.rotation_difference(prev_rot).angle > alt_rot.rotation_difference(prev_rot).angle:
rot = alt_rot
prev_rots[pose_bone] = rot

set_keyframe(curves_loc[bone_id], time * fps, kf.loc - loc_pos)
set_keyframe(curves_rot[bone_id], time * fps, rot)

return act

Expand Down

0 comments on commit 4ce9caa

Please sign in to comment.