Skip to content

Commit bf7f4d6

Browse files
author
Roberto De Ioris
committed
added mixamo.py
1 parent 05b4ccf commit bf7f4d6

File tree

1 file changed

+153
-0
lines changed
  • tutorials/FixingMixamoRootMotionWithPython_Assets

1 file changed

+153
-0
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import unreal_engine as ue
2+
from unreal_engine.classes import SkeletalMesh, Skeleton, AnimSequence, AnimSequenceFactory
3+
from unreal_engine import FTransform, FRawAnimSequenceTrack, FQuat
4+
from unreal_engine import SWindow, SObjectPropertyEntryBox
5+
6+
class DialogException(Exception):
7+
def __init__(self, message):
8+
ue.message_dialog_open(0, message)
9+
10+
class RootMotionFixer:
11+
12+
13+
def add_root_to_skeleton(self, mesh, bone='root'):
14+
base_path = mesh.get_path_name()
15+
new_path = ue.create_modal_save_asset_dialog('Choose destination path', ue.get_path(base_path), ue.get_base_filename(base_path) + '_rooted')
16+
if not new_path:
17+
raise DialogException('Please specify a new path for the Skeletal Mesh copy')
18+
package_name = ue.object_path_to_package_name(new_path)
19+
object_name = ue.get_base_filename(new_path)
20+
# the last True allows for overwrites
21+
new_mesh = mesh.duplicate(package_name, object_name, True)
22+
23+
# generate a new skeleton
24+
new_skel = self.build_new_skeleton(mesh.Skeleton, object_name + '_Skeleton', bone)
25+
# save the new skeleton in the same package directory of the new skeletal mesh
26+
new_skel.save_package(package_name)
27+
28+
# assign the new skeleton to the new mesh
29+
new_mesh.skeletal_mesh_set_skeleton(new_skel)
30+
31+
new_skel.save_package()
32+
33+
self.fix_bones_influences(new_mesh, mesh.Skeleton)
34+
35+
new_mesh.save_package()
36+
37+
def build_new_skeleton(self, skeleton, name, root):
38+
new_skel = Skeleton(name)
39+
new_skel.skeleton_add_bone(root, -1, FTransform())
40+
for index in range(0, skeleton.skeleton_bones_get_num()):
41+
bone_name = skeleton.skeleton_get_bone_name(index)
42+
bone_parent = skeleton.skeleton_get_parent_index(index)
43+
bone_transform = skeleton.skeleton_get_ref_bone_pose(index)
44+
if bone_parent == -1:
45+
bone_parent_name = root
46+
else:
47+
bone_parent_name = skeleton.skeleton_get_bone_name(bone_parent)
48+
new_bone_parent = new_skel.skeleton_find_bone_index(bone_parent_name)
49+
new_skel.skeleton_add_bone(bone_name, new_bone_parent, bone_transform)
50+
return new_skel
51+
52+
def get_updated_bone_index(self, old_skeleton, new_skeleton, old_bone_map, new_bone_map, index):
53+
54+
# get the skeleton bone_id from the map
55+
true_bone_id = old_bone_map[index]
56+
57+
# get the bone name
58+
bone_name = old_skeleton.skeleton_get_bone_name(true_bone_id)
59+
60+
# get the new index
61+
new_bone_id = new_skeleton.skeleton_find_bone_index(bone_name)
62+
63+
# check if a new mapping is available
64+
if new_bone_id in new_bone_map:
65+
return new_bone_map.index(new_bone_id)
66+
67+
new_bone_map.append(new_bone_id)
68+
return len(new_bone_map)-1
69+
70+
def fix_bones_influences(self, mesh, old_skeleton):
71+
vertices = mesh.skeletal_mesh_get_soft_vertices()
72+
new_vertices = []
73+
old_bone_map = mesh.skeletal_mesh_get_bone_map()
74+
new_bone_map = []
75+
76+
for vertex in vertices:
77+
bone_ids = list(vertex.influence_bones)
78+
for index, bone_id in enumerate(bone_ids):
79+
if vertex.influence_weights[index] > 0:
80+
bone_ids[index] = self.get_updated_bone_index(old_skeleton, mesh.Skeleton, old_bone_map, new_bone_map, bone_id)
81+
vertex.influence_bones = bone_ids
82+
new_vertices.append(vertex)
83+
84+
# assign new vertices
85+
mesh.skeletal_mesh_set_soft_vertices(new_vertices)
86+
# add the new bone mapping
87+
mesh.skeletal_mesh_set_bone_map(new_bone_map)
88+
89+
# specify which bones are active and required (ensure root is added to required bones)
90+
mesh.skeletal_mesh_set_active_bone_indices(new_bone_map)
91+
if 0 not in new_bone_map:
92+
new_bone_map += [0]
93+
mesh.skeletal_mesh_set_required_bones(new_bone_map)
94+
95+
def set_skeleton(self, asset_data):
96+
self.choosen_skeleton = asset_data.get_asset()
97+
self.window.request_destroy()
98+
99+
def split_hips(self, animation, bone='Hips'):
100+
self.choosen_skeleton = None
101+
# first ask for which skeleton to use:
102+
self.window = SWindow(title='Choose your new Skeleton', modal=True, sizing_rule=1)(
103+
SObjectPropertyEntryBox(allowed_class=Skeleton, on_object_changed=self.set_skeleton)
104+
)
105+
self.window.add_modal()
106+
if not self.choosen_skeleton:
107+
raise DialogException('Please specify a Skeleton for retargeting')
108+
109+
factory = AnimSequenceFactory()
110+
factory.TargetSkeleton = self.choosen_skeleton
111+
112+
base_path = animation.get_path_name()
113+
package_name = ue.get_path(base_path)
114+
object_name = ue.get_base_filename(base_path)
115+
116+
new_anim = factory.factory_create_new(package_name + '/' + object_name + '_rooted')
117+
118+
new_anim.NumFrames = animation.NumFrames
119+
new_anim.SequenceLength = animation.SequenceLength
120+
121+
for index, name in enumerate(animation.AnimationTrackNames):
122+
data = animation.get_raw_animation_track(index)
123+
if name == bone:
124+
# extract root motion
125+
root_motion = [position - data.pos_keys[0] for position in data.pos_keys]
126+
127+
# remove root motion from original track
128+
data.pos_keys = [data.pos_keys[0]]
129+
new_anim.add_new_raw_track(name, data)
130+
131+
# create a new track (the root motion one)
132+
root_data = FRawAnimSequenceTrack()
133+
root_data.pos_keys = root_motion
134+
# ensure empty rotations !
135+
root_data.rot_keys = [FQuat()]
136+
137+
# add the track
138+
new_anim.add_new_raw_track('root', root_data)
139+
else:
140+
new_anim.add_new_raw_track(name, data)
141+
142+
new_anim.save_package()
143+
144+
145+
root_motion_fixer = RootMotionFixer()
146+
147+
for uobject in ue.get_selected_assets():
148+
if uobject.is_a(SkeletalMesh):
149+
root_motion_fixer.add_root_to_skeleton(uobject)
150+
elif uobject.is_a(AnimSequence):
151+
root_motion_fixer.split_hips(uobject)
152+
else:
153+
raise DialogException('Only Skeletal Meshes are supported')

0 commit comments

Comments
 (0)