4

So I have one FBX that have it's own armature and one object. Let's call it T pose. I have second FBX that contains one armature with let's say walk animation.

I am trying to assign walk animation to object with t-pose with no success. What have I tried so far:

  • Adding just walk armature to object won't work. I mean I can add the armature, but walk armature's rest pose is different than t-pose's rest pose. If I just change armature, animation is awkward.

  • I wrote one simple Python script that copy rest pose of walk animation and paste it to rest pose of t-pose. That didn't helped.

  • tried Copying FBX animation to armature

Am I right that deformations in animations are just offsets from rest pose?

I see others had same problems like: Merge multiple fbx animations into single rig or Import fbx animation

Is there any solution for this? Any comment is appreciated.

Izzy
  • 243
  • 3
  • 13

1 Answers1

3

The solution is rather simple.

You need to add Bone Constraint -> Copy Transforms to armature without animation. As Target add armature with animation, and as a Bone, bone with the same name. Rinse and repeat for all bones.

Here is the code that automates all:

  • Creates constraints and link bones with other armature
  • Iterate over all frames, apply transform, and insert keyframes for all bones
  • Delete constrains

Just fill source and target armature, and animation from and to. Moreover, you can just call add_constraints() function which will link armatures and bones.

import bpy

source = "Bip001" target = "Bip001.001" frame_from = 0 frame_to = 35

add constrains and link them with target bones

def add_constraints(): global source, target

source_ob = bpy.data.objects[source]

for bone in source_ob.pose.bones:
    sel_bone=source_ob.data.bones[bone.name]
    sel_bone.select=True
    bpy.context.object.data.bones.active=sel_bone

    trans_bone = bpy.context.object.pose.bones[bone.name]
    if trans_bone.constraints.find('Copy Transforms') == -1:
        if bpy.data.objects[target].pose.bones.get(bone.name) is not None:
            bpy.ops.pose.constraint_add(type='COPY_TRANSFORMS')
            trans_bone.constraints["Copy Transforms"].target = bpy.data.objects[target]
            trans_bone.constraints["Copy Transforms"].subtarget = bone.name

delete previously created constrains

def del_constraints( ): for bone in bpy.context.selected_pose_bones: copyLocConstraints = [ c for c in bone.constraints if c.type == 'COPY_TRANSFORMS' ] # Iterate over all the bone's copy location constraints and delete them all for c in copyLocConstraints: bone.constraints.remove( c ) # Remove constraint

def apply_animation(): global source, target, frame_from, frame_to #set keying set to whole character #we have all bones selected scene = bpy.context.scene for frame in range(frame_from, frame_to ): scene.frame_current = frame scene.frame_set(scene.frame_current) # apply visual transfrom to pose Ctrl+A bpy.ops.pose.visual_transform_apply() # insert all keyframes -> press I bpy.ops.anim.keyframe_insert_menu(type='ACTIVE', confirm_success=True)

def select_all_bones(): global source, target ob = bpy.data.objects[source] for bone in ob.pose.bones: b=ob.data.bones[bone.name] b.select=True bpy.context.object.data.bones.active=b

ks = bpy.data.scenes["Scene"].keying_sets_all ks.active = ks['Whole Character']

ob = bpy.data.objects[source]

uncomment line below if you are using Blender version below 2.8 and comment out 2nd line

#bpy.context.scene.objects.active = ob bpy.context.view_layer.objects.active = ob bpy.ops.object.mode_set(mode='POSE')
#select_all_bones() add_constraints() apply_animation() del_constraints()

Izzy
  • 243
  • 3
  • 13
  • 2
    Thank you for that plugin code Izzy. I just ran it on Blender 2.8.8lts and it works great, with one caveat. The source and target in the code are reversed, it seems. I had to enter my actual source as Target, and my actual target as Source in your code. Thanks again for filling in one of the multiple gaps that the Blender Foundation allows to remain in their software. – nmc Nov 04 '20 at 20:17
  • You are welcome @nmc. If I get some free time, I may create an addon from it. – Izzy Nov 05 '20 at 11:15
  • Thank you for the code, it works great in the GUI. However, it fails when called from the command line (headless mode). The problem is with the call to bpy.ops.anim.keyframe_insert_menu, which throws a "context is incorrect" error. I believe it's because the call refers to a menu, which is not available in a non-GUI context. Is there any other call that would accomplish the same thing? – Rafael Jan 20 '21 at 19:25
  • @Rafael Idk, maybe in that case you are in object mode. Did you try to switch to edit mode? – Izzy Jan 20 '21 at 20:23
  • 1
    @Izzy, I solved the problem by replacing your call to keyframe_insert_menu with a block that calls bone.keyframe_insert for each bone. – Rafael Jan 20 '21 at 20:29