mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Implement standalone loader for fbx animation
This commit is contained in:
parent
242bc8796b
commit
abe8fb001d
1 changed files with 125 additions and 92 deletions
|
|
@ -26,33 +26,12 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def _process(self, path, asset_dir, asset_name, instance_name):
|
||||
automated = False
|
||||
actor = None
|
||||
|
||||
def _import_animation(
|
||||
self, path, asset_dir, asset_name, skeleton, automated
|
||||
):
|
||||
task = unreal.AssetImportTask()
|
||||
task.options = unreal.FbxImportUI()
|
||||
|
||||
if instance_name:
|
||||
automated = True
|
||||
# Old method to get the actor
|
||||
# actor_name = 'PersistentLevel.' + instance_name
|
||||
# actor = unreal.EditorLevelLibrary.get_actor_reference(actor_name)
|
||||
actors = unreal.EditorLevelLibrary.get_all_level_actors()
|
||||
for a in actors:
|
||||
if a.get_class().get_name() != "SkeletalMeshActor":
|
||||
continue
|
||||
if a.get_actor_label() == instance_name:
|
||||
actor = a
|
||||
break
|
||||
if not actor:
|
||||
raise Exception(f"Could not find actor {instance_name}")
|
||||
skeleton = actor.skeletal_mesh_component.skeletal_mesh.skeleton
|
||||
task.options.set_editor_property('skeleton', skeleton)
|
||||
|
||||
if not actor:
|
||||
return None
|
||||
|
||||
folder_entity = get_current_folder_entity(fields=["attrib.fps"])
|
||||
|
||||
task.set_editor_property('filename', path)
|
||||
|
|
@ -72,6 +51,7 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
task.options.set_editor_property('import_mesh', False)
|
||||
task.options.set_editor_property('import_animations', True)
|
||||
task.options.set_editor_property('override_full_name', True)
|
||||
task.options.set_editor_property('skeleton', skeleton)
|
||||
|
||||
task.options.anim_sequence_import_data.set_editor_property(
|
||||
'animation_length',
|
||||
|
|
@ -108,6 +88,34 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
animation = imported_asset
|
||||
break
|
||||
|
||||
return animation
|
||||
|
||||
def _process(self, path, asset_dir, asset_name, instance_name):
|
||||
automated = False
|
||||
actor = None
|
||||
|
||||
if instance_name:
|
||||
automated = True
|
||||
# Old method to get the actor
|
||||
# actor_name = 'PersistentLevel.' + instance_name
|
||||
# actor = unreal.EditorLevelLibrary.get_actor_reference(actor_name)
|
||||
actors = unreal.EditorLevelLibrary.get_all_level_actors()
|
||||
for a in actors:
|
||||
if a.get_class().get_name() != "SkeletalMeshActor":
|
||||
continue
|
||||
if a.get_actor_label() == instance_name:
|
||||
actor = a
|
||||
break
|
||||
if not actor:
|
||||
raise Exception(f"Could not find actor {instance_name}")
|
||||
skeleton = actor.skeletal_mesh_component.skeletal_mesh.skeleton
|
||||
|
||||
if not actor:
|
||||
return None
|
||||
|
||||
animation = self._import_animation(
|
||||
path, asset_dir, asset_name, skeleton, automated)
|
||||
|
||||
if animation:
|
||||
animation.set_editor_property('enable_root_motion', True)
|
||||
actor.skeletal_mesh_component.set_editor_property(
|
||||
|
|
@ -117,72 +125,9 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
|
||||
return animation
|
||||
|
||||
def load(self, context, name, namespace, options=None):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): Product name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
# Create directory for asset and Ayon container
|
||||
root = "/Game/Ayon"
|
||||
folder_path = context["folder"]["path"]
|
||||
hierarchy = folder_path.lstrip("/").split("/")
|
||||
folder_name = hierarchy.pop(-1)
|
||||
product_type = context["product"]["productType"]
|
||||
|
||||
suffix = "_CON"
|
||||
asset_name = f"{folder_name}_{name}" if folder_name else f"{name}"
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
f"{root}/Animations/{folder_name}/{name}", suffix="")
|
||||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{hierarchy[0]}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_asset().get_path_name()
|
||||
|
||||
hierarchy_dir = root
|
||||
for h in hierarchy:
|
||||
hierarchy_dir = f"{hierarchy_dir}/{h}"
|
||||
hierarchy_dir = f"{hierarchy_dir}/{folder_name}"
|
||||
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{hierarchy_dir}/"],
|
||||
recursive_paths=True)
|
||||
levels = ar.get_assets(_filter)
|
||||
level = levels[0].get_asset().get_path_name()
|
||||
|
||||
unreal.EditorLevelLibrary.save_all_dirty_levels()
|
||||
unreal.EditorLevelLibrary.load_level(level)
|
||||
|
||||
container_name += suffix
|
||||
|
||||
EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
path = self.filepath_from_context(context)
|
||||
libpath = path.replace(".fbx", ".json")
|
||||
|
||||
def _load_from_json(
|
||||
self, libpath, path, asset_dir, asset_name, hierarchy_dir
|
||||
):
|
||||
with open(libpath, "r") as fp:
|
||||
data = json.load(fp)
|
||||
|
||||
|
|
@ -222,6 +167,93 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
for s in sections:
|
||||
s.params.set_editor_property('animation', animation)
|
||||
|
||||
def _load_standalone_animation(self, path, asset_dir, asset_name):
|
||||
selection = unreal.EditorUtilityLibrary.get_selected_assets()
|
||||
if not selection:
|
||||
raise ValueError("No skeleton selected.")
|
||||
|
||||
skeleton = selection[0]
|
||||
if skeleton.get_class() != unreal.Skeleton.static_class():
|
||||
raise ValueError("Selected asset is not a skeleton.")
|
||||
|
||||
animation = self._import_animation(
|
||||
path, asset_dir, asset_name, skeleton, True)
|
||||
|
||||
return animation
|
||||
|
||||
def load(self, context, name, namespace, options=None):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): Product name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
# Create directory for asset and Ayon container
|
||||
root = "/Game/Ayon"
|
||||
folder_path = context["folder"]["path"]
|
||||
hierarchy = folder_path.lstrip("/").split("/")
|
||||
folder_name = hierarchy.pop(-1)
|
||||
product_type = context["product"]["productType"]
|
||||
|
||||
suffix = "_CON"
|
||||
asset_name = f"{folder_name}_{name}" if folder_name else f"{name}"
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
f"{root}/Animations/{folder_name}/{name}", suffix="")
|
||||
|
||||
path = self.filepath_from_context(context)
|
||||
libpath = path.replace(".fbx", ".json")
|
||||
|
||||
# check if json file exists.
|
||||
if os.path.exists(libpath):
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{hierarchy[0]}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_asset().get_path_name()
|
||||
|
||||
hierarchy_dir = root
|
||||
for h in hierarchy:
|
||||
hierarchy_dir = f"{hierarchy_dir}/{h}"
|
||||
hierarchy_dir = f"{hierarchy_dir}/{folder_name}"
|
||||
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{hierarchy_dir}/"],
|
||||
recursive_paths=True)
|
||||
levels = ar.get_assets(_filter)
|
||||
level = levels[0].get_asset().get_path_name()
|
||||
|
||||
unreal.EditorLevelLibrary.save_all_dirty_levels()
|
||||
unreal.EditorLevelLibrary.load_level(level)
|
||||
|
||||
container_name += suffix
|
||||
|
||||
EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
self._load_from_json(
|
||||
libpath, path, asset_dir, asset_name, hierarchy_dir)
|
||||
else:
|
||||
self._load_standalone_animation(path, asset_dir, asset_name)
|
||||
|
||||
# Create Asset Container
|
||||
unreal_pipeline.create_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
|
@ -249,8 +281,9 @@ class AnimationFBXLoader(plugin.Loader):
|
|||
for a in imported_content:
|
||||
EditorAssetLibrary.save_asset(a)
|
||||
|
||||
unreal.EditorLevelLibrary.save_current_level()
|
||||
unreal.EditorLevelLibrary.load_level(master_level)
|
||||
if master_level:
|
||||
unreal.EditorLevelLibrary.save_current_level()
|
||||
unreal.EditorLevelLibrary.load_level(master_level)
|
||||
|
||||
def update(self, container, context):
|
||||
repre_entity = context["representation"]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue