Merge pull request #3116 from simonebarbieri/enhancement/unreal-layouts_and_camera_update_and_improvements

This commit is contained in:
Milan Kolar 2022-05-13 11:21:13 +02:00 committed by GitHub
commit 7fbb1dfa91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 607 additions and 309 deletions

View file

@ -1,5 +1,8 @@
import os
import unreal import unreal
from openpype.api import Anatomy
from openpype.hosts.unreal.api import pipeline from openpype.hosts.unreal.api import pipeline
@ -46,6 +49,15 @@ def start_rendering():
if data["family"] == "render": if data["family"] == "render":
inst_data.append(data) inst_data.append(data)
try:
project = os.environ.get("AVALON_PROJECT")
anatomy = Anatomy(project)
root = anatomy.roots['renders']
except Exception:
raise Exception("Could not find render root in anatomy settings.")
render_dir = f"{root}/{project}"
# subsystem = unreal.get_editor_subsystem( # subsystem = unreal.get_editor_subsystem(
# unreal.MoviePipelineQueueSubsystem) # unreal.MoviePipelineQueueSubsystem)
# queue = subsystem.get_queue() # queue = subsystem.get_queue()
@ -105,7 +117,7 @@ def start_rendering():
settings.custom_end_frame = r.get("frame_range")[1] settings.custom_end_frame = r.get("frame_range")[1]
settings.use_custom_playback_range = True settings.use_custom_playback_range = True
settings.file_name_format = "{sequence_name}.{frame_number}" settings.file_name_format = "{sequence_name}.{frame_number}"
settings.output_directory.path += r.get('output') settings.output_directory.path = f"{render_dir}/{r.get('output')}"
renderPass = job.get_configuration().find_or_add_setting_by_class( renderPass = job.get_configuration().find_or_add_setting_by_class(
unreal.MoviePipelineDeferredPassBase) unreal.MoviePipelineDeferredPassBase)

View file

@ -134,7 +134,6 @@ class AnimationFBXLoader(plugin.Loader):
Returns: Returns:
list(str): list of container content list(str): list of container content
""" """
# Create directory for asset and avalon container # Create directory for asset and avalon container
hierarchy = context.get('asset').get('data').get('parents') hierarchy = context.get('asset').get('data').get('parents')
root = "/Game/OpenPype" root = "/Game/OpenPype"
@ -149,11 +148,30 @@ class AnimationFBXLoader(plugin.Loader):
asset_dir, container_name = tools.create_unique_asset_name( asset_dir, container_name = tools.create_unique_asset_name(
f"{root}/Animations/{asset}/{name}", suffix="") f"{root}/Animations/{asset}/{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_editor_property('object_path')
hierarchy_dir = root hierarchy_dir = root
for h in hierarchy: for h in hierarchy:
hierarchy_dir = f"{hierarchy_dir}/{h}" hierarchy_dir = f"{hierarchy_dir}/{h}"
hierarchy_dir = f"{hierarchy_dir}/{asset}" hierarchy_dir = f"{hierarchy_dir}/{asset}"
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[f"{hierarchy_dir}/"],
recursive_paths=True)
levels = ar.get_assets(filter)
level = levels[0].get_editor_property('object_path')
unreal.EditorLevelLibrary.save_all_dirty_levels()
unreal.EditorLevelLibrary.load_level(level)
container_name += suffix container_name += suffix
EditorAssetLibrary.make_directory(asset_dir) EditorAssetLibrary.make_directory(asset_dir)
@ -165,7 +183,7 @@ class AnimationFBXLoader(plugin.Loader):
instance_name = data.get("instance_name") instance_name = data.get("instance_name")
animation = self._process(asset_dir, container_name, instance_name) animation = self._process(asset_dir, asset_name, instance_name)
asset_content = EditorAssetLibrary.list_assets( asset_content = EditorAssetLibrary.list_assets(
hierarchy_dir, recursive=True, include_folder=False) hierarchy_dir, recursive=True, include_folder=False)
@ -224,6 +242,9 @@ class AnimationFBXLoader(plugin.Loader):
for a in imported_content: for a in imported_content:
EditorAssetLibrary.save_asset(a) EditorAssetLibrary.save_asset(a)
unreal.EditorLevelLibrary.save_current_level()
unreal.EditorLevelLibrary.load_level(master_level)
def update(self, container, representation): def update(self, container, representation):
name = container["asset_name"] name = container["asset_name"]
source_path = get_representation_path(representation) source_path = get_representation_path(representation)

View file

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""Load camera from FBX.""" """Load camera from FBX."""
import os from pathlib import Path
import unreal import unreal
from unreal import EditorAssetLibrary from unreal import EditorAssetLibrary
@ -268,68 +268,242 @@ class CameraLoader(plugin.Loader):
return asset_content return asset_content
def update(self, container, representation): def update(self, container, representation):
path = container["namespace"]
ar = unreal.AssetRegistryHelpers.get_asset_registry() ar = unreal.AssetRegistryHelpers.get_asset_registry()
tools = unreal.AssetToolsHelpers().get_asset_tools()
asset_content = EditorAssetLibrary.list_assets( root = "/Game/OpenPype"
path, recursive=False, include_folder=False
)
asset_name = ""
for a in asset_content:
asset = ar.get_asset_by_object_path(a)
if a.endswith("_CON"):
loaded_asset = EditorAssetLibrary.load_asset(a)
EditorAssetLibrary.set_metadata_tag(
loaded_asset, "representation", str(representation["_id"])
)
EditorAssetLibrary.set_metadata_tag(
loaded_asset, "parent", str(representation["parent"])
)
asset_name = EditorAssetLibrary.get_metadata_tag(
loaded_asset, "asset_name"
)
elif asset.asset_class == "LevelSequence":
EditorAssetLibrary.delete_asset(a)
sequence = tools.create_asset( asset_dir = container.get('namespace')
asset_name=asset_name,
package_path=path, context = representation.get("context")
asset_class=unreal.LevelSequence,
factory=unreal.LevelSequenceFactoryNew() hierarchy = context.get('hierarchy').split("/")
h_dir = f"{root}/{hierarchy[0]}"
h_asset = hierarchy[0]
master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map"
EditorLevelLibrary.save_current_level()
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[asset_dir],
recursive_paths=False)
sequences = ar.get_assets(filter)
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[str(Path(asset_dir).parent.as_posix())],
recursive_paths=True)
maps = ar.get_assets(filter)
# There should be only one map in the list
EditorLevelLibrary.load_level(maps[0].get_full_name())
level_sequence = sequences[0].get_asset()
display_rate = level_sequence.get_display_rate()
playback_start = level_sequence.get_playback_start()
playback_end = level_sequence.get_playback_end()
sequence_name = f"{container.get('asset')}_camera"
# Get the actors in the level sequence.
objs = unreal.SequencerTools.get_bound_objects(
unreal.EditorLevelLibrary.get_editor_world(),
level_sequence,
level_sequence.get_bindings(),
unreal.SequencerScriptingRange(
has_start_value=True,
has_end_value=True,
inclusive_start=level_sequence.get_playback_start(),
exclusive_end=level_sequence.get_playback_end()
)
) )
io_asset = legacy_io.Session["AVALON_ASSET"] # Delete actors from the map
asset_doc = legacy_io.find_one({ for o in objs:
"type": "asset", if o.bound_objects[0].get_class().get_name() == "CineCameraActor":
"name": io_asset actor_path = o.bound_objects[0].get_path_name().split(":")[-1]
}) actor = EditorLevelLibrary.get_actor_reference(actor_path)
EditorLevelLibrary.destroy_actor(actor)
data = asset_doc.get("data") # Remove the Level Sequence from the parent.
# We need to traverse the hierarchy from the master sequence to find
# the level sequence.
root = "/Game/OpenPype"
namespace = container.get('namespace').replace(f"{root}/", "")
ms_asset = namespace.split('/')[0]
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[f"{root}/{ms_asset}"],
recursive_paths=False)
sequences = ar.get_assets(filter)
master_sequence = sequences[0].get_asset()
if data: sequences = [master_sequence]
sequence.set_display_rate(unreal.FrameRate(data.get("fps"), 1.0))
sequence.set_playback_start(data.get("frameStart")) parent = None
sequence.set_playback_end(data.get("frameEnd")) sub_scene = None
for s in sequences:
tracks = s.get_master_tracks()
subscene_track = None
for t in tracks:
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
subscene_track = t
break
if subscene_track:
sections = subscene_track.get_sections()
for ss in sections:
if ss.get_sequence().get_name() == sequence_name:
parent = s
sub_scene = ss
# subscene_track.remove_section(ss)
break
sequences.append(ss.get_sequence())
# Update subscenes indexes.
i = 0
for ss in sections:
ss.set_row_index(i)
i += 1
if parent:
break
assert parent, "Could not find the parent sequence"
EditorAssetLibrary.delete_asset(level_sequence.get_path_name())
settings = unreal.MovieSceneUserImportFBXSettings() settings = unreal.MovieSceneUserImportFBXSettings()
settings.set_editor_property('reduce_keys', False) settings.set_editor_property('reduce_keys', False)
tools = unreal.AssetToolsHelpers().get_asset_tools()
new_sequence = tools.create_asset(
asset_name=sequence_name,
package_path=asset_dir,
asset_class=unreal.LevelSequence,
factory=unreal.LevelSequenceFactoryNew()
)
new_sequence.set_display_rate(display_rate)
new_sequence.set_playback_start(playback_start)
new_sequence.set_playback_end(playback_end)
sub_scene.set_sequence(new_sequence)
unreal.SequencerTools.import_fbx( unreal.SequencerTools.import_fbx(
EditorLevelLibrary.get_editor_world(), EditorLevelLibrary.get_editor_world(),
sequence, new_sequence,
sequence.get_bindings(), new_sequence.get_bindings(),
settings, settings,
str(representation["data"]["path"]) str(representation["data"]["path"])
) )
data = {
"representation": str(representation["_id"]),
"parent": str(representation["parent"])
}
unreal_pipeline.imprint(
"{}/{}".format(asset_dir, container.get('container_name')), data)
EditorLevelLibrary.save_current_level()
asset_content = EditorAssetLibrary.list_assets(
asset_dir, recursive=True, include_folder=False)
for a in asset_content:
EditorAssetLibrary.save_asset(a)
EditorLevelLibrary.load_level(master_level)
def remove(self, container): def remove(self, container):
path = container["namespace"] path = Path(container.get("namespace"))
parent_path = os.path.dirname(path) parent_path = str(path.parent.as_posix())
EditorAssetLibrary.delete_directory(path) ar = unreal.AssetRegistryHelpers.get_asset_registry()
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[f"{str(path.as_posix())}"],
recursive_paths=False)
sequences = ar.get_assets(filter)
if not sequences:
raise Exception("Could not find sequence.")
world = ar.get_asset_by_object_path(
EditorLevelLibrary.get_editor_world().get_path_name())
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[f"{parent_path}"],
recursive_paths=True)
maps = ar.get_assets(filter)
# There should be only one map in the list
if not maps:
raise Exception("Could not find map.")
map = maps[0]
EditorLevelLibrary.save_all_dirty_levels()
EditorLevelLibrary.load_level(map.get_full_name())
# Remove the camera from the level.
actors = EditorLevelLibrary.get_all_level_actors()
for a in actors:
if a.__class__ == unreal.CineCameraActor:
EditorLevelLibrary.destroy_actor(a)
EditorLevelLibrary.save_all_dirty_levels()
EditorLevelLibrary.load_level(world.get_full_name())
# There should be only one sequence in the path.
sequence_name = sequences[0].asset_name
# Remove the Level Sequence from the parent.
# We need to traverse the hierarchy from the master sequence to find
# the level sequence.
root = "/Game/OpenPype"
namespace = container.get('namespace').replace(f"{root}/", "")
ms_asset = namespace.split('/')[0]
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[f"{root}/{ms_asset}"],
recursive_paths=False)
sequences = ar.get_assets(filter)
master_sequence = sequences[0].get_asset()
sequences = [master_sequence]
parent = None
for s in sequences:
tracks = s.get_master_tracks()
subscene_track = None
for t in tracks:
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
subscene_track = t
break
if subscene_track:
sections = subscene_track.get_sections()
for ss in sections:
if ss.get_sequence().get_name() == sequence_name:
parent = s
subscene_track.remove_section(ss)
break
sequences.append(ss.get_sequence())
# Update subscenes indexes.
i = 0
for ss in sections:
ss.set_row_index(i)
i += 1
if parent:
break
assert parent, "Could not find the parent sequence"
EditorAssetLibrary.delete_directory(str(path.as_posix()))
# Check if there isn't any more assets in the parent folder, and
# delete it if not.
asset_content = EditorAssetLibrary.list_assets( asset_content = EditorAssetLibrary.list_assets(
parent_path, recursive=False, include_folder=True parent_path, recursive=False, include_folder=True
) )

View file

@ -208,7 +208,14 @@ class LayoutLoader(plugin.Loader):
actors.append(actor) actors.append(actor)
binding = sequence.add_possessable(actor) binding = None
for p in sequence.get_possessables():
if p.get_name() == actor.get_name():
binding = p
break
if not binding:
binding = sequence.add_possessable(actor)
bindings.append(binding) bindings.append(binding)
@ -299,15 +306,101 @@ class LayoutLoader(plugin.Loader):
# Add animation to the sequencer # Add animation to the sequencer
bindings = bindings_dict.get(instance_name) bindings = bindings_dict.get(instance_name)
ar = unreal.AssetRegistryHelpers.get_asset_registry()
for binding in bindings: for binding in bindings:
binding.add_track(unreal.MovieSceneSkeletalAnimationTrack) tracks = binding.get_tracks()
for track in binding.get_tracks(): track = None
if not tracks:
track = binding.add_track(
unreal.MovieSceneSkeletalAnimationTrack)
else:
track = tracks[0]
sections = track.get_sections()
section = None
if not sections:
section = track.add_section() section = track.add_section()
section.set_range( else:
sequence.get_playback_start(), section = sections[0]
sequence.get_playback_end())
sec_params = section.get_editor_property('params') sec_params = section.get_editor_property('params')
sec_params.set_editor_property('animation', animation) curr_anim = sec_params.get_editor_property('animation')
if curr_anim:
# Checks if the animation path has a container.
# If it does, it means that the animation is already
# in the sequencer.
anim_path = str(Path(
curr_anim.get_path_name()).parent
).replace('\\', '/')
filter = unreal.ARFilter(
class_names=["AssetContainer"],
package_paths=[anim_path],
recursive_paths=False)
containers = ar.get_assets(filter)
if len(containers) > 0:
return
section.set_range(
sequence.get_playback_start(),
sequence.get_playback_end())
sec_params = section.get_editor_property('params')
sec_params.set_editor_property('animation', animation)
def _generate_sequence(self, h, h_dir):
tools = unreal.AssetToolsHelpers().get_asset_tools()
sequence = tools.create_asset(
asset_name=h,
package_path=h_dir,
asset_class=unreal.LevelSequence,
factory=unreal.LevelSequenceFactoryNew()
)
asset_data = legacy_io.find_one({
"type": "asset",
"name": h_dir.split('/')[-1]
})
id = asset_data.get('_id')
start_frames = []
end_frames = []
elements = list(
legacy_io.find({"type": "asset", "data.visualParent": id}))
for e in elements:
start_frames.append(e.get('data').get('clipIn'))
end_frames.append(e.get('data').get('clipOut'))
elements.extend(legacy_io.find({
"type": "asset",
"data.visualParent": e.get('_id')
}))
min_frame = min(start_frames)
max_frame = max(end_frames)
sequence.set_display_rate(
unreal.FrameRate(asset_data.get('data').get("fps"), 1.0))
sequence.set_playback_start(min_frame)
sequence.set_playback_end(max_frame)
tracks = sequence.get_master_tracks()
track = None
for t in tracks:
if (t.get_class() ==
unreal.MovieSceneCameraCutTrack.static_class()):
track = t
break
if not track:
track = sequence.add_master_track(
unreal.MovieSceneCameraCutTrack)
return sequence, (min_frame, max_frame)
def _process(self, lib_path, asset_dir, sequence, loaded=None): def _process(self, lib_path, asset_dir, sequence, loaded=None):
ar = unreal.AssetRegistryHelpers.get_asset_registry() ar = unreal.AssetRegistryHelpers.get_asset_registry()
@ -326,6 +419,8 @@ class LayoutLoader(plugin.Loader):
actors_dict = {} actors_dict = {}
bindings_dict = {} bindings_dict = {}
loaded_assets = []
for element in data: for element in data:
reference = None reference = None
if element.get('reference_fbx'): if element.get('reference_fbx'):
@ -360,7 +455,7 @@ class LayoutLoader(plugin.Loader):
continue continue
options = { options = {
"asset_dir": asset_dir # "asset_dir": asset_dir
} }
assets = load_container( assets = load_container(
@ -370,6 +465,17 @@ class LayoutLoader(plugin.Loader):
options=options options=options
) )
container = None
for asset in assets:
obj = ar.get_asset_by_object_path(asset).get_asset()
if obj.get_class().get_name() == 'AssetContainer':
container = obj
if obj.get_class().get_name() == 'Skeleton':
skeleton = obj
loaded_assets.append(container.get_path_name())
instances = [ instances = [
item for item in data item for item in data
if (item.get('reference_fbx') == reference or if (item.get('reference_fbx') == reference or
@ -390,15 +496,6 @@ class LayoutLoader(plugin.Loader):
actors_dict[inst] = actors actors_dict[inst] = actors
bindings_dict[inst] = bindings bindings_dict[inst] = bindings
if family == 'rig':
# Finds skeleton among the imported assets
for asset in assets:
obj = ar.get_asset_by_object_path(asset).get_asset()
if obj.get_class().get_name() == 'Skeleton':
skeleton = obj
if skeleton:
break
if skeleton: if skeleton:
skeleton_dict[reference] = skeleton skeleton_dict[reference] = skeleton
else: else:
@ -411,6 +508,8 @@ class LayoutLoader(plugin.Loader):
asset_dir, path, instance_name, skeleton, actors_dict, asset_dir, path, instance_name, skeleton, actors_dict,
animation_file, bindings_dict, sequence) animation_file, bindings_dict, sequence)
return loaded_assets
@staticmethod @staticmethod
def _remove_family(assets, components, class_name, prop_name): def _remove_family(assets, components, class_name, prop_name):
ar = unreal.AssetRegistryHelpers.get_asset_registry() ar = unreal.AssetRegistryHelpers.get_asset_registry()
@ -478,10 +577,10 @@ class LayoutLoader(plugin.Loader):
hierarchy = context.get('asset').get('data').get('parents') hierarchy = context.get('asset').get('data').get('parents')
root = self.ASSET_ROOT root = self.ASSET_ROOT
hierarchy_dir = root hierarchy_dir = root
hierarchy_list = [] hierarchy_dir_list = []
for h in hierarchy: for h in hierarchy:
hierarchy_dir = f"{hierarchy_dir}/{h}" hierarchy_dir = f"{hierarchy_dir}/{h}"
hierarchy_list.append(hierarchy_dir) hierarchy_dir_list.append(hierarchy_dir)
asset = context.get('asset').get('name') asset = context.get('asset').get('name')
suffix = "_CON" suffix = "_CON"
if asset: if asset:
@ -499,43 +598,31 @@ class LayoutLoader(plugin.Loader):
# Create map for the shot, and create hierarchy of map. If the maps # Create map for the shot, and create hierarchy of map. If the maps
# already exist, we will use them. # already exist, we will use them.
maps = [] h_dir = hierarchy_dir_list[0]
for h in hierarchy_list: h_asset = hierarchy[0]
a = h.split('/')[-1] master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map"
map = f"{h}/{a}_map.{a}_map" if not EditorAssetLibrary.does_asset_exist(master_level):
new = False EditorLevelLibrary.new_level(f"{h_dir}/{h_asset}_map")
if not EditorAssetLibrary.does_asset_exist(map):
EditorLevelLibrary.new_level(f"{h}/{a}_map")
new = True
maps.append({"map": map, "new": new})
level = f"{asset_dir}/{asset}_map.{asset}_map"
EditorLevelLibrary.new_level(f"{asset_dir}/{asset}_map") EditorLevelLibrary.new_level(f"{asset_dir}/{asset}_map")
maps.append(
{"map": f"{asset_dir}/{asset}_map.{asset}_map", "new": True})
for i in range(0, len(maps) - 1): EditorLevelLibrary.load_level(master_level)
for j in range(i + 1, len(maps)): EditorLevelUtils.add_level_to_world(
if maps[j].get('new'): EditorLevelLibrary.get_editor_world(),
EditorLevelLibrary.load_level(maps[i].get('map')) level,
EditorLevelUtils.add_level_to_world( unreal.LevelStreamingDynamic
EditorLevelLibrary.get_editor_world(), )
maps[j].get('map'), EditorLevelLibrary.save_all_dirty_levels()
unreal.LevelStreamingDynamic EditorLevelLibrary.load_level(level)
)
EditorLevelLibrary.save_all_dirty_levels()
EditorLevelLibrary.load_level(maps[-1].get('map'))
# Get all the sequences in the hierarchy. It will create them, if # Get all the sequences in the hierarchy. It will create them, if
# they don't exist. # they don't exist.
sequences = [] sequences = []
frame_ranges = [] frame_ranges = []
i = 0 for (h_dir, h) in zip(hierarchy_dir_list, hierarchy):
for h in hierarchy_list:
root_content = EditorAssetLibrary.list_assets( root_content = EditorAssetLibrary.list_assets(
h, recursive=False, include_folder=False) h_dir, recursive=False, include_folder=False)
existing_sequences = [ existing_sequences = [
EditorAssetLibrary.find_asset_data(asset) EditorAssetLibrary.find_asset_data(asset)
@ -545,55 +632,10 @@ class LayoutLoader(plugin.Loader):
] ]
if not existing_sequences: if not existing_sequences:
sequence = tools.create_asset( sequence, frame_range = self._generate_sequence(h, h_dir)
asset_name=hierarchy[i],
package_path=h,
asset_class=unreal.LevelSequence,
factory=unreal.LevelSequenceFactoryNew()
)
asset_data = legacy_io.find_one({
"type": "asset",
"name": h.split('/')[-1]
})
id = asset_data.get('_id')
start_frames = []
end_frames = []
elements = list(
legacy_io.find({"type": "asset", "data.visualParent": id}))
for e in elements:
start_frames.append(e.get('data').get('clipIn'))
end_frames.append(e.get('data').get('clipOut'))
elements.extend(legacy_io.find({
"type": "asset",
"data.visualParent": e.get('_id')
}))
min_frame = min(start_frames)
max_frame = max(end_frames)
sequence.set_display_rate(
unreal.FrameRate(asset_data.get('data').get("fps"), 1.0))
sequence.set_playback_start(min_frame)
sequence.set_playback_end(max_frame)
sequences.append(sequence) sequences.append(sequence)
frame_ranges.append((min_frame, max_frame)) frame_ranges.append(frame_range)
tracks = sequence.get_master_tracks()
track = None
for t in tracks:
if (t.get_class() ==
unreal.MovieSceneCameraCutTrack.static_class()):
track = t
break
if not track:
track = sequence.add_master_track(
unreal.MovieSceneCameraCutTrack)
else: else:
for e in existing_sequences: for e in existing_sequences:
sequences.append(e.get_asset()) sequences.append(e.get_asset())
@ -601,8 +643,6 @@ class LayoutLoader(plugin.Loader):
e.get_asset().get_playback_start(), e.get_asset().get_playback_start(),
e.get_asset().get_playback_end())) e.get_asset().get_playback_end()))
i += 1
shot = tools.create_asset( shot = tools.create_asset(
asset_name=asset, asset_name=asset,
package_path=asset_dir, package_path=asset_dir,
@ -612,15 +652,11 @@ class LayoutLoader(plugin.Loader):
# sequences and frame_ranges have the same length # sequences and frame_ranges have the same length
for i in range(0, len(sequences) - 1): for i in range(0, len(sequences) - 1):
maps_to_add = []
for j in range(i + 1, len(maps)):
maps_to_add.append(maps[j].get('map'))
self._set_sequence_hierarchy( self._set_sequence_hierarchy(
sequences[i], sequences[i + 1], sequences[i], sequences[i + 1],
frame_ranges[i][1], frame_ranges[i][1],
frame_ranges[i + 1][0], frame_ranges[i + 1][1], frame_ranges[i + 1][0], frame_ranges[i + 1][1],
maps_to_add) [level])
data = self._get_data(asset) data = self._get_data(asset)
shot.set_display_rate( shot.set_display_rate(
@ -631,11 +667,11 @@ class LayoutLoader(plugin.Loader):
sequences[-1], shot, sequences[-1], shot,
frame_ranges[-1][1], frame_ranges[-1][1],
data.get('clipIn'), data.get('clipOut'), data.get('clipIn'), data.get('clipOut'),
[maps[-1].get('map')]) [level])
EditorLevelLibrary.load_level(maps[-1].get('map')) EditorLevelLibrary.load_level(level)
self._process(self.fname, asset_dir, shot) loaded_assets = self._process(self.fname, asset_dir, shot)
for s in sequences: for s in sequences:
EditorAssetLibrary.save_asset(s.get_full_name()) EditorAssetLibrary.save_asset(s.get_full_name())
@ -656,7 +692,8 @@ class LayoutLoader(plugin.Loader):
"loader": str(self.__class__.__name__), "loader": str(self.__class__.__name__),
"representation": context["representation"]["_id"], "representation": context["representation"]["_id"],
"parent": context["representation"]["parent"], "parent": context["representation"]["parent"],
"family": context["representation"]["context"]["family"] "family": context["representation"]["context"]["family"],
"loaded_assets": loaded_assets
} }
unreal_pipeline.imprint( unreal_pipeline.imprint(
"{}/{}".format(asset_dir, container_name), data) "{}/{}".format(asset_dir, container_name), data)
@ -667,148 +704,192 @@ class LayoutLoader(plugin.Loader):
for a in asset_content: for a in asset_content:
EditorAssetLibrary.save_asset(a) EditorAssetLibrary.save_asset(a)
EditorLevelLibrary.load_level(maps[0].get('map')) EditorLevelLibrary.load_level(master_level)
return asset_content return asset_content
def update(self, container, representation): def update(self, container, representation):
ar = unreal.AssetRegistryHelpers.get_asset_registry() ar = unreal.AssetRegistryHelpers.get_asset_registry()
root = "/Game/OpenPype"
asset_dir = container.get('namespace')
context = representation.get("context")
hierarchy = context.get('hierarchy').split("/")
h_dir = f"{root}/{hierarchy[0]}"
h_asset = hierarchy[0]
master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map"
# # Create a temporary level to delete the layout level.
# EditorLevelLibrary.save_all_dirty_levels()
# EditorAssetLibrary.make_directory(f"{root}/tmp")
# tmp_level = f"{root}/tmp/temp_map"
# if not EditorAssetLibrary.does_asset_exist(f"{tmp_level}.temp_map"):
# EditorLevelLibrary.new_level(tmp_level)
# else:
# EditorLevelLibrary.load_level(tmp_level)
# Get layout level
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[asset_dir],
recursive_paths=False)
levels = ar.get_assets(filter)
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[asset_dir],
recursive_paths=False)
sequences = ar.get_assets(filter)
layout_level = levels[0].get_editor_property('object_path')
EditorLevelLibrary.save_all_dirty_levels()
EditorLevelLibrary.load_level(layout_level)
# Delete all the actors in the level
actors = unreal.EditorLevelLibrary.get_all_level_actors()
for actor in actors:
unreal.EditorLevelLibrary.destroy_actor(actor)
EditorLevelLibrary.save_current_level()
EditorAssetLibrary.delete_directory(f"{asset_dir}/animations/")
source_path = get_representation_path(representation) source_path = get_representation_path(representation)
destination_path = container["namespace"]
lib_path = Path(get_representation_path(representation))
self._remove_actors(destination_path) loaded_assets = self._process(
source_path, asset_dir, sequences[0].get_asset())
# Delete old animations data = {
anim_path = f"{destination_path}/animations/" "representation": str(representation["_id"]),
EditorAssetLibrary.delete_directory(anim_path) "parent": str(representation["parent"]),
"loaded_assets": loaded_assets
with open(source_path, "r") as fp: }
data = json.load(fp)
references = [e.get('reference_fbx') for e in data]
asset_containers = self._get_asset_containers(destination_path)
loaded = []
# Delete all the assets imported with the previous version of the
# layout, if they're not in the new layout.
for asset_container in asset_containers:
if asset_container.get_editor_property(
'asset_name') == container["objectName"]:
continue
ref = EditorAssetLibrary.get_metadata_tag(
asset_container.get_asset(), 'representation')
ppath = asset_container.get_editor_property('package_path')
if ref not in references:
# If the asset is not in the new layout, delete it.
# Also check if the parent directory is empty, and delete that
# as well, if it is.
EditorAssetLibrary.delete_directory(ppath)
parent = os.path.dirname(str(ppath))
parent_content = EditorAssetLibrary.list_assets(
parent, recursive=False, include_folder=True
)
if len(parent_content) == 0:
EditorAssetLibrary.delete_directory(parent)
else:
# If the asset is in the new layout, search the instances in
# the JSON file, and create actors for them.
actors_dict = {}
skeleton_dict = {}
for element in data:
reference = element.get('reference_fbx')
instance_name = element.get('instance_name')
skeleton = None
if reference == ref and ref not in loaded:
loaded.append(ref)
family = element.get('family')
assets = EditorAssetLibrary.list_assets(
ppath, recursive=True, include_folder=False)
instances = [
item for item in data
if item.get('reference_fbx') == reference]
for instance in instances:
transform = instance.get('transform')
inst = instance.get('instance_name')
actors = []
if family == 'model':
actors = self._process_family(
assets, 'StaticMesh', transform, inst)
elif family == 'rig':
actors = self._process_family(
assets, 'SkeletalMesh', transform, inst)
actors_dict[inst] = actors
if family == 'rig':
# Finds skeleton among the imported assets
for asset in assets:
obj = ar.get_asset_by_object_path(
asset).get_asset()
if obj.get_class().get_name() == 'Skeleton':
skeleton = obj
if skeleton:
break
if skeleton:
skeleton_dict[reference] = skeleton
else:
skeleton = skeleton_dict.get(reference)
animation_file = element.get('animation')
if animation_file and skeleton:
self._import_animation(
destination_path, lib_path,
instance_name, skeleton,
actors_dict, animation_file)
self._process(source_path, destination_path, loaded)
container_path = "{}/{}".format(container["namespace"],
container["objectName"])
# update metadata
unreal_pipeline.imprint( unreal_pipeline.imprint(
container_path, "{}/{}".format(asset_dir, container.get('container_name')), data)
{
"representation": str(representation["_id"]), EditorLevelLibrary.save_current_level()
"parent": str(representation["parent"])
})
asset_content = EditorAssetLibrary.list_assets( asset_content = EditorAssetLibrary.list_assets(
destination_path, recursive=True, include_folder=False) asset_dir, recursive=True, include_folder=False)
for a in asset_content: for a in asset_content:
EditorAssetLibrary.save_asset(a) EditorAssetLibrary.save_asset(a)
EditorLevelLibrary.load_level(master_level)
def remove(self, container): def remove(self, container):
""" """
First, destroy all actors of the assets to be removed. Then, deletes Delete the layout. First, check if the assets loaded with the layout
the asset's directory. are used by other layouts. If not, delete the assets.
""" """
path = container["namespace"] path = Path(container.get("namespace"))
parent_path = os.path.dirname(path)
self._remove_actors(path) containers = unreal_pipeline.ls()
layout_containers = [
c for c in containers
if (c.get('asset_name') != container.get('asset_name') and
c.get('family') == "layout")]
EditorAssetLibrary.delete_directory(path) # Check if the assets have been loaded by other layouts, and deletes
# them if they haven't.
for asset in container.get('loaded_assets'):
layouts = [
lc for lc in layout_containers
if asset in lc.get('loaded_assets')]
if len(layouts) == 0:
EditorAssetLibrary.delete_directory(str(Path(asset).parent))
# Remove the Level Sequence from the parent.
# We need to traverse the hierarchy from the master sequence to find
# the level sequence.
root = "/Game/OpenPype"
namespace = container.get('namespace').replace(f"{root}/", "")
ms_asset = namespace.split('/')[0]
ar = unreal.AssetRegistryHelpers.get_asset_registry()
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[f"{root}/{ms_asset}"],
recursive_paths=False)
sequences = ar.get_assets(filter)
master_sequence = sequences[0].get_asset()
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[f"{root}/{ms_asset}"],
recursive_paths=False)
levels = ar.get_assets(filter)
master_level = levels[0].get_editor_property('object_path')
sequences = [master_sequence]
parent = None
for s in sequences:
tracks = s.get_master_tracks()
subscene_track = None
visibility_track = None
for t in tracks:
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
subscene_track = t
if (t.get_class() ==
unreal.MovieSceneLevelVisibilityTrack.static_class()):
visibility_track = t
if subscene_track:
sections = subscene_track.get_sections()
for ss in sections:
if ss.get_sequence().get_name() == container.get('asset'):
parent = s
subscene_track.remove_section(ss)
break
sequences.append(ss.get_sequence())
# Update subscenes indexes.
i = 0
for ss in sections:
ss.set_row_index(i)
i += 1
if visibility_track:
sections = visibility_track.get_sections()
for ss in sections:
if (unreal.Name(f"{container.get('asset')}_map")
in ss.get_level_names()):
visibility_track.remove_section(ss)
# Update visibility sections indexes.
i = -1
prev_name = []
for ss in sections:
if prev_name != ss.get_level_names():
i += 1
ss.set_row_index(i)
prev_name = ss.get_level_names()
if parent:
break
assert parent, "Could not find the parent sequence"
# Create a temporary level to delete the layout level.
EditorLevelLibrary.save_all_dirty_levels()
EditorAssetLibrary.make_directory(f"{root}/tmp")
tmp_level = f"{root}/tmp/temp_map"
if not EditorAssetLibrary.does_asset_exist(f"{tmp_level}.temp_map"):
EditorLevelLibrary.new_level(tmp_level)
else:
EditorLevelLibrary.load_level(tmp_level)
# Delete the layout directory.
EditorAssetLibrary.delete_directory(str(path))
EditorLevelLibrary.load_level(master_level)
EditorAssetLibrary.delete_directory(f"{root}/tmp")
EditorLevelLibrary.save_current_level()
# Delete the parent folder if there aren't any more layouts in it.
asset_content = EditorAssetLibrary.list_assets( asset_content = EditorAssetLibrary.list_assets(
parent_path, recursive=False, include_folder=True str(path.parent), recursive=False, include_folder=True
) )
if len(asset_content) == 0: if len(asset_content) == 0:
EditorAssetLibrary.delete_directory(parent_path) EditorAssetLibrary.delete_directory(str(path.parent))

View file

@ -52,54 +52,55 @@ class SkeletalMeshFBXLoader(plugin.Loader):
asset_name = "{}_{}".format(asset, name) asset_name = "{}_{}".format(asset, name)
else: else:
asset_name = "{}".format(name) asset_name = "{}".format(name)
version = context.get('version').get('name')
tools = unreal.AssetToolsHelpers().get_asset_tools() tools = unreal.AssetToolsHelpers().get_asset_tools()
asset_dir, container_name = tools.create_unique_asset_name( asset_dir, container_name = tools.create_unique_asset_name(
"{}/{}/{}".format(root, asset, name), suffix="") f"{root}/{asset}/{name}_v{version:03d}", suffix="")
container_name += suffix container_name += suffix
unreal.EditorAssetLibrary.make_directory(asset_dir) if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir):
unreal.EditorAssetLibrary.make_directory(asset_dir)
task = unreal.AssetImportTask() task = unreal.AssetImportTask()
task.set_editor_property('filename', self.fname) task.set_editor_property('filename', self.fname)
task.set_editor_property('destination_path', asset_dir) task.set_editor_property('destination_path', asset_dir)
task.set_editor_property('destination_name', asset_name) task.set_editor_property('destination_name', asset_name)
task.set_editor_property('replace_existing', False) task.set_editor_property('replace_existing', False)
task.set_editor_property('automated', True) task.set_editor_property('automated', True)
task.set_editor_property('save', False) task.set_editor_property('save', False)
# set import options here # set import options here
options = unreal.FbxImportUI() options = unreal.FbxImportUI()
options.set_editor_property('import_as_skeletal', True) options.set_editor_property('import_as_skeletal', True)
options.set_editor_property('import_animations', False) options.set_editor_property('import_animations', False)
options.set_editor_property('import_mesh', True) options.set_editor_property('import_mesh', True)
options.set_editor_property('import_materials', True) options.set_editor_property('import_materials', False)
options.set_editor_property('import_textures', True) options.set_editor_property('import_textures', False)
options.set_editor_property('skeleton', None) options.set_editor_property('skeleton', None)
options.set_editor_property('create_physics_asset', False) options.set_editor_property('create_physics_asset', False)
options.set_editor_property('mesh_type_to_import', options.set_editor_property(
unreal.FBXImportType.FBXIT_SKELETAL_MESH) 'mesh_type_to_import',
unreal.FBXImportType.FBXIT_SKELETAL_MESH)
options.skeletal_mesh_import_data.set_editor_property( options.skeletal_mesh_import_data.set_editor_property(
'import_content_type', 'import_content_type',
unreal.FBXImportContentType.FBXICT_ALL unreal.FBXImportContentType.FBXICT_ALL)
) # set to import normals, otherwise Unreal will compute them
# set to import normals, otherwise Unreal will compute them # and it will take a long time, depending on the size of the mesh
# and it will take a long time, depending on the size of the mesh options.skeletal_mesh_import_data.set_editor_property(
options.skeletal_mesh_import_data.set_editor_property( 'normal_import_method',
'normal_import_method', unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS)
unreal.FBXNormalImportMethod.FBXNIM_IMPORT_NORMALS
)
task.options = options task.options = options
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501 unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501
# Create Asset Container # Create Asset Container
unreal_pipeline.create_container( unreal_pipeline.create_container(
container=container_name, path=asset_dir) container=container_name, path=asset_dir)
data = { data = {
"schema": "openpype:container-2.0", "schema": "openpype:container-2.0",

View file

@ -1,8 +1,11 @@
import os
from pathlib import Path from pathlib import Path
import unreal import unreal
import pyblish.api from openpype.api import Anatomy
from openpype.hosts.unreal.api import pipeline from openpype.hosts.unreal.api import pipeline
import pyblish.api
class CollectRenderInstances(pyblish.api.InstancePlugin): class CollectRenderInstances(pyblish.api.InstancePlugin):
@ -77,9 +80,15 @@ class CollectRenderInstances(pyblish.api.InstancePlugin):
self.log.debug(f"new instance data: {new_data}") self.log.debug(f"new instance data: {new_data}")
project_dir = unreal.Paths.project_dir() try:
render_dir = (f"{project_dir}/Saved/MovieRenders/" project = os.environ.get("AVALON_PROJECT")
f"{s.get('output')}") anatomy = Anatomy(project)
root = anatomy.roots['renders']
except Exception:
raise Exception(
"Could not find render root in anatomy settings.")
render_dir = f"{root}/{project}/{s.get('output')}"
render_path = Path(render_dir) render_path = Path(render_dir)
frames = [] frames = []