Skip to content

Commit 1d52418

Browse files
author
Roberto De Ioris
authored
Update FixingMixamoRootMotionWithPython.md
1 parent 4adcc0a commit 1d52418

1 file changed

Lines changed: 82 additions & 2 deletions

File tree

‎tutorials/FixingMixamoRootMotionWithPython.md‎

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,92 @@ The reason is in how the character is rigged:
3636

3737
![default_skeleton](https://github.com/20tab/UnrealEnginePython/raw/master/tutorials/FixingMixamoRootMotionWithPython_Assets/default_skeleton.png)
3838

39-
as you can see the root of the skeleton tree is the 'Hips' joint/bone. Animations use this bone for both translations and rotations, breaking the way UE4 works. Unreal Engine expects the root of the skeleton to be at the local origin and all of the bones/joints weighted to vertices come below it.
39+
as you can see the root of the skeleton tree is the 'Hips' joint/bone. Animations use this bone for both translations and rotations, breaking the way UE4 works. Unreal Engine expects the root of the skeleton to be at the local origin and all of the other bones/joints (weighted to vertices) below it.
4040

41-
So our first ste will be building a new skeleton with this specific configuration.
41+
So our first step will be building a new skeleton with this specific configuration.
4242

4343
## Step 2: generating a new skeleton
4444

45+
Open your favourite python editor (or the embedded one) and create the /Game/Scripts/mixamo.py file:
46+
47+
```python
48+
import unreal_engine as ue
49+
from unreal_engine.classes import SkeletalMesh, Skeleton
50+
from unreal_engine import FTransform
51+
52+
53+
class DialogException(Exception):
54+
"""
55+
Handy exception class for spawning a message dialog on error
56+
"""
57+
def __init__(self, message):
58+
ue.message_dialog_open(0, message)
59+
60+
class RootMotionFixer:
61+
62+
def add_root_to_skeleton(self, mesh, bone='root'):
63+
# retrieve the mesh path, so we can give the user a handy default for the file picker
64+
base_path = mesh.get_path_name()
65+
# open the save asset dialog with handy defaults
66+
new_path = ue.create_modal_save_asset_dialog('Choose destination path', ue.get_path(base_path), ue.get_base_filename(base_path) + '_rooted')
67+
if not new_path:
68+
raise DialogException('Please specify a new path for the Skeletal Mesh copy')
69+
70+
# build package and object names for the mesh copy
71+
package_name = ue.object_path_to_package_name(new_path)
72+
object_name = ue.get_base_filename(new_path)
73+
# the last True allows for overwrites
74+
new_mesh = mesh.duplicate(package_name, object_name, True)
75+
76+
# generate a new skeleton
77+
new_skel = self.build_new_skeleton(mesh.Skeleton, object_name + '_Skeleton', bone)
78+
# save the new skeleton in the same package directory of the new skeletal mesh
79+
new_skel.save_package(package_name)
80+
81+
# assign the new skeleton to the new mesh
82+
new_mesh.skeletal_mesh_set_skeleton(new_skel)
83+
84+
new_skel.save_package()
85+
86+
def build_new_skeleton(self, skeleton, name, root):
87+
"""
88+
Generate a new skeleton with a 'root' bone
89+
"""
90+
new_skel = Skeleton(name)
91+
# add the root bone with -1 as parent
92+
new_skel.skeleton_add_bone(root, -1, FTransform())
93+
94+
# copy bone indices from the original skeleton
95+
for index in range(0, skeleton.skeleton_bones_get_num()):
96+
# get bone name, parent index and ref pose
97+
bone_name = skeleton.skeleton_get_bone_name(index)
98+
bone_parent = skeleton.skeleton_get_parent_index(index)
99+
bone_transform = skeleton.skeleton_get_ref_bone_pose(index)
100+
101+
# if this is the old 'root' bone (Hips), change its parent to the new 'root'
102+
if bone_parent == -1:
103+
bone_parent_name = root
104+
else:
105+
bone_parent_name = skeleton.skeleton_get_bone_name(bone_parent)
106+
107+
# get new bone parent id
108+
new_bone_parent = new_skel.skeleton_find_bone_index(bone_parent_name)
109+
# add the new bone
110+
new_skel.skeleton_add_bone(bone_name, new_bone_parent, bone_transform)
111+
return new_skel
112+
113+
114+
root_motion_fixer = RootMotionFixer()
115+
116+
# get the list of currently selected assets in the content browser
117+
for uobject in ue.get_selected_assets():
118+
# check if uobject is a SkeletlMesh
119+
if uobject.is_a(SkeletalMesh):
120+
root_motion_fixer.add_root_to_skeleton(uobject)
121+
else:
122+
raise DialogException('Only Skeletal Meshes are supported')
123+
```
124+
45125
## Step 3: fixing skeletal mesh bone influences
46126

47127
## Step 4: splitting 'Hips' track in animation

0 commit comments

Comments
 (0)