Implemented render publishing

This commit is contained in:
Simone Barbieri 2022-03-18 15:07:30 +00:00
parent 8e1ec6d251
commit 6d787cadd1
7 changed files with 313 additions and 2 deletions

View file

@ -47,6 +47,7 @@ def install():
print("installing OpenPype for Unreal ...")
print("-=" * 40)
logger.info("installing OpenPype for Unreal")
pyblish.api.register_host("unreal")
pyblish.api.register_plugin_path(str(PUBLISH_PATH))
register_loader_plugin_path(str(LOAD_PATH))
api.register_plugin_path(LegacyCreator, str(CREATE_PATH))
@ -416,3 +417,24 @@ def cast_map_to_str_dict(umap) -> dict:
"""
return {str(key): str(value) for (key, value) in umap.items()}
def get_subsequences(sequence: unreal.LevelSequence):
"""Get list of subsequences from sequence.
Args:
sequence (unreal.LevelSequence): Sequence
Returns:
list(unreal.LevelSequence): List of subsequences
"""
tracks = sequence.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 is not None and subscene_track.get_sections():
return subscene_track.get_sections()
return []

View file

@ -0,0 +1,113 @@
import unreal
from openpype.hosts.unreal.api import pipeline
from openpype.hosts.unreal.api.plugin import Creator
class CreateRender(Creator):
"""Create instance for sequence for rendering"""
name = "unrealRender"
label = "Unreal - Render"
family = "render"
icon = "cube"
asset_types = ["LevelSequence"]
root = "/Game/OpenPype/PublishInstances"
suffix = "_INS"
def __init__(self, *args, **kwargs):
super(CreateRender, self).__init__(*args, **kwargs)
def process(self):
subset = self.data["subset"]
ar = unreal.AssetRegistryHelpers.get_asset_registry()
# Get the master sequence and the master level.
# There should be only one sequence and one level in the directory.
filter = unreal.ARFilter(
class_names=["LevelSequence"],
package_paths=[f"/Game/OpenPype/{self.data['asset']}"],
recursive_paths=False)
sequences = ar.get_assets(filter)
ms = sequences[0].get_editor_property('object_path')
filter = unreal.ARFilter(
class_names=["World"],
package_paths=[f"/Game/OpenPype/{self.data['asset']}"],
recursive_paths=False)
levels = ar.get_assets(filter)
ml = levels[0].get_editor_property('object_path')
selection = []
if (self.options or {}).get("useSelection"):
sel_objects = unreal.EditorUtilityLibrary.get_selected_assets()
selection = [
a.get_path_name() for a in sel_objects
if a.get_class().get_name() in self.asset_types]
else:
selection.append(self.data['sequence'])
unreal.log(f"selection: {selection}")
path = f"{self.root}"
unreal.EditorAssetLibrary.make_directory(path)
ar = unreal.AssetRegistryHelpers.get_asset_registry()
for a in selection:
ms_obj = ar.get_asset_by_object_path(ms).get_asset()
seq_data = None
if a == ms:
seq_data = {
"sequence": ms_obj,
"output": f"{ms_obj.get_name()}",
"frame_range": (
ms_obj.get_playback_start(), ms_obj.get_playback_end())
}
else:
seq_data_list = [{
"sequence": ms_obj,
"output": f"{ms_obj.get_name()}",
"frame_range": (
ms_obj.get_playback_start(), ms_obj.get_playback_end())
}]
for s in seq_data_list:
subscenes = pipeline.get_subsequences(s.get('sequence'))
for ss in subscenes:
curr_data = {
"sequence": ss.get_sequence(),
"output": (f"{s.get('output')}/"
f"{ss.get_sequence().get_name()}"),
"frame_range": (
ss.get_start_frame(), ss.get_end_frame() - 1)
}
if ss.get_sequence().get_path_name() == a:
seq_data = curr_data
break
seq_data_list.append(curr_data)
if seq_data is not None:
break
if not seq_data:
continue
d = self.data.copy()
d["members"] = [a]
d["sequence"] = a
d["master_sequence"] = ms
d["master_level"] = ml
d["output"] = seq_data.get('output')
d["frameStart"] = seq_data.get('frame_range')[0]
d["frameEnd"] = seq_data.get('frame_range')[1]
container_name = f"{subset}{self.suffix}"
pipeline.create_publish_instance(
instance=container_name, path=path)
pipeline.imprint(f"{path}/{container_name}", d)

View file

@ -17,7 +17,7 @@ class CollectInstances(pyblish.api.ContextPlugin):
"""
label = "Collect Instances"
order = pyblish.api.CollectorOrder
order = pyblish.api.CollectorOrder - 0.1
hosts = ["unreal"]
def process(self, context):

View file

@ -0,0 +1,24 @@
import pyblish.api
class CollectRemoveMarked(pyblish.api.ContextPlugin):
"""Remove marked data
Remove instances that have 'remove' in their instance.data
"""
order = pyblish.api.CollectorOrder + 0.499
label = 'Remove Marked Instances'
def process(self, context):
self.log.debug(context)
# make ftrack publishable
instances_to_remove = []
for instance in context:
if instance.data.get('remove'):
instances_to_remove.append(instance)
for instance in instances_to_remove:
context.remove(instance)

View file

@ -0,0 +1,106 @@
from pathlib import Path
from tkinter.font import families
import unreal
import pyblish.api
from openpype import lib
from openpype.pipeline import legacy_create
from openpype.hosts.unreal.api import pipeline
class CollectRenderInstances(pyblish.api.InstancePlugin):
""" This collector will try to find all the rendered frames.
"""
order = pyblish.api.CollectorOrder
hosts = ["unreal"]
families = ["render"]
label = "Collect Render Instances"
def process(self, instance):
self.log.debug("Preparing Rendering Instances")
context = instance.context
data = instance.data
data['remove'] = True
ar = unreal.AssetRegistryHelpers.get_asset_registry()
sequence = ar.get_asset_by_object_path(
data.get('sequence')).get_asset()
sequences = [{
"sequence": sequence,
"output": data.get('output'),
"frame_range": (
data.get('frameStart'), data.get('frameEnd'))
}]
for s in sequences:
self.log.debug(f"Processing: {s.get('sequence').get_name()}")
subscenes = pipeline.get_subsequences(s.get('sequence'))
if subscenes:
for ss in subscenes:
sequences.append({
"sequence": ss.get_sequence(),
"output": (f"{s.get('output')}/"
f"{ss.get_sequence().get_name()}"),
"frame_range": (
ss.get_start_frame(), ss.get_end_frame() - 1)
})
else:
# Avoid creating instances for camera sequences
if "_camera" not in s.get('sequence').get_name():
seq = s.get('sequence')
seq_name = seq.get_name()
new_instance = context.create_instance(
f"{data.get('subset')}_"
f"{seq_name}")
new_instance[:] = seq_name
new_data = new_instance.data
new_data["asset"] = seq_name
new_data["setMembers"] = seq_name
new_data["family"] = "render"
new_data["families"] = ["render", "review"]
new_data["parent"] = data.get("parent")
new_data["subset"] = f"{data.get('subset')}_{seq_name}"
new_data["level"] = data.get("level")
new_data["output"] = s.get('output')
new_data["fps"] = seq.get_display_rate().numerator
new_data["frameStart"] = s.get('frame_range')[0]
new_data["frameEnd"] = s.get('frame_range')[1]
new_data["sequence"] = seq.get_path_name()
new_data["master_sequence"] = data["master_sequence"]
new_data["master_level"] = data["master_level"]
self.log.debug(f"new instance data: {new_data}")
project_dir = unreal.Paths.project_dir()
render_dir = (f"{project_dir}/Saved/MovieRenders/"
f"{s.get('output')}")
render_path = Path(render_dir)
frames = []
for x in render_path.iterdir():
if x.is_file() and x.suffix == '.png':
frames.append(str(x.name))
if "representations" not in new_instance.data:
new_instance.data["representations"] = []
repr = {
'frameStart': s.get('frame_range')[0],
'frameEnd': s.get('frame_range')[1],
'name': 'png',
'ext': 'png',
'files': frames,
'stagingDir': render_dir,
'tags': ['review']
}
new_instance.data["representations"].append(repr)

View file

@ -0,0 +1,45 @@
from pathlib import Path
import unreal
import pyblish.api
class ValidateSequenceFrames(pyblish.api.InstancePlugin):
"""Ensure the sequence of frames is complete
The files found in the folder are checked against the frameStart and
frameEnd of the instance. If the first or last file is not
corresponding with the first or last frame it is flagged as invalid.
"""
order = pyblish.api.ValidatorOrder
label = "Validate Sequence Frames"
families = ["render"]
hosts = ["unreal"]
optional = True
def process(self, instance):
self.log.debug(instance.data)
representations = instance.data.get("representations")
for repr in representations:
frames = []
for x in repr.get("files"):
# Get frame number. The last one contains the file extension,
# while the one before that is the frame number.
# `lstrip` removes any leading zeros. `or "0"` is to tackle
# the case where the frame number is "00".
frame = int(str(x).split('.')[-2])
frames.append(frame)
frames.sort()
current_range = (frames[0], frames[-1])
required_range = (instance.data["frameStart"],
instance.data["frameEnd"])
if current_range != required_range:
raise ValueError(f"Invalid frame range: {current_range} - "
f"expected: {required_range}")
assert len(frames) == int(frames[-1]) - int(frames[0]) + 1, \
"Missing frames"

View file

@ -51,7 +51,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
"resolve",
"webpublisher",
"aftereffects",
"flame"
"flame",
"unreal"
]
# Supported extensions