I’m using a method to correct the position of the character’s physical assets. It involves creating a version of the SKM_Manny_Invis mesh with the position and rotation of Paragom character’s bones. I can directly import this invisible mesh into the Hero’s PawnCosmeticsComponent, which will fix the positions in Manny’s Physics Assets. If the character’s body is larger, it will be necessary to create a new Physics Asset to correct the scale.
The idea behind this method is to avoid adding extra bones to Manny’s skeleton and focus solely on correcting the position.
To perform this procedure, you need to follow the following steps in Blender and create 3 collections:
SK_Pose Collection: This collection contains the Paragom’s Armatures, and their names should match the ones used in the export. Make sure to set the SK_Pose collection to remain invisible, as its visibility can cause errors.
SK_Base Collection: In this collection, you will find the original SK_Manny. It is important to note that the bones of the SK_Edit Armature will have their parents cleared and will be reparented in each cycle. The SK_Base collection serves as a parameter for this information, meaning that if its hierarchy is modified, it will affect everything else. Just like SK_Pose, the SK_Base collection should also be set to remain invisible, as its visibility can cause errors.
SK_Edit Collection: This collection contains a copy of SK_Manny_Invis. This copy will have its bones repositioned and will be exported. It is important to keep the name of this copy as ‘root’, and it should have the Mesh and Armature visible as they need to be selected.
During the import of the skeletons, make sure to select the ‘ignore leaf bones’ option.
from bpy import data, ops, context
from mathutils import Matrix
class Init():
def __init__(self):
self.ExecuteToOneFileInPose(init=False)
self.ExecuteToAllFileInPose(init=True)
pass
def ExecuteToOneFileInPose(self,init=False):
SK_Name = "root_Shinbi"
Set_Path = "D:\\Documents\\ExportUnrealMesh\\MeshsInvis\\"
if init:
Get().GetArmature(Name=SK_Name)
Execute().CleanParentsBones()
Execute().RepositionBones()
Execute().SetParentBones()
Execute().Export_FBX(Name=SK_Name, Path=Set_Path)
pass
pass
def ExecuteToAllFileInPose(self, init=False):
global SK_Pose_List, SK_Pose
Set_Path = "D:\\Documents\\ExportUnrealMesh\\MeshsInvis\\"
if init:
Get().GetPoseList()
for SK in SK_Pose_List:
SK_Pose = SK.pose.bones
Execute().CleanParentsBones()
Execute().RepositionBones()
Execute().SetParentBones()
Execute().Export_FBX(Name=SK.name, Path=Set_Path)
pass
pass
pass
class Get():
def GetArmature(self, Name = 'ArmatureName'):
global SK_Pose, SK_Manny, SK_Edit, SK_Edit_Pose
SK_Pose = data.objects[Name].pose.bones
SK_Manny = data.objects['root_Base'].pose.bones
SK_Edit = data.objects['root']
SK_Edit_Pose = data.objects['root'].pose.bones
pass
def GetPoseList(self):
global SK_Pose_List, SK_Manny, SK_Edit, SK_Edit_Pose
Coll = data.collections['SK_Pose'].objects
SK_Pose_List = [obj for obj in Coll if obj.type =="ARMATURE"]
SK_Manny = data.objects['root_Base'].pose.bones
SK_Edit = data.objects['root']
SK_Edit_Pose = data.objects['root'].pose.bones
pass
pass
class Execute():
def CleanParentsBones(self):
global SK_Pose, SK_Manny, SK_Edit, SK_Edit_Pose
context.view_layer.objects.active = SK_Edit
ops.object.mode_set(mode='OBJECT')
ops.object.select_all(action='DESELECT')
SK_Edit.select_set(True)
ops.object.mode_set(mode='POSE')
ops.pose.select_all(action='DESELECT')
for bone_A in SK_Manny:
if bone_A.name in SK_Pose:
bone_B = SK_Edit_Pose[bone_A.name]
bone_B.bone.select = True
pass
else:
bone_B = SK_Edit_Pose[bone_A.name]
bone_B.bone.select = False
pass
pass
ops.object.mode_set(mode='EDIT')
ops.armature.parent_clear(type='CLEAR')
ops.object.mode_set(mode='OBJECT')
pass
def RepositionBones(self):
global SK_Pose, SK_Manny, SK_Edit_Pose
ops.object.mode_set(mode='POSE')
ops.pose.select_all(action='DESELECT')
for bone in SK_Manny:
if bone.name in SK_Pose:
bone_edit = SK_Edit_Pose[bone.name]
bone_edit.matrix = SK_Pose[bone.name].matrix.copy()
bone_edit.bone.select = True
pass
pass
ops.pose.armature_apply(selected=False)
pass
def SetParentBones(self):
global SK_Manny, SK_Edit
ops.object.mode_set(mode='OBJECT')
ops.object.select_all(action='DESELECT')
SK_Edit.select_set(True)
context.view_layer.objects.active = SK_Edit
ops.object.mode_set(mode='EDIT')
ops.armature.select_all(action='SELECT')
ops.armature.parent_clear(type='CLEAR')
ops.armature.select_all(action='DESELECT')
for bone in SK_Manny:
parent_bone = SK_Edit.data.edit_bones[bone.name]
for child in bone.children:
child_bone = SK_Edit.data.edit_bones[child.name]
child_bone.parent = parent_bone
pass
pass
ops.object.mode_set(mode='OBJECT')
pass
def Export_FBX(self, Name='ArmatureName', Path="None" ):
file = f"{Path}{Name}.fbx"
ops.object.mode_set(mode='OBJECT')
ops.object.select_all(action='SELECT')
ops.export_scene.fbx(filepath=file,
check_existing=True,
filter_glob='*.fbx',
use_selection=True,
use_active_collection=False,
global_scale=1.0,
apply_unit_scale=True,
apply_scale_options='FBX_SCALE_NONE',
use_space_transform=True,
bake_space_transform=False,
object_types={'MESH', 'ARMATURE'},
use_mesh_modifiers=True,
use_mesh_modifiers_render=True,
mesh_smooth_type='EDGE',
use_subsurf=False,
use_mesh_edges=False,
use_tspace=False,
use_custom_props=True,
add_leaf_bones=False,
primary_bone_axis='Y',
secondary_bone_axis='X',
use_armature_deform_only=False,
armature_nodetype='NULL',
bake_anim=True,
bake_anim_use_all_bones=True,
bake_anim_use_nla_strips=True,
bake_anim_use_all_actions=True,
bake_anim_force_startend_keying=True,
bake_anim_step=1.0,
bake_anim_simplify_factor=1.0,
path_mode='AUTO',
embed_textures=False,
batch_mode='OFF',
use_batch_own_dir=True,
use_metadata=True,
axis_forward='X',
axis_up='Z')
pass
pass
pass
Init()
If all the settings are correct, the Python script will work properly, and you will achieve a result similar to this.