mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
Merge pull request #5124 from ynput/enhancement/OP-5751_render-multiple-cameras
multiple render camera supports for 3dsmax
This commit is contained in:
commit
837acf9b2e
7 changed files with 458 additions and 39 deletions
|
|
@ -37,6 +37,95 @@ class RenderProducts(object):
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_multiple_beauty(self, outputs, cameras):
|
||||||
|
beauty_output_frames = dict()
|
||||||
|
for output, camera in zip(outputs, cameras):
|
||||||
|
filename, ext = os.path.splitext(output)
|
||||||
|
filename = filename.replace(".", "")
|
||||||
|
ext = ext.replace(".", "")
|
||||||
|
start_frame = int(rt.rendStart)
|
||||||
|
end_frame = int(rt.rendEnd) + 1
|
||||||
|
new_beauty = self.get_expected_beauty(
|
||||||
|
filename, start_frame, end_frame, ext
|
||||||
|
)
|
||||||
|
beauty_output = ({
|
||||||
|
f"{camera}_beauty": new_beauty
|
||||||
|
})
|
||||||
|
beauty_output_frames.update(beauty_output)
|
||||||
|
return beauty_output_frames
|
||||||
|
|
||||||
|
def get_multiple_aovs(self, outputs, cameras):
|
||||||
|
renderer_class = get_current_renderer()
|
||||||
|
renderer = str(renderer_class).split(":")[0]
|
||||||
|
aovs_frames = {}
|
||||||
|
for output, camera in zip(outputs, cameras):
|
||||||
|
filename, ext = os.path.splitext(output)
|
||||||
|
filename = filename.replace(".", "")
|
||||||
|
ext = ext.replace(".", "")
|
||||||
|
start_frame = int(rt.rendStart)
|
||||||
|
end_frame = int(rt.rendEnd) + 1
|
||||||
|
|
||||||
|
if renderer in [
|
||||||
|
"ART_Renderer",
|
||||||
|
"V_Ray_6_Hotfix_3",
|
||||||
|
"V_Ray_GPU_6_Hotfix_3",
|
||||||
|
"Default_Scanline_Renderer",
|
||||||
|
"Quicksilver_Hardware_Renderer",
|
||||||
|
]:
|
||||||
|
render_name = self.get_render_elements_name()
|
||||||
|
if render_name:
|
||||||
|
for name in render_name:
|
||||||
|
aovs_frames.update({
|
||||||
|
f"{camera}_{name}": self.get_expected_aovs(
|
||||||
|
filename, name, start_frame,
|
||||||
|
end_frame, ext)
|
||||||
|
})
|
||||||
|
elif renderer == "Redshift_Renderer":
|
||||||
|
render_name = self.get_render_elements_name()
|
||||||
|
if render_name:
|
||||||
|
rs_aov_files = rt.Execute("renderers.current.separateAovFiles") # noqa
|
||||||
|
# this doesn't work, always returns False
|
||||||
|
# rs_AovFiles = rt.RedShift_Renderer().separateAovFiles
|
||||||
|
if ext == "exr" and not rs_aov_files:
|
||||||
|
for name in render_name:
|
||||||
|
if name == "RsCryptomatte":
|
||||||
|
aovs_frames.update({
|
||||||
|
f"{camera}_{name}": self.get_expected_aovs(
|
||||||
|
filename, name, start_frame,
|
||||||
|
end_frame, ext)
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
for name in render_name:
|
||||||
|
aovs_frames.update({
|
||||||
|
f"{camera}_{name}": self.get_expected_aovs(
|
||||||
|
filename, name, start_frame,
|
||||||
|
end_frame, ext)
|
||||||
|
})
|
||||||
|
elif renderer == "Arnold":
|
||||||
|
render_name = self.get_arnold_product_name()
|
||||||
|
if render_name:
|
||||||
|
for name in render_name:
|
||||||
|
aovs_frames.update({
|
||||||
|
f"{camera}_{name}": self.get_expected_arnold_product( # noqa
|
||||||
|
filename, name, start_frame,
|
||||||
|
end_frame, ext)
|
||||||
|
})
|
||||||
|
elif renderer in [
|
||||||
|
"V_Ray_6_Hotfix_3",
|
||||||
|
"V_Ray_GPU_6_Hotfix_3"
|
||||||
|
]:
|
||||||
|
if ext != "exr":
|
||||||
|
render_name = self.get_render_elements_name()
|
||||||
|
if render_name:
|
||||||
|
for name in render_name:
|
||||||
|
aovs_frames.update({
|
||||||
|
f"{camera}_{name}": self.get_expected_aovs(
|
||||||
|
filename, name, start_frame,
|
||||||
|
end_frame, ext)
|
||||||
|
})
|
||||||
|
|
||||||
|
return aovs_frames
|
||||||
|
|
||||||
def get_aovs(self, container):
|
def get_aovs(self, container):
|
||||||
render_dir = os.path.dirname(rt.rendOutputFilename)
|
render_dir = os.path.dirname(rt.rendOutputFilename)
|
||||||
|
|
||||||
|
|
@ -63,7 +152,7 @@ class RenderProducts(object):
|
||||||
if render_name:
|
if render_name:
|
||||||
for name in render_name:
|
for name in render_name:
|
||||||
render_dict.update({
|
render_dict.update({
|
||||||
name: self.get_expected_render_elements(
|
name: self.get_expected_aovs(
|
||||||
output_file, name, start_frame,
|
output_file, name, start_frame,
|
||||||
end_frame, img_fmt)
|
end_frame, img_fmt)
|
||||||
})
|
})
|
||||||
|
|
@ -77,14 +166,14 @@ class RenderProducts(object):
|
||||||
for name in render_name:
|
for name in render_name:
|
||||||
if name == "RsCryptomatte":
|
if name == "RsCryptomatte":
|
||||||
render_dict.update({
|
render_dict.update({
|
||||||
name: self.get_expected_render_elements(
|
name: self.get_expected_aovs(
|
||||||
output_file, name, start_frame,
|
output_file, name, start_frame,
|
||||||
end_frame, img_fmt)
|
end_frame, img_fmt)
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
for name in render_name:
|
for name in render_name:
|
||||||
render_dict.update({
|
render_dict.update({
|
||||||
name: self.get_expected_render_elements(
|
name: self.get_expected_aovs(
|
||||||
output_file, name, start_frame,
|
output_file, name, start_frame,
|
||||||
end_frame, img_fmt)
|
end_frame, img_fmt)
|
||||||
})
|
})
|
||||||
|
|
@ -95,7 +184,8 @@ class RenderProducts(object):
|
||||||
for name in render_name:
|
for name in render_name:
|
||||||
render_dict.update({
|
render_dict.update({
|
||||||
name: self.get_expected_arnold_product(
|
name: self.get_expected_arnold_product(
|
||||||
output_file, name, start_frame, end_frame, img_fmt)
|
output_file, name, start_frame,
|
||||||
|
end_frame, img_fmt)
|
||||||
})
|
})
|
||||||
elif renderer in [
|
elif renderer in [
|
||||||
"V_Ray_6_Hotfix_3",
|
"V_Ray_6_Hotfix_3",
|
||||||
|
|
@ -106,7 +196,7 @@ class RenderProducts(object):
|
||||||
if render_name:
|
if render_name:
|
||||||
for name in render_name:
|
for name in render_name:
|
||||||
render_dict.update({
|
render_dict.update({
|
||||||
name: self.get_expected_render_elements(
|
name: self.get_expected_aovs(
|
||||||
output_file, name, start_frame,
|
output_file, name, start_frame,
|
||||||
end_frame, img_fmt) # noqa
|
end_frame, img_fmt) # noqa
|
||||||
})
|
})
|
||||||
|
|
@ -169,8 +259,8 @@ class RenderProducts(object):
|
||||||
|
|
||||||
return render_name
|
return render_name
|
||||||
|
|
||||||
def get_expected_render_elements(self, folder, name,
|
def get_expected_aovs(self, folder, name,
|
||||||
start_frame, end_frame, fmt):
|
start_frame, end_frame, fmt):
|
||||||
"""Get all the expected render element output files. """
|
"""Get all the expected render element output files. """
|
||||||
render_elements = []
|
render_elements = []
|
||||||
for f in range(start_frame, end_frame):
|
for f in range(start_frame, end_frame):
|
||||||
|
|
|
||||||
|
|
@ -74,13 +74,13 @@ class RenderSettings(object):
|
||||||
output = os.path.join(output_dir, container)
|
output = os.path.join(output_dir, container)
|
||||||
try:
|
try:
|
||||||
aov_separator = self._aov_chars[(
|
aov_separator = self._aov_chars[(
|
||||||
self._project_settings["maya"]
|
self._project_settings["max"]
|
||||||
["RenderSettings"]
|
["RenderSettings"]
|
||||||
["aov_separator"]
|
["aov_separator"]
|
||||||
)]
|
)]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
aov_separator = "."
|
aov_separator = "."
|
||||||
output_filename = "{0}..{1}".format(output, img_fmt)
|
output_filename = f"{output}..{img_fmt}"
|
||||||
output_filename = output_filename.replace("{aov_separator}",
|
output_filename = output_filename.replace("{aov_separator}",
|
||||||
aov_separator)
|
aov_separator)
|
||||||
rt.rendOutputFilename = output_filename
|
rt.rendOutputFilename = output_filename
|
||||||
|
|
@ -146,13 +146,13 @@ class RenderSettings(object):
|
||||||
for i in range(render_elem_num):
|
for i in range(render_elem_num):
|
||||||
renderlayer_name = render_elem.GetRenderElement(i)
|
renderlayer_name = render_elem.GetRenderElement(i)
|
||||||
target, renderpass = str(renderlayer_name).split(":")
|
target, renderpass = str(renderlayer_name).split(":")
|
||||||
aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext)
|
aov_name = f"{dir}_{renderpass}..{ext}"
|
||||||
render_elem.SetRenderElementFileName(i, aov_name)
|
render_elem.SetRenderElementFileName(i, aov_name)
|
||||||
|
|
||||||
def get_render_output(self, container, output_dir):
|
def get_render_output(self, container, output_dir):
|
||||||
output = os.path.join(output_dir, container)
|
output = os.path.join(output_dir, container)
|
||||||
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
|
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
|
||||||
output_filename = "{0}..{1}".format(output, img_fmt)
|
output_filename = f"{output}..{img_fmt}"
|
||||||
return output_filename
|
return output_filename
|
||||||
|
|
||||||
def get_render_element(self):
|
def get_render_element(self):
|
||||||
|
|
@ -167,3 +167,61 @@ class RenderSettings(object):
|
||||||
orig_render_elem.append(render_element)
|
orig_render_elem.append(render_element)
|
||||||
|
|
||||||
return orig_render_elem
|
return orig_render_elem
|
||||||
|
|
||||||
|
def get_batch_render_elements(self, container,
|
||||||
|
output_dir, camera):
|
||||||
|
render_element_list = list()
|
||||||
|
output = os.path.join(output_dir, container)
|
||||||
|
render_elem = rt.maxOps.GetCurRenderElementMgr()
|
||||||
|
render_elem_num = render_elem.NumRenderElements()
|
||||||
|
if render_elem_num < 0:
|
||||||
|
return
|
||||||
|
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
|
||||||
|
|
||||||
|
for i in range(render_elem_num):
|
||||||
|
renderlayer_name = render_elem.GetRenderElement(i)
|
||||||
|
target, renderpass = str(renderlayer_name).split(":")
|
||||||
|
aov_name = f"{output}_{camera}_{renderpass}..{img_fmt}"
|
||||||
|
render_element_list.append(aov_name)
|
||||||
|
return render_element_list
|
||||||
|
|
||||||
|
def get_batch_render_output(self, camera):
|
||||||
|
target_layer_no = rt.batchRenderMgr.FindView(camera)
|
||||||
|
target_layer = rt.batchRenderMgr.GetView(target_layer_no)
|
||||||
|
return target_layer.outputFilename
|
||||||
|
|
||||||
|
def batch_render_elements(self, camera):
|
||||||
|
target_layer_no = rt.batchRenderMgr.FindView(camera)
|
||||||
|
target_layer = rt.batchRenderMgr.GetView(target_layer_no)
|
||||||
|
outputfilename = target_layer.outputFilename
|
||||||
|
directory = os.path.dirname(outputfilename)
|
||||||
|
render_elem = rt.maxOps.GetCurRenderElementMgr()
|
||||||
|
render_elem_num = render_elem.NumRenderElements()
|
||||||
|
if render_elem_num < 0:
|
||||||
|
return
|
||||||
|
ext = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
|
||||||
|
|
||||||
|
for i in range(render_elem_num):
|
||||||
|
renderlayer_name = render_elem.GetRenderElement(i)
|
||||||
|
target, renderpass = str(renderlayer_name).split(":")
|
||||||
|
aov_name = f"{directory}_{camera}_{renderpass}..{ext}"
|
||||||
|
render_elem.SetRenderElementFileName(i, aov_name)
|
||||||
|
|
||||||
|
def batch_render_layer(self, container,
|
||||||
|
output_dir, cameras):
|
||||||
|
outputs = list()
|
||||||
|
output = os.path.join(output_dir, container)
|
||||||
|
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
|
||||||
|
for cam in cameras:
|
||||||
|
camera = rt.getNodeByName(cam)
|
||||||
|
layer_no = rt.batchRenderMgr.FindView(cam)
|
||||||
|
renderlayer = None
|
||||||
|
if layer_no == 0:
|
||||||
|
renderlayer = rt.batchRenderMgr.CreateView(camera)
|
||||||
|
else:
|
||||||
|
renderlayer = rt.batchRenderMgr.GetView(layer_no)
|
||||||
|
# use camera name as renderlayer name
|
||||||
|
renderlayer.name = cam
|
||||||
|
renderlayer.outputFilename = f"{output}_{cam}..{img_fmt}"
|
||||||
|
outputs.append(renderlayer.outputFilename)
|
||||||
|
return outputs
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
"""Creator plugin for creating camera."""
|
"""Creator plugin for creating camera."""
|
||||||
import os
|
import os
|
||||||
from openpype.hosts.max.api import plugin
|
from openpype.hosts.max.api import plugin
|
||||||
|
from openpype.lib import BoolDef
|
||||||
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -17,15 +18,33 @@ class CreateRender(plugin.MaxCreator):
|
||||||
file = rt.maxFileName
|
file = rt.maxFileName
|
||||||
filename, _ = os.path.splitext(file)
|
filename, _ = os.path.splitext(file)
|
||||||
instance_data["AssetName"] = filename
|
instance_data["AssetName"] = filename
|
||||||
|
instance_data["multiCamera"] = pre_create_data.get("multi_cam")
|
||||||
|
num_of_renderlayer = rt.batchRenderMgr.numViews
|
||||||
|
if num_of_renderlayer > 0:
|
||||||
|
rt.batchRenderMgr.DeleteView(num_of_renderlayer)
|
||||||
|
|
||||||
instance = super(CreateRender, self).create(
|
instance = super(CreateRender, self).create(
|
||||||
subset_name,
|
subset_name,
|
||||||
instance_data,
|
instance_data,
|
||||||
pre_create_data)
|
pre_create_data)
|
||||||
|
|
||||||
container_name = instance.data.get("instance_node")
|
container_name = instance.data.get("instance_node")
|
||||||
sel_obj = self.selected_nodes
|
|
||||||
if sel_obj:
|
|
||||||
# set viewport camera for rendering(mandatory for deadline)
|
|
||||||
RenderSettings(self.project_settings).set_render_camera(sel_obj)
|
|
||||||
# set output paths for rendering(mandatory for deadline)
|
# set output paths for rendering(mandatory for deadline)
|
||||||
RenderSettings().render_output(container_name)
|
RenderSettings().render_output(container_name)
|
||||||
|
# TODO: create multiple camera options
|
||||||
|
if self.selected_nodes:
|
||||||
|
selected_nodes_name = []
|
||||||
|
for sel in self.selected_nodes:
|
||||||
|
name = sel.name
|
||||||
|
selected_nodes_name.append(name)
|
||||||
|
RenderSettings().batch_render_layer(
|
||||||
|
container_name, filename,
|
||||||
|
selected_nodes_name)
|
||||||
|
|
||||||
|
def get_pre_create_attr_defs(self):
|
||||||
|
attrs = super(CreateRender, self).get_pre_create_attr_defs()
|
||||||
|
return attrs + [
|
||||||
|
BoolDef("multi_cam",
|
||||||
|
label="Multiple Cameras Submission",
|
||||||
|
default=False),
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ import os
|
||||||
import pyblish.api
|
import pyblish.api
|
||||||
|
|
||||||
from pymxs import runtime as rt
|
from pymxs import runtime as rt
|
||||||
|
from openpype.pipeline.publish import KnownPublishError
|
||||||
from openpype.hosts.max.api import colorspace
|
from openpype.hosts.max.api import colorspace
|
||||||
from openpype.hosts.max.api.lib import get_max_version, get_current_renderer
|
from openpype.hosts.max.api.lib import get_max_version, get_current_renderer
|
||||||
|
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||||
from openpype.hosts.max.api.lib_renderproducts import RenderProducts
|
from openpype.hosts.max.api.lib_renderproducts import RenderProducts
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -23,7 +25,6 @@ class CollectRender(pyblish.api.InstancePlugin):
|
||||||
file = rt.maxFileName
|
file = rt.maxFileName
|
||||||
current_file = os.path.join(folder, file)
|
current_file = os.path.join(folder, file)
|
||||||
filepath = current_file.replace("\\", "/")
|
filepath = current_file.replace("\\", "/")
|
||||||
|
|
||||||
context.data['currentFile'] = current_file
|
context.data['currentFile'] = current_file
|
||||||
|
|
||||||
files_by_aov = RenderProducts().get_beauty(instance.name)
|
files_by_aov = RenderProducts().get_beauty(instance.name)
|
||||||
|
|
@ -39,6 +40,28 @@ class CollectRender(pyblish.api.InstancePlugin):
|
||||||
|
|
||||||
instance.data["cameras"] = [camera.name] if camera else None # noqa
|
instance.data["cameras"] = [camera.name] if camera else None # noqa
|
||||||
|
|
||||||
|
if instance.data.get("multiCamera"):
|
||||||
|
cameras = instance.data.get("members")
|
||||||
|
if not cameras:
|
||||||
|
raise KnownPublishError("There should be at least"
|
||||||
|
" one renderable camera in container")
|
||||||
|
sel_cam = [
|
||||||
|
c.name for c in cameras
|
||||||
|
if rt.classOf(c) in rt.Camera.classes]
|
||||||
|
container_name = instance.data.get("instance_node")
|
||||||
|
render_dir = os.path.dirname(rt.rendOutputFilename)
|
||||||
|
outputs = RenderSettings().batch_render_layer(
|
||||||
|
container_name, render_dir, sel_cam
|
||||||
|
)
|
||||||
|
|
||||||
|
instance.data["cameras"] = sel_cam
|
||||||
|
|
||||||
|
files_by_aov = RenderProducts().get_multiple_beauty(
|
||||||
|
outputs, sel_cam)
|
||||||
|
aovs = RenderProducts().get_multiple_aovs(
|
||||||
|
outputs, sel_cam)
|
||||||
|
files_by_aov.update(aovs)
|
||||||
|
|
||||||
if "expectedFiles" not in instance.data:
|
if "expectedFiles" not in instance.data:
|
||||||
instance.data["expectedFiles"] = list()
|
instance.data["expectedFiles"] = list()
|
||||||
instance.data["files"] = list()
|
instance.data["files"] = list()
|
||||||
|
|
|
||||||
100
openpype/hosts/max/plugins/publish/save_scenes_for_cameras.py
Normal file
100
openpype/hosts/max/plugins/publish/save_scenes_for_cameras.py
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
import pyblish.api
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from pymxs import runtime as rt
|
||||||
|
from openpype.lib import run_subprocess
|
||||||
|
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||||
|
from openpype.hosts.max.api.lib_renderproducts import RenderProducts
|
||||||
|
|
||||||
|
|
||||||
|
class SaveScenesForCamera(pyblish.api.InstancePlugin):
|
||||||
|
"""Save scene files for multiple cameras without
|
||||||
|
editing the original scene before deadline submission
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
label = "Save Scene files for cameras"
|
||||||
|
order = pyblish.api.ExtractorOrder - 0.48
|
||||||
|
hosts = ["max"]
|
||||||
|
families = ["maxrender"]
|
||||||
|
|
||||||
|
def process(self, instance):
|
||||||
|
current_folder = rt.maxFilePath
|
||||||
|
current_filename = rt.maxFileName
|
||||||
|
current_filepath = os.path.join(current_folder, current_filename)
|
||||||
|
camera_scene_files = []
|
||||||
|
scripts = []
|
||||||
|
filename, ext = os.path.splitext(current_filename)
|
||||||
|
fmt = RenderProducts().image_format()
|
||||||
|
cameras = instance.data.get("cameras")
|
||||||
|
if not cameras:
|
||||||
|
return
|
||||||
|
new_folder = f"{current_folder}_{filename}"
|
||||||
|
os.makedirs(new_folder, exist_ok=True)
|
||||||
|
for camera in cameras:
|
||||||
|
new_output = RenderSettings().get_batch_render_output(camera) # noqa
|
||||||
|
new_output = new_output.replace("\\", "/")
|
||||||
|
new_filename = f"{filename}_{camera}{ext}"
|
||||||
|
new_filepath = os.path.join(new_folder, new_filename)
|
||||||
|
new_filepath = new_filepath.replace("\\", "/")
|
||||||
|
camera_scene_files.append(new_filepath)
|
||||||
|
RenderSettings().batch_render_elements(camera)
|
||||||
|
rt.rendOutputFilename = new_output
|
||||||
|
rt.saveMaxFile(current_filepath)
|
||||||
|
script = ("""
|
||||||
|
from pymxs import runtime as rt
|
||||||
|
import os
|
||||||
|
filename = "{filename}"
|
||||||
|
new_filepath = "{new_filepath}"
|
||||||
|
new_output = "{new_output}"
|
||||||
|
camera = "{camera}"
|
||||||
|
rt.rendOutputFilename = new_output
|
||||||
|
directory = os.path.dirname(rt.rendOutputFilename)
|
||||||
|
directory = os.path.join(directory, filename)
|
||||||
|
render_elem = rt.maxOps.GetCurRenderElementMgr()
|
||||||
|
render_elem_num = render_elem.NumRenderElements()
|
||||||
|
if render_elem_num > 0:
|
||||||
|
ext = "{ext}"
|
||||||
|
for i in range(render_elem_num):
|
||||||
|
renderlayer_name = render_elem.GetRenderElement(i)
|
||||||
|
target, renderpass = str(renderlayer_name).split(":")
|
||||||
|
aov_name = f"{{directory}}_{camera}_{{renderpass}}..{ext}"
|
||||||
|
render_elem.SetRenderElementFileName(i, aov_name)
|
||||||
|
rt.saveMaxFile(new_filepath)
|
||||||
|
""").format(filename=instance.name,
|
||||||
|
new_filepath=new_filepath,
|
||||||
|
new_output=new_output,
|
||||||
|
camera=camera,
|
||||||
|
ext=fmt)
|
||||||
|
scripts.append(script)
|
||||||
|
|
||||||
|
maxbatch_exe = os.path.join(
|
||||||
|
os.path.dirname(sys.executable), "3dsmaxbatch")
|
||||||
|
maxbatch_exe = maxbatch_exe.replace("\\", "/")
|
||||||
|
if sys.platform == "windows":
|
||||||
|
maxbatch_exe += ".exe"
|
||||||
|
maxbatch_exe = os.path.normpath(maxbatch_exe)
|
||||||
|
with tempfile.TemporaryDirectory() as tmp_dir_name:
|
||||||
|
tmp_script_path = os.path.join(
|
||||||
|
tmp_dir_name, "extract_scene_files.py")
|
||||||
|
self.log.info("Using script file: {}".format(tmp_script_path))
|
||||||
|
|
||||||
|
with open(tmp_script_path, "wt") as tmp:
|
||||||
|
for script in scripts:
|
||||||
|
tmp.write(script + "\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
current_filepath = current_filepath.replace("\\", "/")
|
||||||
|
tmp_script_path = tmp_script_path.replace("\\", "/")
|
||||||
|
run_subprocess([maxbatch_exe, tmp_script_path,
|
||||||
|
"-sceneFile", current_filepath])
|
||||||
|
except RuntimeError:
|
||||||
|
self.log.debug("Checking the scene files existing")
|
||||||
|
|
||||||
|
for camera_scene in camera_scene_files:
|
||||||
|
if not os.path.exists(camera_scene):
|
||||||
|
self.log.error("Camera scene files not existed yet!")
|
||||||
|
raise RuntimeError("MaxBatch.exe doesn't run as expected")
|
||||||
|
self.log.debug(f"Found Camera scene:{camera_scene}")
|
||||||
|
|
@ -15,6 +15,12 @@ from openpype.pipeline import (
|
||||||
from openpype.pipeline.publish.lib import (
|
from openpype.pipeline.publish.lib import (
|
||||||
replace_with_published_scene_path
|
replace_with_published_scene_path
|
||||||
)
|
)
|
||||||
|
from openpype.pipeline.publish import KnownPublishError
|
||||||
|
from openpype.hosts.max.api.lib import (
|
||||||
|
get_current_renderer,
|
||||||
|
get_multipass_setting
|
||||||
|
)
|
||||||
|
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
|
||||||
from openpype_modules.deadline import abstract_submit_deadline
|
from openpype_modules.deadline import abstract_submit_deadline
|
||||||
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
from openpype_modules.deadline.abstract_submit_deadline import DeadlineJobInfo
|
||||||
from openpype.lib import is_running_from_build
|
from openpype.lib import is_running_from_build
|
||||||
|
|
@ -54,7 +60,7 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
cls.priority)
|
cls.priority)
|
||||||
cls.chuck_size = settings.get("chunk_size", cls.chunk_size)
|
cls.chuck_size = settings.get("chunk_size", cls.chunk_size)
|
||||||
cls.group = settings.get("group", cls.group)
|
cls.group = settings.get("group", cls.group)
|
||||||
|
# TODO: multiple camera instance, separate job infos
|
||||||
def get_job_info(self):
|
def get_job_info(self):
|
||||||
job_info = DeadlineJobInfo(Plugin="3dsmax")
|
job_info = DeadlineJobInfo(Plugin="3dsmax")
|
||||||
|
|
||||||
|
|
@ -71,7 +77,6 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
|
|
||||||
src_filepath = context.data["currentFile"]
|
src_filepath = context.data["currentFile"]
|
||||||
src_filename = os.path.basename(src_filepath)
|
src_filename = os.path.basename(src_filepath)
|
||||||
|
|
||||||
job_info.Name = "%s - %s" % (src_filename, instance.name)
|
job_info.Name = "%s - %s" % (src_filename, instance.name)
|
||||||
job_info.BatchName = src_filename
|
job_info.BatchName = src_filename
|
||||||
job_info.Plugin = instance.data["plugin"]
|
job_info.Plugin = instance.data["plugin"]
|
||||||
|
|
@ -134,11 +139,11 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
|
|
||||||
# Add list of expected files to job
|
# Add list of expected files to job
|
||||||
# ---------------------------------
|
# ---------------------------------
|
||||||
exp = instance.data.get("expectedFiles")
|
if not instance.data.get("multiCamera"):
|
||||||
|
exp = instance.data.get("expectedFiles")
|
||||||
for filepath in self._iter_expected_files(exp):
|
for filepath in self._iter_expected_files(exp):
|
||||||
job_info.OutputDirectory += os.path.dirname(filepath)
|
job_info.OutputDirectory += os.path.dirname(filepath)
|
||||||
job_info.OutputFilename += os.path.basename(filepath)
|
job_info.OutputFilename += os.path.basename(filepath)
|
||||||
|
|
||||||
return job_info
|
return job_info
|
||||||
|
|
||||||
|
|
@ -163,11 +168,11 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
def process_submission(self):
|
def process_submission(self):
|
||||||
|
|
||||||
instance = self._instance
|
instance = self._instance
|
||||||
filepath = self.scene_path
|
filepath = instance.context.data["currentFile"]
|
||||||
|
|
||||||
files = instance.data["expectedFiles"]
|
files = instance.data["expectedFiles"]
|
||||||
if not files:
|
if not files:
|
||||||
raise RuntimeError("No Render Elements found!")
|
raise KnownPublishError("No Render Elements found!")
|
||||||
first_file = next(self._iter_expected_files(files))
|
first_file = next(self._iter_expected_files(files))
|
||||||
output_dir = os.path.dirname(first_file)
|
output_dir = os.path.dirname(first_file)
|
||||||
instance.data["outputDir"] = output_dir
|
instance.data["outputDir"] = output_dir
|
||||||
|
|
@ -181,9 +186,17 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
|
|
||||||
self.log.debug("Submitting 3dsMax render..")
|
self.log.debug("Submitting 3dsMax render..")
|
||||||
project_settings = instance.context.data["project_settings"]
|
project_settings = instance.context.data["project_settings"]
|
||||||
payload = self._use_published_name(payload_data, project_settings)
|
if instance.data.get("multiCamera"):
|
||||||
job_info, plugin_info = payload
|
self.log.debug("Submitting jobs for multiple cameras..")
|
||||||
self.submit(self.assemble_payload(job_info, plugin_info))
|
payload = self._use_published_name_for_multiples(
|
||||||
|
payload_data, project_settings)
|
||||||
|
job_infos, plugin_infos = payload
|
||||||
|
for job_info, plugin_info in zip(job_infos, plugin_infos):
|
||||||
|
self.submit(self.assemble_payload(job_info, plugin_info))
|
||||||
|
else:
|
||||||
|
payload = self._use_published_name(payload_data, project_settings)
|
||||||
|
job_info, plugin_info = payload
|
||||||
|
self.submit(self.assemble_payload(job_info, plugin_info))
|
||||||
|
|
||||||
def _use_published_name(self, data, project_settings):
|
def _use_published_name(self, data, project_settings):
|
||||||
# Not all hosts can import these modules.
|
# Not all hosts can import these modules.
|
||||||
|
|
@ -206,7 +219,7 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
|
|
||||||
files = instance.data.get("expectedFiles")
|
files = instance.data.get("expectedFiles")
|
||||||
if not files:
|
if not files:
|
||||||
raise RuntimeError("No render elements found")
|
raise KnownPublishError("No render elements found")
|
||||||
first_file = next(self._iter_expected_files(files))
|
first_file = next(self._iter_expected_files(files))
|
||||||
old_output_dir = os.path.dirname(first_file)
|
old_output_dir = os.path.dirname(first_file)
|
||||||
output_beauty = RenderSettings().get_render_output(instance.name,
|
output_beauty = RenderSettings().get_render_output(instance.name,
|
||||||
|
|
@ -218,6 +231,7 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
plugin_data["RenderOutput"] = beauty_name
|
plugin_data["RenderOutput"] = beauty_name
|
||||||
# as 3dsmax has version with different languages
|
# as 3dsmax has version with different languages
|
||||||
plugin_data["Language"] = "ENU"
|
plugin_data["Language"] = "ENU"
|
||||||
|
|
||||||
renderer_class = get_current_renderer()
|
renderer_class = get_current_renderer()
|
||||||
|
|
||||||
renderer = str(renderer_class).split(":")[0]
|
renderer = str(renderer_class).split(":")[0]
|
||||||
|
|
@ -249,6 +263,120 @@ class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline,
|
||||||
|
|
||||||
return job_info, plugin_info
|
return job_info, plugin_info
|
||||||
|
|
||||||
|
def get_job_info_through_camera(self, camera):
|
||||||
|
"""Get the job parameters for deadline submission when
|
||||||
|
multi-camera is enabled.
|
||||||
|
Args:
|
||||||
|
infos(dict): a dictionary with job info.
|
||||||
|
"""
|
||||||
|
instance = self._instance
|
||||||
|
context = instance.context
|
||||||
|
job_info = copy.deepcopy(self.job_info)
|
||||||
|
exp = instance.data.get("expectedFiles")
|
||||||
|
|
||||||
|
src_filepath = context.data["currentFile"]
|
||||||
|
src_filename = os.path.basename(src_filepath)
|
||||||
|
job_info.Name = "%s - %s - %s" % (
|
||||||
|
src_filename, instance.name, camera)
|
||||||
|
for filepath in self._iter_expected_files(exp):
|
||||||
|
if camera not in filepath:
|
||||||
|
continue
|
||||||
|
job_info.OutputDirectory += os.path.dirname(filepath)
|
||||||
|
job_info.OutputFilename += os.path.basename(filepath)
|
||||||
|
|
||||||
|
return job_info
|
||||||
|
# set the output filepath with the relative camera
|
||||||
|
|
||||||
|
def get_plugin_info_through_camera(self, camera):
|
||||||
|
"""Get the plugin parameters for deadline submission when
|
||||||
|
multi-camera is enabled.
|
||||||
|
Args:
|
||||||
|
infos(dict): a dictionary with plugin info.
|
||||||
|
"""
|
||||||
|
instance = self._instance
|
||||||
|
# set the target camera
|
||||||
|
plugin_info = copy.deepcopy(self.plugin_info)
|
||||||
|
|
||||||
|
plugin_data = {}
|
||||||
|
# set the output filepath with the relative camera
|
||||||
|
if instance.data.get("multiCamera"):
|
||||||
|
scene_filepath = instance.context.data["currentFile"]
|
||||||
|
scene_filename = os.path.basename(scene_filepath)
|
||||||
|
scene_directory = os.path.dirname(scene_filepath)
|
||||||
|
current_filename, ext = os.path.splitext(scene_filename)
|
||||||
|
camera_scene_name = f"{current_filename}_{camera}{ext}"
|
||||||
|
camera_scene_filepath = os.path.join(
|
||||||
|
scene_directory, f"_{current_filename}", camera_scene_name)
|
||||||
|
plugin_data["SceneFile"] = camera_scene_filepath
|
||||||
|
|
||||||
|
files = instance.data.get("expectedFiles")
|
||||||
|
if not files:
|
||||||
|
raise KnownPublishError("No render elements found")
|
||||||
|
first_file = next(self._iter_expected_files(files))
|
||||||
|
old_output_dir = os.path.dirname(first_file)
|
||||||
|
rgb_output = RenderSettings().get_batch_render_output(camera) # noqa
|
||||||
|
rgb_bname = os.path.basename(rgb_output)
|
||||||
|
dir = os.path.dirname(first_file)
|
||||||
|
beauty_name = f"{dir}/{rgb_bname}"
|
||||||
|
beauty_name = beauty_name.replace("\\", "/")
|
||||||
|
plugin_info["RenderOutput"] = beauty_name
|
||||||
|
renderer_class = get_current_renderer()
|
||||||
|
|
||||||
|
renderer = str(renderer_class).split(":")[0]
|
||||||
|
if renderer in [
|
||||||
|
"ART_Renderer",
|
||||||
|
"Redshift_Renderer",
|
||||||
|
"V_Ray_6_Hotfix_3",
|
||||||
|
"V_Ray_GPU_6_Hotfix_3",
|
||||||
|
"Default_Scanline_Renderer",
|
||||||
|
"Quicksilver_Hardware_Renderer",
|
||||||
|
]:
|
||||||
|
render_elem_list = RenderSettings().get_batch_render_elements(
|
||||||
|
instance.name, old_output_dir, camera
|
||||||
|
)
|
||||||
|
for i, element in enumerate(render_elem_list):
|
||||||
|
if camera in element:
|
||||||
|
elem_bname = os.path.basename(element)
|
||||||
|
new_elem = f"{dir}/{elem_bname}"
|
||||||
|
new_elem = new_elem.replace("/", "\\")
|
||||||
|
plugin_info["RenderElementOutputFilename%d" % i] = new_elem # noqa
|
||||||
|
|
||||||
|
if camera:
|
||||||
|
# set the default camera and target camera
|
||||||
|
# (weird parameters from max)
|
||||||
|
plugin_data["Camera"] = camera
|
||||||
|
plugin_data["Camera1"] = camera
|
||||||
|
plugin_data["Camera0"] = None
|
||||||
|
|
||||||
|
plugin_info.update(plugin_data)
|
||||||
|
return plugin_info
|
||||||
|
|
||||||
|
def _use_published_name_for_multiples(self, data, project_settings):
|
||||||
|
"""Process the parameters submission for deadline when
|
||||||
|
user enables multi-cameras option.
|
||||||
|
Args:
|
||||||
|
job_info_list (list): A list of multiple job infos
|
||||||
|
plugin_info_list (list): A list of multiple plugin infos
|
||||||
|
"""
|
||||||
|
job_info_list = []
|
||||||
|
plugin_info_list = []
|
||||||
|
instance = self._instance
|
||||||
|
cameras = instance.data.get("cameras", [])
|
||||||
|
plugin_data = {}
|
||||||
|
multipass = get_multipass_setting(project_settings)
|
||||||
|
if multipass:
|
||||||
|
plugin_data["DisableMultipass"] = 0
|
||||||
|
else:
|
||||||
|
plugin_data["DisableMultipass"] = 1
|
||||||
|
for cam in cameras:
|
||||||
|
job_info = self.get_job_info_through_camera(cam)
|
||||||
|
plugin_info = self.get_plugin_info_through_camera(cam)
|
||||||
|
plugin_info.update(plugin_data)
|
||||||
|
job_info_list.append(job_info)
|
||||||
|
plugin_info_list.append(plugin_info)
|
||||||
|
|
||||||
|
return job_info_list, plugin_info_list
|
||||||
|
|
||||||
def from_published_scene(self, replace_in_path=True):
|
def from_published_scene(self, replace_in_path=True):
|
||||||
instance = self._instance
|
instance = self._instance
|
||||||
if instance.data["renderer"] == "Redshift_Renderer":
|
if instance.data["renderer"] == "Redshift_Renderer":
|
||||||
|
|
|
||||||
|
|
@ -582,16 +582,17 @@ def _create_instances_for_aov(instance, skeleton, aov_filter, additional_data,
|
||||||
group_name = subset
|
group_name = subset
|
||||||
|
|
||||||
# if there are multiple cameras, we need to add camera name
|
# if there are multiple cameras, we need to add camera name
|
||||||
if isinstance(col, (list, tuple)):
|
expected_filepath = col[0] if isinstance(col, (list, tuple)) else col
|
||||||
cam = [c for c in cameras if c in col[0]]
|
cams = [cam for cam in cameras if cam in expected_filepath]
|
||||||
else:
|
if cams:
|
||||||
# in case of single frame
|
for cam in cams:
|
||||||
cam = [c for c in cameras if c in col]
|
if aov:
|
||||||
if cam:
|
if not aov.startswith(cam):
|
||||||
if aov:
|
subset_name = '{}_{}_{}'.format(group_name, cam, aov)
|
||||||
subset_name = '{}_{}_{}'.format(group_name, cam, aov)
|
else:
|
||||||
else:
|
subset_name = "{}_{}".format(group_name, aov)
|
||||||
subset_name = '{}_{}'.format(group_name, cam)
|
else:
|
||||||
|
subset_name = '{}_{}'.format(group_name, cam)
|
||||||
else:
|
else:
|
||||||
if aov:
|
if aov:
|
||||||
subset_name = '{}_{}'.format(group_name, aov)
|
subset_name = '{}_{}'.format(group_name, aov)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue