mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-26 05:42:15 +01:00
Implemented AOVs rendering in deadline and publishing
This commit is contained in:
parent
eb1b5425de
commit
9f56721334
3 changed files with 122 additions and 25 deletions
52
openpype/hosts/blender/api/colorspace.py
Normal file
52
openpype/hosts/blender/api/colorspace.py
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
import attr
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
@attr.s
|
||||
class LayerMetadata(object):
|
||||
"""Data class for Render Layer metadata."""
|
||||
frameStart = attr.ib()
|
||||
frameEnd = attr.ib()
|
||||
|
||||
|
||||
@attr.s
|
||||
class RenderProduct(object):
|
||||
"""Getting Colorspace as
|
||||
Specific Render Product Parameter for submitting
|
||||
publish job.
|
||||
"""
|
||||
colorspace = attr.ib() # colorspace
|
||||
view = attr.ib()
|
||||
productName = attr.ib(default=None)
|
||||
|
||||
|
||||
class ARenderProduct(object):
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor."""
|
||||
# Initialize
|
||||
self.layer_data = self._get_layer_data()
|
||||
self.layer_data.products = self.get_colorspace_data()
|
||||
|
||||
def _get_layer_data(self):
|
||||
scene = bpy.context.scene
|
||||
|
||||
return LayerMetadata(
|
||||
frameStart=int(scene.frame_start),
|
||||
frameEnd=int(scene.frame_end),
|
||||
)
|
||||
|
||||
def get_colorspace_data(self):
|
||||
"""To be implemented by renderer class.
|
||||
This should return a list of RenderProducts.
|
||||
Returns:
|
||||
list: List of RenderProduct
|
||||
"""
|
||||
return [
|
||||
RenderProduct(
|
||||
colorspace="sRGB",
|
||||
view="ACES 1.0",
|
||||
productName=""
|
||||
)
|
||||
]
|
||||
|
|
@ -12,10 +12,7 @@ from openpype.pipeline import (
|
|||
from openpype.settings import (
|
||||
get_project_settings,
|
||||
)
|
||||
from openpype.hosts.blender.api.ops import (
|
||||
MainThreadItem,
|
||||
execute_in_main_thread
|
||||
)
|
||||
from openpype.hosts.blender.api import colorspace
|
||||
import pyblish.api
|
||||
|
||||
|
||||
|
|
@ -73,12 +70,13 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
return render_product
|
||||
|
||||
@staticmethod
|
||||
def generate_expected_files(
|
||||
render_product, frame_start, frame_end, frame_step
|
||||
def generate_expected_beauty(
|
||||
render_product, frame_start, frame_end, frame_step, ext
|
||||
):
|
||||
"""Generate the expected files for the render product.
|
||||
This returns a list of files that should be rendered. It replaces
|
||||
the sequence of `#` with the frame number.
|
||||
"""
|
||||
Generate the expected files for the render product for the beauty
|
||||
render. This returns a list of files that should be rendered. It
|
||||
replaces the sequence of `#` with the frame number.
|
||||
"""
|
||||
path = os.path.dirname(render_product)
|
||||
file = os.path.basename(render_product)
|
||||
|
|
@ -87,9 +85,39 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
|
||||
for frame in range(frame_start, frame_end + 1, frame_step):
|
||||
frame_str = str(frame).rjust(4, "0")
|
||||
expected_file = os.path.join(path, re.sub("#+", frame_str, file))
|
||||
filename = re.sub("#+", frame_str, file)
|
||||
expected_file = f"{os.path.join(path, filename)}.{ext}"
|
||||
expected_files.append(expected_file.replace("\\", "/"))
|
||||
|
||||
return {
|
||||
"beauty": expected_files
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def generate_expected_aovs(
|
||||
aov_file_product, frame_start, frame_end, frame_step, ext
|
||||
):
|
||||
"""
|
||||
Generate the expected files for the render product for the beauty
|
||||
render. This returns a list of files that should be rendered. It
|
||||
replaces the sequence of `#` with the frame number.
|
||||
"""
|
||||
expected_files = {}
|
||||
|
||||
for aov_name, aov_file in aov_file_product:
|
||||
path = os.path.dirname(aov_file)
|
||||
file = os.path.basename(aov_file)
|
||||
|
||||
aov_files = []
|
||||
|
||||
for frame in range(frame_start, frame_end + 1, frame_step):
|
||||
frame_str = str(frame).rjust(4, "0")
|
||||
filename = re.sub("#+", frame_str, file)
|
||||
expected_file = f"{os.path.join(path, filename)}.{ext}"
|
||||
aov_files.append(expected_file.replace("\\", "/"))
|
||||
|
||||
expected_files[aov_name] = aov_files
|
||||
|
||||
return expected_files
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -117,7 +145,7 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
elif ext == "tif":
|
||||
image_settings.file_format = "TIFF"
|
||||
|
||||
def _create_context():
|
||||
def _create_context(self):
|
||||
context = bpy.context.copy()
|
||||
|
||||
win = bpy.context.window_manager.windows[0]
|
||||
|
|
@ -132,7 +160,7 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
|
||||
return context
|
||||
|
||||
def _set_node_tree(self, output_path, instance):
|
||||
def set_node_tree(self, output_path, instance):
|
||||
# Set the scene to use the compositor node tree to render
|
||||
bpy.context.scene.use_nodes = True
|
||||
|
||||
|
|
@ -153,8 +181,9 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
# render.
|
||||
# We also exclude some layers.
|
||||
exclude_sockets = ["Image", "Alpha"]
|
||||
passes = [
|
||||
socket for socket in rl_node.outputs
|
||||
passes = [
|
||||
socket
|
||||
for socket in rl_node.outputs
|
||||
if socket.enabled and socket.name not in exclude_sockets
|
||||
]
|
||||
|
||||
|
|
@ -182,11 +211,16 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
image_settings = bpy.context.scene.render.image_settings
|
||||
output.format.file_format = image_settings.file_format
|
||||
|
||||
aov_file_products = []
|
||||
|
||||
# For each active render pass, we add a new socket to the output node
|
||||
# and link it
|
||||
for render_pass in passes:
|
||||
bpy.ops.node.output_file_add_socket(
|
||||
context, file_path=f"{instance.name}_{render_pass.name}.####")
|
||||
filepath = f"{instance.name}_{render_pass.name}.####"
|
||||
bpy.ops.node.output_file_add_socket(context, file_path=filepath)
|
||||
|
||||
aov_file_products.append(
|
||||
(render_pass.name, os.path.join(output_path, filepath)))
|
||||
|
||||
node_input = output.inputs[-1]
|
||||
|
||||
|
|
@ -195,10 +229,7 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
# Restore the area type
|
||||
area.ui_type = old_area_type
|
||||
|
||||
def set_node_tree(self, output_path, instance):
|
||||
""" Run the creator on Blender main thread"""
|
||||
mti = MainThreadItem(self._set_node_tree, output_path, instance)
|
||||
execute_in_main_thread(mti)
|
||||
return aov_file_products
|
||||
|
||||
@staticmethod
|
||||
def set_render_camera(instance):
|
||||
|
|
@ -230,7 +261,7 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
output_path = os.path.join(file_path, render_folder, file_name)
|
||||
|
||||
render_product = self.get_render_product(output_path, instance)
|
||||
self.set_node_tree(output_path, instance)
|
||||
aov_file_product = self.set_node_tree(output_path, instance)
|
||||
|
||||
# We set the render path, the format and the camera
|
||||
bpy.context.scene.render.filepath = render_product
|
||||
|
|
@ -245,9 +276,15 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
frame_handle_start = context.data["frameStartHandle"]
|
||||
frame_handle_end = context.data["frameEndHandle"]
|
||||
|
||||
expected_files = self.generate_expected_files(
|
||||
expected_beauty = self.generate_expected_beauty(
|
||||
render_product, int(frame_start), int(frame_end),
|
||||
int(bpy.context.scene.frame_step))
|
||||
int(bpy.context.scene.frame_step), ext)
|
||||
|
||||
expected_aovs = self.generate_expected_aovs(
|
||||
aov_file_product, int(frame_start), int(frame_end),
|
||||
int(bpy.context.scene.frame_step), ext)
|
||||
|
||||
expected_files = expected_beauty | expected_aovs
|
||||
|
||||
instance.data.update({
|
||||
"frameStart": frame_start,
|
||||
|
|
@ -257,7 +294,14 @@ class CollectBlenderRender(pyblish.api.InstancePlugin):
|
|||
"fps": context.data["fps"],
|
||||
"byFrameStep": bpy.context.scene.frame_step,
|
||||
"farm": True,
|
||||
"expectedFiles": expected_files,
|
||||
"expectedFiles": [expected_files],
|
||||
# OCIO not currently implemented in Blender, but the following
|
||||
# settings are required by the schema, so it is hardcoded.
|
||||
# TODO: Implement OCIO in Blender
|
||||
"colorspaceConfig": "",
|
||||
"colorspaceDisplay": "sRGB",
|
||||
"colorspaceView": "ACES 1.0 SDR-video",
|
||||
"renderProducts": colorspace.ARenderProduct(),
|
||||
})
|
||||
|
||||
self.log.info(f"data: {instance.data}")
|
||||
|
|
|
|||
|
|
@ -163,7 +163,8 @@ class BlenderSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
|
|||
if not expected_files:
|
||||
raise RuntimeError("No Render Elements found!")
|
||||
|
||||
output_dir = os.path.dirname(expected_files[0])
|
||||
first_file = next(iter_expected_files(expected_files))
|
||||
output_dir = os.path.dirname(first_file)
|
||||
instance.data["outputDir"] = output_dir
|
||||
instance.data["toBeRenderedOn"] = "deadline"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue