Skip to content

Commit

Permalink
Custom root bone name setting (#87)
Browse files Browse the repository at this point in the history
* Custom root bone name setting
* Custom root bone name tests
  • Loading branch information
vaeryn-uk authored Sep 27, 2024
1 parent eb00c74 commit 812e564
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 3 deletions.
5 changes: 4 additions & 1 deletion src/addons/send2ue/core/io/fbx_b3.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,10 @@ def fbx_data_object_elements(root, ob_obj, scene_data):
obj_type = b"Camera"

if ob_obj.type == 'ARMATURE':
if bpy.context.scene.send2ue.export_object_name_as_root:
if bpy.context.scene.send2ue.export_custom_root_name:
# if the user has provided a custom name for a root bone, use this directly
ob_obj.name = bpy.context.scene.send2ue.export_custom_root_name
elif bpy.context.scene.send2ue.export_object_name_as_root:
# if the object is already named armature this forces the object name to root
if 'armature' == ob_obj.name.lower():
ob_obj.name = 'root'
Expand Down
5 changes: 4 additions & 1 deletion src/addons/send2ue/core/io/fbx_b4.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,10 @@ def fbx_data_object_elements(root, ob_obj, scene_data):
obj_type = b"Camera"

if ob_obj.type == 'ARMATURE':
if bpy.context.scene.send2ue.export_object_name_as_root:
if bpy.context.scene.send2ue.export_custom_root_name:
# if the user has provided a custom name for a root bone, use this directly
ob_obj.name = bpy.context.scene.send2ue.export_custom_root_name
elif bpy.context.scene.send2ue.export_object_name_as_root:
# if the object is already named armature this forces the object name to root
if 'armature' == ob_obj.name.lower():
ob_obj.name = 'root'
Expand Down
8 changes: 8 additions & 0 deletions src/addons/send2ue/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,14 @@ class Send2UeSceneProperties(property_class):
"the first bone in the armature hierarchy is used as the root bone in unreal."
)
)
export_custom_root_name: bpy.props.StringProperty(
name="Custom root bone name",
default="",
description=(
"If specified, this adds a root bone by this name in Unreal. This overrides the "
"\"Export object name as root bone\" setting."
)
)
export_custom_property_fcurves: bpy.props.BoolProperty(
name="Export custom property fcurves",
default=True,
Expand Down
1 change: 1 addition & 0 deletions src/addons/send2ue/ui/dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def draw_export_tab(self, layout):
properties = bpy.context.scene.send2ue
self.draw_property(properties, layout, 'use_object_origin')
self.draw_property(properties, layout, 'export_object_name_as_root')
self.draw_property(properties, layout, 'export_custom_root_name')

# animation settings box
self.draw_expanding_section(
Expand Down
4 changes: 4 additions & 0 deletions tests/test_send2ue_cubes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ def test_auto_stash_active_action_option(self):
def test_export_object_name_as_root_option(self):
pass

@unittest.skip
def test_custom_root_bone_name(self):
pass

@unittest.skip
def test_export_custom_property_fcurves_option(self):
pass
Expand Down
13 changes: 13 additions & 0 deletions tests/test_send2ue_mannequins.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,19 @@ def test_export_object_name_as_root_option(self):
'frames': [2, 6, 11]
}})

def test_custom_root_bone_name(self):
"""
Tests custom root bone name option.
"""
self.run_custom_root_bone_name_option_tests({
'SK_Mannequin_Female': {
'rig': 'female_root',
'animations': ['third_person_walk_01', 'third_person_run_01'],
'bones': ['spine_02', 'calf_l', 'lowerarm_r'],
'frames': [2, 6, 11],
'custom_name': 'my_test_root_bone',
}})

def test_export_custom_property_fcurves_option(self):
"""
Tests export custom property fcurves option.
Expand Down
35 changes: 34 additions & 1 deletion tests/utils/base_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ def assert_curve(self, animation_name, curve_name, exists=True):
else:
self.assertFalse(result, f'Curve "{curve_name}" exists on animation "{animation_name}" when it should not!')

def assert_animation_hierarchy(self, rig_name, animation_name, bone_name, include_object=True):
def assert_animation_hierarchy(self, rig_name, animation_name, bone_name, include_object=True, custom_root_name=None):
self.log(
f'Checking the bone hierarchy of "{animation_name}" to see if "{bone_name}" has the same path '
f'to the root bone...'
Expand All @@ -702,6 +702,9 @@ def assert_animation_hierarchy(self, rig_name, animation_name, bone_name, includ
unreal_bone_path = self.unreal.get_bone_path_to_root(asset_path, bone_name)
blender_bone_path = self.blender.get_bone_path_to_root(rig_name, bone_name, include_object)

if custom_root_name:
blender_bone_path.append(custom_root_name)

self.assertEqual(
collections.Counter(blender_bone_path),
collections.Counter(unreal_bone_path),
Expand Down Expand Up @@ -1084,6 +1087,29 @@ def run_export_object_name_as_root_option_tests(self, objects_and_animations):
for frame in frames:
self.assert_animation_translation(rig_name, animation_name, bone_name, frame)

def run_custom_root_bone_name_option_tests(self, objects_and_animations):
self.blender.set_addon_property('scene', 'send2ue', 'export_all_actions', True)
self.blender.set_addon_property('scene', 'send2ue', 'import_animations', True)
# This option should be ignored as we're setting a custom name below.
self.blender.set_addon_property('scene', 'send2ue', 'export_object_name_as_root', True)

for object_name, data in objects_and_animations.items():
rig_name = data.get('rig')
animation_names = data.get('animations')
bone_names = data.get('bones')
frames = data.get('frames')
custom_root_bone_name = data.get('custom_name')
self.blender.set_addon_property('scene', 'send2ue', 'export_custom_root_name', custom_root_bone_name)
self.move_to_collection([object_name, rig_name], 'Export')
self.send2ue_operation()
self.assert_mesh_import(object_name)
# check that the animations are as expected
for animation_name in animation_names:
for bone_name in bone_names:
self.assert_animation_hierarchy(rig_name, animation_name, bone_name, include_object=False, custom_root_name=custom_root_bone_name)
for frame in frames:
self.assert_animation_translation(rig_name, animation_name, bone_name, frame)

def run_export_custom_property_fcurves_option_tests(self, objects_and_animations):
self.blender.set_addon_property('scene', 'send2ue', 'export_all_actions', True)
self.blender.set_addon_property('scene', 'send2ue', 'import_animations', True)
Expand Down Expand Up @@ -1168,6 +1194,9 @@ def test_export_object_name_as_root_option(self):
"""
raise NotImplementedError('This test case must be implemented or skipped')

def test_custom_root_bone_name(self):
raise NotImplementedError('This test case must be implemented or skipped')

def test_export_custom_property_fcurves_option(self):
"""
Tests export custom property fcurves option.
Expand Down Expand Up @@ -1235,6 +1264,10 @@ def test_auto_stash_active_action_option(self):
def test_export_object_name_as_root_option(self):
pass

@unittest.skip
def test_custom_root_bone_name(self):
pass

@unittest.skip
def test_use_object_origin_option(self):
pass
Expand Down

0 comments on commit 812e564

Please sign in to comment.