Skip to content

Commit

Permalink
Send2UE - Use Collections as Folders Animation & Groom Support (#44)
Browse files Browse the repository at this point in the history
* Implementing @eXifreXi contribution

Adds animation support to Use Collections as Folders extension

* added groom support

implemented pre_groom_export and added to pre_import

* cleanup

* updated pre_import

checks object name exists

* added support for particle hair

* set target skel first

* updated test

should now verify asset exists in full path

* Update base_test_case.py

* added groom folder parameter

added groom folder path parameter to assert_binding_asset
  • Loading branch information
JoshQuake authored Aug 20, 2024
1 parent 2a386fd commit 04b1b11
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/addons/send2ue/dependencies/unreal.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,8 +449,8 @@ def create_binding_asset(groom_asset_path, mesh_asset_path):
)

# source groom asset and target skeletal mesh for the binding asset
groom_binding_asset.set_editor_property('groom', groom_asset)
groom_binding_asset.set_editor_property('target_skeletal_mesh', mesh_asset)
groom_binding_asset.set_editor_property('groom', groom_asset)

# if a previous version of the binding asset exists, consolidate all references with new asset
if existing_binding_asset:
Expand Down
119 changes: 104 additions & 15 deletions src/addons/send2ue/resources/extensions/use_collections_as_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,43 @@ class UseCollectionsAsFoldersExtension(ExtensionBase):
"the specified mesh folder in your unreal project"
)
)

def pre_animation_export(self, asset_data, properties):
"""
Defines the pre animation export logic that uses blender collections as unreal folders
:param dict asset_data: A mutable dictionary of asset data for the current asset.
:param Send2UeSceneProperties properties: The scene property group that contains all the addon properties.
"""
if self.use_collections_as_folders:
asset_type = asset_data.get('_asset_type')
if asset_type and asset_type in [UnrealTypes.ANIM_SEQUENCE]:
action_name = asset_data.get('_action_name')
if action_name:
action = bpy.data.actions.get(action_name)

if not action:
print(f"Couldnt find action '{action_name}'.")
return

# Grab armature object to then grab the collection from, as we can't grab it from the action itself
armature_object_name = asset_data['_armature_object_name']
armature_object = bpy.data.objects.get(armature_object_name)
if not armature_object:
print(f"Couldnt find armature '{armature_object_name}'.")
return

asset_name = utilities.get_asset_name(action_name, properties)

_, file_extension = os.path.splitext(asset_data.get('file_path'))
export_path = self.get_full_export_path(properties, UnrealTypes.ANIM_SEQUENCE, armature_object)
file_name_with_extension = f'{asset_name}{file_extension}'

file_path = os.path.join(export_path, file_name_with_extension)

self.update_asset_data({
'file_path': file_path
})

def pre_mesh_export(self, asset_data, properties):
"""
Expand All @@ -27,9 +64,7 @@ def pre_mesh_export(self, asset_data, properties):
"""
if self.use_collections_as_folders:
asset_type = asset_data.get('_asset_type')
if asset_type and asset_type in [UnrealTypes.ANIM_SEQUENCE, UnrealTypes.GROOM]:
print('Unsupported at this time')
elif asset_type and asset_type in [UnrealTypes.STATIC_MESH, UnrealTypes.SKELETAL_MESH]:
if asset_type and asset_type in [UnrealTypes.STATIC_MESH, UnrealTypes.SKELETAL_MESH]:
object_name = asset_data.get('_mesh_object_name')
if object_name:
scene_object = bpy.data.objects.get(object_name)
Expand All @@ -49,6 +84,36 @@ def pre_mesh_export(self, asset_data, properties):
'file_path': os.path.join(export_path, file_name_with_extension)
})

def pre_groom_export(self, asset_data, properties):
"""
Defines the pre groom export logic that uses blender collections as unreal folders
:param dict asset_data: A mutable dictionary of asset data for the current asset.
:param Send2UeSceneProperties properties: The scene property group that contains all the addon properties.
"""

if self.use_collections_as_folders:
asset_type = asset_data.get('_asset_type')
if asset_type and asset_type in [UnrealTypes.GROOM]:
object_name = asset_data.get('_object_name')
if object_name:
scene_object = bpy.data.objects.get(object_name)
particle_object_name = asset_data.get('_particle_object_name')
if particle_object_name:
scene_object = utilities.get_mesh_object_for_groom_name(object_name)

asset_name = utilities.get_asset_name(object_name, properties)

_, file_extension = os.path.splitext(asset_data.get('file_path'))
export_path = self.get_full_export_path(properties, asset_type, scene_object)
file_name_with_extension = f'{asset_name}{file_extension}'
file_path = os.path.join(export_path, file_name_with_extension)
self.update_asset_data({
'file_path': file_path
})



def get_full_export_path(self, properties, asset_type, scene_object):
"""
Gets the unreal export path when use_collections_as_folders extension is active.
Expand All @@ -73,17 +138,41 @@ def pre_import(self, asset_data, properties):
if self.use_collections_as_folders:
asset_type = asset_data.get('_asset_type')
if asset_type and asset_type == UnrealTypes.ANIM_SEQUENCE:
object_name = asset_data['_armature_object_name']
scene_object = bpy.data.objects.get(object_name)
# update skeletal asset path now that it is under new collections path
self.update_asset_data({
'skeleton_asset_path': utilities.get_skeleton_asset_path(
scene_object,
properties,
self.get_full_import_path,
scene_object,
)
})
action_name = asset_data.get('_action_name')
if action_name:
asset_name = utilities.get_asset_name(action_name, properties)

armature_object_name = asset_data['_armature_object_name']
armature_object = bpy.data.objects.get(armature_object_name)

import_path = self.get_full_import_path(properties, UnrealTypes.ANIM_SEQUENCE, armature_object)

self.update_asset_data({
# update skeletal asset path now that it is under new collections path
'skeleton_asset_path': utilities.get_skeleton_asset_path(
armature_object,
properties,
self.get_full_import_path,
armature_object,
),
# update asset folder and path to the new path based on the collections
'asset_folder': import_path,
'asset_path': f'{import_path}{asset_name}'
})
elif asset_type and asset_type == UnrealTypes.GROOM:
object_name = asset_data.get('_object_name')
if object_name:
scene_object = bpy.data.objects.get(object_name)
particle_object_name = asset_data.get('_particle_object_name')
if particle_object_name:
scene_object = utilities.get_mesh_object_for_groom_name(object_name)

asset_name = utilities.get_asset_name(object_name, properties)
import_path = self.get_full_import_path(properties, asset_type, scene_object)
self.update_asset_data({
'asset_folder': import_path,
'asset_path': f'{import_path}{asset_name}'
})
elif asset_type:
object_name = asset_data.get('_mesh_object_name')
if object_name:
Expand Down Expand Up @@ -161,4 +250,4 @@ def draw_paths(self, dialog, layout, properties):
:param bpy.types.UILayout layout: The extension layout area.
:param Send2UeSceneProperties properties: The scene property group that contains all the addon properties.
"""
dialog.draw_property(self, layout, 'use_collections_as_folders')
dialog.draw_property(self, layout, 'use_collections_as_folders')
8 changes: 5 additions & 3 deletions tests/test_send2ue_extension_use_collections_as_folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ def run_use_collections_as_folders_option_tests(self, objects_and_collections):
# check that the mesh name is the parent collection
mesh_folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_mesh_folder_path')
collection_hierarchy_path = '/'.join(collection_hierarchy[1:])
folder_path = f'{mesh_folder_path}{collection_hierarchy_path}/'
self.assert_asset_exists(object_name, folder_path, True)
final_mesh_folder_path = f'{mesh_folder_path}{collection_hierarchy_path}/'
self.assert_asset_exists(object_name, final_mesh_folder_path, True)

# check that the groom assets have the correct binding target mesh
for groom in groom_systems:
self.assert_binding_asset(groom, object_name, folder_path)
groom_folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_groom_folder_path')
final_groom_folder_path = f'{groom_folder_path}{collection_hierarchy_path}/'
self.assert_binding_asset(groom, object_name, final_mesh_folder_path, final_groom_folder_path)


class TestSend2UeExtensionCollectionsAsFoldersCubes(
Expand Down
7 changes: 4 additions & 3 deletions tests/utils/base_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ def assert_asset_exists(self, asset_name, folder_path, exists=True):
self.log(f'Ensuring that "{asset_name}" exists...')
self.assertTrue(
self.unreal.asset_exists(f'{folder_path}{asset_name}'),
f'The "{asset_name}" does not exist in unreal!'
f'The "{asset_name}" does not exist in unreal! Loc: {folder_path}'
)
else:
self.log(f'Ensuring that "{asset_name}" does not exist...')
Expand Down Expand Up @@ -434,14 +434,15 @@ def assert_groom_import(self, asset_name, exists=True):
folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_groom_folder_path')
self.assert_asset_exists(asset_name, folder_path, exists)

def assert_binding_asset(self, groom_asset_name, target_mesh_name, mesh_folder_path=None):
def assert_binding_asset(self, groom_asset_name, target_mesh_name, mesh_folder_path=None, groom_folder_path=None):
self.log(f'Checking that binding asset is created correctly for "{groom_asset_name}"...')

binding_asset_name = f'{groom_asset_name}_{target_mesh_name}_Binding'

if not mesh_folder_path:
mesh_folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_mesh_folder_path')
groom_folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_groom_folder_path')
if not groom_folder_path:
groom_folder_path = self.blender.get_addon_property('scene', 'send2ue', 'unreal_groom_folder_path')

self.assert_asset_exists(binding_asset_name, groom_folder_path, True)

Expand Down

0 comments on commit 04b1b11

Please sign in to comment.