Mixamo Animation Retargeting

Just a quick post here for other people who would like to use this fantastic quick fix and remove root motion like I did. Below is the source code to a Python script for Blender to add the root bone to the FBX from Mixamo and remove the X/Z motion from the Hips bone. This will remove the root motion and make it easier for coders to work with especially over networked apps. This is unsupported as work 12+hrs a day and try to do this stuff at night =)

Usage: blender.exe --background --python “fixMixamo.py” – “C:\Animations\standing_aim_overdraw.fbx” (Obviously change out the folder/file)

Notes:
This will not remove the Cube in the scene so I suggest you remove all the items in the scene and save it as your Blender start up
This script will export the FBX file based on your settings so make sure to save your Operator Presets in the FBX Export to Forward: -Z Forward and Up: Y Up.
Finally the export will place the file in a path \Fixed inside the path the original FBX file was in EG: “C:\Animations\standing_aim_overdraw.fbx” ends up in “C:\Animations\Fixed\standing_aim_overdraw.fbx” –> Make sure to create the \Fixed folder, I was too lazy to do that in the script!



import sys
import os
import bpy

argv = sys.argv
argv = argv[argv.index("--") + 1:]  # get all args after "--"

bpy.ops.import_scene.fbx(filepath = argv[0])

#Select Armature
bpy.ops.object.select_all(action='DESELECT')
bpy.context.scene.objects.active = bpy.data.objects"Armature"]
bpy.data.objects"Armature"].select = True

#Armature -> Mocap tools -> Fix BVH Axis Orientation
bpy.ops.mocap.rotate_fix()

#Set X rotation to 0
import mathutils
eul = mathutils.Euler((0,0,0), "XYZ")
bpy.data.objects"Armature"].rotation_euler = eul

#Edit Mode 				
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.armature.select_all()

#Add Bone at 0, 0, 0
bpy.context.scene.cursor_location = (0.0, 0.0, 0.0)
arm = bpy.data.armatures"Armature"]
#Rename bone = root
bone = arm.edit_bones.new("root")
bone.head = (0,0,0)
bone.tail = (0,0,1)
#Select Hips + Set Parent
arm.edit_bones"Hips"].parent = arm.edit_bones"root"]
bpy.ops.object.mode_set(mode='OBJECT')


#Pose Mode
bpy.ops.object.mode_set(mode='POSE')
#Set root.keyframe(0) to 0,0,0
bpy.data.objects"Armature"].data.bones.active = bpy.data.objects"Armature"].pose.bones'root'].bone
bpy.context.active_pose_bone.keyframe_insert(data_path="location", frame=0)

#Set the active pose bone to Hips
bpy.data.objects"Armature"].data.bones.active = bpy.data.objects"Armature"].pose.bones'Hips'].bone
bone = bpy.context.active_pose_bone
name = bone.name
typ = "location"
maxLength = 0
#Get the fcurve for Hips.location
fcurves = [fc for fc in bpy.data.actions[0].fcurves if fc.data_path.startswith('pose.bones"%s"].%s' % (name, typ))]
#Set the maxLength to the last frame with data in it
for fc in fcurves: 
	for kfp in fc.keyframe_points: 
		if kfp.co[0] > maxLength: maxLength = kfp.co[0]

#Get the frames from the fcurve
for fc in fcurves: frames = [kfp.co[0] for kfp in fc.keyframe_points if kfp.co[0] >= 0 and kfp.co[0] <= maxLength]

#Remove all keyframes of Hips.location.x
for f in frames: success = bone.keyframe_delete(data_path=typ, index=0, frame=f)

#Remove all keyframes of Hips.location.z
for f in frames: success = bone.keyframe_delete(data_path=typ, index=2, frame=f)

#Set the animation timers based on the imported FBX
bpy.context.scene.frame_set(0)
bpy.context.scene.frame_start = 0
bpy.context.scene.frame_end = maxLength
bpy.ops.object.mode_set(mode='OBJECT')

lastSlash = argv[0].rindex('\\')
newFile = argv[0][0:lastSlash] + '\\Fixed'+argv[0][lastSlash:len(argv[0])]
bpy.ops.export_scene.fbx(filepath=newFile)