style fix

This commit is contained in:
Kayla Man 2023-02-20 21:29:11 +08:00
parent 281d53bb01
commit 8002dc4f4f
9 changed files with 275 additions and 306 deletions

View file

@ -134,19 +134,21 @@ def get_default_render_folder(project_setting=None):
def set_framerange(startFrame, endFrame):
"""Get/set the type of time range to be rendered.
Possible values are:
1 -Single frame.
2 -Active time segment ( animationRange ).
3 -User specified Range.
4 -User specified Frame pickup string (for example "1,3,5-12").
"""
# hard-code, there should be a custom setting for this
Args:
start_frame (int): Start frame number.
end_frame (int): End frame number.
Note:
Frame range can be specified in different types. Possible values are:
* `1` - Single frame.
* `2` - Active time segment ( animationRange ).
* `3` - User specified Range.
* `4` - User specified Frame pickup string (for example `1,3,5-12`).
Todo:
Current type is hard-coded, there should be a custom setting for this.
"""
rt.rendTimeType = 4
if startFrame is not None and endFrame is not None:
frameRange = "{0}-{1}".format(startFrame, endFrame)
@ -157,3 +159,15 @@ def get_multipass_setting(project_setting=None):
return (project_setting["max"]
["RenderSettings"]
["multipass"])
def get_max_version():
"""
Args:
get max version date for deadline
Returns:
#(25000, 62, 0, 25, 0, 0, 997, 2023, "")
max_info[7] = max version date
"""
max_info = rt.maxversion()
return max_info[7]

View file

@ -15,7 +15,6 @@ from openpype.pipeline import legacy_io
class RenderProducts(object):
@classmethod
def __init__(self, project_settings=None):
self._project_settings = project_settings
if not self._project_settings:
@ -36,15 +35,11 @@ class RenderProducts(object):
filename,
container)
context = get_current_project_asset()
startFrame = context["data"].get("frameStart")
endFrame = context["data"].get("frameEnd") + 1
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
full_render_list = self.beauty_render_product(output_file,
startFrame,
endFrame,
img_fmt)
full_render_list = []
beauty = self.beauty_render_product(output_file, img_fmt)
full_render_list.append(beauty)
renderer_class = get_current_renderer()
renderer = str(renderer_class).split(":")[0]
@ -60,41 +55,29 @@ class RenderProducts(object):
renderer == "Quicksilver_Hardware_Renderer"
):
render_elem_list = self.render_elements_product(output_file,
startFrame,
endFrame,
img_fmt)
for render_elem in render_elem_list:
full_render_list.append(render_elem)
if render_elem_list:
for render_elem in render_elem_list:
full_render_list.append(render_elem)
return full_render_list
if renderer == "Arnold":
aov_list = self.arnold_render_product(output_file,
startFrame,
endFrame,
img_fmt)
if aov_list:
for aov in aov_list:
full_render_list.append(aov)
return full_render_list
def beauty_render_product(self, folder, startFrame, endFrame, fmt):
# get the beauty
beauty_frame_range = list()
for f in range(startFrame, endFrame):
beauty = "{0}.{1}.{2}".format(folder,
str(f),
fmt)
beauty = beauty.replace("\\", "/")
beauty_frame_range.append(beauty)
return beauty_frame_range
def beauty_render_product(self, folder, fmt):
beauty_output = f"{folder}.####.{fmt}"
beauty_output = beauty_output.replace("\\", "/")
return beauty_output
# TODO: Get the arnold render product
def arnold_render_product(self, folder, startFrame, endFrame, fmt):
def arnold_render_product(self, folder, fmt):
"""Get all the Arnold AOVs"""
aovs = list()
aovs = []
amw = rt.MaxtoAOps.AOVsManagerWindow()
aov_mgr = rt.renderers.current.AOVManager
@ -105,21 +88,17 @@ class RenderProducts(object):
for i in range(aov_group_num):
# get the specific AOV group
for aov in aov_mgr.drivers[i].aov_list:
for f in range(startFrame, endFrame):
render_element = "{0}_{1}.{2}.{3}".format(folder,
str(aov.name),
str(f),
fmt)
render_element = render_element.replace("\\", "/")
aovs.append(render_element)
render_element = f"{folder}_{aov.name}.####.{fmt}"
render_element = render_element.replace("\\", "/")
aovs.append(render_element)
# close the AOVs manager window
amw.close()
return aovs
def render_elements_product(self, folder, startFrame, endFrame, fmt):
def render_elements_product(self, folder, fmt):
"""Get all the render element output files. """
render_dirname = list()
render_dirname = []
render_elem = rt.maxOps.GetCurRenderElementMgr()
render_elem_num = render_elem.NumRenderElements()
@ -128,16 +107,11 @@ class RenderProducts(object):
renderlayer_name = render_elem.GetRenderElement(i)
target, renderpass = str(renderlayer_name).split(":")
if renderlayer_name.enabled:
for f in range(startFrame, endFrame):
render_element = "{0}_{1}.{2}.{3}".format(folder,
renderpass,
str(f),
fmt)
render_element = render_element.replace("\\", "/")
render_dirname.append(render_element)
render_element = f"{folder}_{renderpass}.####.{fmt}"
render_element = render_element.replace("\\", "/")
render_dirname.append(render_element)
return render_dirname
def image_format(self):
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
return img_fmt
return self._project_settings["max"]["RenderSettings"]["image_format"] # noqa

View file

@ -22,7 +22,6 @@ class RenderSettings(object):
"underscore": "_"
}
@classmethod
def __init__(self, project_settings=None):
self._project_settings = project_settings
if not self._project_settings:
@ -41,7 +40,7 @@ class RenderSettings(object):
if not found:
raise RuntimeError("Camera not found")
def set_renderoutput(self, container):
def render_output(self, container):
folder = rt.maxFilePath
# hard-coded, should be customized in the setting
file = rt.maxFileName
@ -144,7 +143,7 @@ class RenderSettings(object):
aov_name = "{0}_{1}..{2}".format(dir, renderpass, ext)
render_elem.SetRenderElementFileName(i, aov_name)
def get_renderoutput(self, container, output_dir):
def get_render_output(self, container, output_dir):
output = os.path.join(output_dir, container)
img_fmt = self._project_settings["max"]["RenderSettings"]["image_format"] # noqa
outputFilename = "{0}..{1}".format(output, img_fmt)

View file

@ -30,4 +30,4 @@ class CreateRender(plugin.MaxCreator):
# set viewport camera for rendering(mandatory for deadline)
RenderSettings().set_render_camera(sel_obj)
# set output paths for rendering(mandatory for deadline)
RenderSettings().set_renderoutput(container_name)
RenderSettings().render_output(container_name)

View file

@ -4,7 +4,8 @@ import os
import pyblish.api
from pymxs import runtime as rt
from openpype.pipeline import legacy_io
from openpype.pipeline import get_current_asset_name
from openpype.hosts.max.api.lib import get_max_version
from openpype.hosts.max.api.lib_renderproducts import RenderProducts
from openpype.client import get_last_version_by_subset_name
@ -25,7 +26,7 @@ class CollectRender(pyblish.api.InstancePlugin):
filepath = current_file.replace("\\", "/")
context.data['currentFile'] = current_file
asset = legacy_io.Session["AVALON_ASSET"]
asset = get_current_asset_name()
render_layer_files = RenderProducts().render_product(instance.name)
folder = folder.replace("\\", "/")
@ -51,6 +52,7 @@ class CollectRender(pyblish.api.InstancePlugin):
"subset": instance.name,
"asset": asset,
"publish": True,
"maxversion": str(get_max_version()),
"imageFormat": imgFormat,
"family": 'maxrender',
"families": ['maxrender'],

View file

@ -1,237 +0,0 @@
import os
import json
import getpass
import requests
import pyblish.api
from openpype.pipeline import legacy_io
from openpype.settings import get_project_settings
from openpype.hosts.max.api.lib import (
get_current_renderer,
get_multipass_setting
)
from openpype.hosts.max.api.lib_rendersettings import RenderSettings
class MaxSubmitRenderDeadline(pyblish.api.InstancePlugin):
"""
3DMax File Submit Render Deadline
"""
label = "Submit 3DsMax Render to Deadline"
order = pyblish.api.IntegratorOrder
hosts = ["max"]
families = ["maxrender"]
targets = ["local"]
use_published = True
priority = 50
chunk_size = 1
group = None
deadline_pool = None
deadline_pool_secondary = None
framePerTask = 1
def process(self, instance):
context = instance.context
filepath = context.data["currentFile"]
filename = os.path.basename(filepath)
comment = context.data.get("comment", "")
deadline_user = context.data.get("deadlineUser", getpass.getuser())
jobname = "{0} - {1}".format(filename, instance.name)
# StartFrame to EndFrame
frames = "{start}-{end}".format(
start=int(instance.data["frameStart"]),
end=int(instance.data["frameEnd"])
)
if self.use_published:
for item in context:
if "workfile" in item.data["families"]:
msg = "Workfile (scene) must be published along"
assert item.data["publish"] is True, msg
template_data = item.data.get("anatomyData")
rep = item.data.get("representations")[0].get("name")
template_data["representation"] = rep
template_data["ext"] = rep
template_data["comment"] = None
anatomy_data = context.data["anatomy"]
anatomy_filled = anatomy_data.format(template_data)
template_filled = anatomy_filled["publish"]["path"]
filepath = os.path.normpath(template_filled)
filepath = filepath.replace("\\", "/")
self.log.info(
"Using published scene for render {}".format(filepath)
)
if not os.path.exists(filepath):
self.log.error("published scene does not exist!")
new_scene = self._clean_name(filepath)
# use the anatomy data for setting up the path of the files
orig_scene = self._clean_name(instance.context.data["currentFile"])
expected_files = instance.data.get("expectedFiles")
new_exp = []
for file in expected_files:
new_file = str(file).replace(orig_scene, new_scene)
new_exp.append(new_file)
instance.data["expectedFiles"] = new_exp
metadata_folder = instance.data.get("publishRenderMetadataFolder")
if metadata_folder:
metadata_folder = metadata_folder.replace(orig_scene,
new_scene)
instance.data["publishRenderMetadataFolder"] = metadata_folder
payload = {
"JobInfo": {
# Top-level group name
"BatchName": filename,
# Job name, as seen in Monitor
"Name": jobname,
# Arbitrary username, for visualisation in Monitor
"UserName": deadline_user,
"Plugin": instance.data["plugin"],
"Group": self.group,
"Pool": self.deadline_pool,
"secondaryPool": self.deadline_pool_secondary,
"Frames": frames,
"ChunkSize": self.chunk_size,
"Priority": instance.data.get("priority", self.priority),
"Comment": comment,
"FramesPerTask": self.framePerTask
},
"PluginInfo": {
# Input
"SceneFile": filepath,
"Version": "2023",
"SaveFile": True,
# Mandatory for Deadline
# Houdini version without patch number
"IgnoreInputs": True
},
# Mandatory for Deadline, may be empty
"AuxFiles": []
}
# Include critical environment variables with submission + api.Session
keys = [
# Submit along the current Avalon tool setup that we launched
# this application with so the Render Slave can build its own
# similar environment using it, e.g. "maya2018;vray4.x;yeti3.1.9"
"AVALON_TOOLS",
"OPENPYPE_VERSION"
]
# Add mongo url if it's enabled
if context.data.get("deadlinePassMongoUrl"):
keys.append("OPENPYPE_MONGO")
environment = dict({key: os.environ[key] for key in keys
if key in os.environ}, **legacy_io.Session)
payload["JobInfo"].update({
"EnvironmentKeyValue%d" % index: "{key}={value}".format(
key=key,
value=environment[key]
) for index, key in enumerate(environment)
})
# Include OutputFilename entries
# The first entry also enables double-click to preview rendered
# frames from Deadline Monitor
output_data = {}
# need to be fixed
for i, filepath in enumerate(instance.data["expectedFiles"]):
dirname = os.path.dirname(filepath)
fname = os.path.basename(filepath)
output_data["OutputDirectory%d" % i] = dirname.replace("\\", "/")
output_data["OutputFilename%d" % i] = fname
if not os.path.exists(dirname):
self.log.info("Ensuring output directory exists: %s" %
dirname)
os.makedirs(dirname)
plugin_data = {}
project_setting = get_project_settings(
legacy_io.Session["AVALON_PROJECT"]
)
multipass = get_multipass_setting(project_setting)
if multipass:
plugin_data["DisableMultipass"] = 0
else:
plugin_data["DisableMultipass"] = 1
if self.use_published:
old_output_dir = os.path.dirname(expected_files[0])
output_beauty = RenderSettings().get_renderoutput(instance.name,
old_output_dir)
output_beauty = output_beauty.replace(orig_scene, new_scene)
output_beauty = output_beauty.replace("\\", "/")
plugin_data["RenderOutput"] = output_beauty
renderer_class = get_current_renderer()
renderer = str(renderer_class).split(":")[0]
if (
renderer == "ART_Renderer" or
renderer == "Redshift_Renderer" or
renderer == "V_Ray_6_Hotfix_3" or
renderer == "V_Ray_GPU_6_Hotfix_3" or
renderer == "Default_Scanline_Renderer" or
renderer == "Quicksilver_Hardware_Renderer"
):
render_elem_list = RenderSettings().get_render_element()
for i, element in enumerate(render_elem_list):
element = element.replace(orig_scene, new_scene)
plugin_data["RenderElementOutputFilename%d" % i] = element # noqa
self.log.debug("plugin data:{}".format(plugin_data))
self.log.info("Scene name was switched {} -> {}".format(
orig_scene, new_scene
))
payload["JobInfo"].update(output_data)
payload["PluginInfo"].update(plugin_data)
self.submit(instance, payload)
def submit(self, instance, payload):
context = instance.context
deadline_url = context.data.get("defaultDeadline")
deadline_url = instance.data.get(
"deadlineUrl", deadline_url)
assert deadline_url, "Requires Deadline Webservice URL"
plugin = payload["JobInfo"]["Plugin"]
self.log.info("Using Render Plugin : {}".format(plugin))
self.log.info("Submitting..")
self.log.debug(json.dumps(payload, indent=4, sort_keys=True))
# E.g. http://192.168.0.1:8082/api/jobs
url = "{}/api/jobs".format(deadline_url)
response = requests.post(url, json=payload)
if not response.ok:
raise Exception(response.text)
# Store output dir for unified publisher (expectedFilesequence)
expected_files = instance.data["expectedFiles"]
output_dir = os.path.dirname(expected_files[0])
instance.data["toBeRenderedOn"] = "deadline"
instance.data["outputDir"] = output_dir
instance.data["deadlineSubmissionJob"] = response.json()
def rename_render_element(self):
pass
def _clean_name(self, path):
return os.path.splitext(os.path.basename(path))[0]

View file

@ -0,0 +1,217 @@
import os
import getpass
import copy
import attr
from openpype.pipeline import legacy_io
from openpype.settings import get_project_settings
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.abstract_submit_deadline import DeadlineJobInfo
@attr.s
class MaxPluginInfo(object):
SceneFile = attr.ib(default=None) # Input
Version = attr.ib(default=None) # Mandatory for Deadline
SaveFile = attr.ib(default=True)
IgnoreInputs = attr.ib(default=True)
class MaxSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
label = "Submit Render to Deadline"
hosts = ["max"]
families = ["maxrender"]
targets = ["local"]
use_published = True
priority = 50
tile_priority = 50
chunk_size = 1
jobInfo = {}
pluginInfo = {}
group = None
deadline_pool = None
deadline_pool_secondary = None
framePerTask = 1
def get_job_info(self):
job_info = DeadlineJobInfo(Plugin="3dsmax")
# todo: test whether this works for existing production cases
# where custom jobInfo was stored in the project settings
job_info.update(self.jobInfo)
instance = self._instance
context = instance.context
# Always use the original work file name for the Job name even when
# rendering is done from the published Work File. The original work
# file name is clearer because it can also have subversion strings,
# etc. which are stripped for the published file.
src_filepath = context.data["currentFile"]
src_filename = os.path.basename(src_filepath)
job_info.Name = "%s - %s" % (src_filename, instance.name)
job_info.BatchName = src_filename
job_info.Plugin = instance.data["plugin"]
job_info.UserName = context.data.get("deadlineUser", getpass.getuser())
# Deadline requires integers in frame range
frames = "{start}-{end}".format(
start=int(instance.data["frameStart"]),
end=int(instance.data["frameEnd"])
)
job_info.Frames = frames
job_info.Pool = instance.data.get("primaryPool")
job_info.SecondaryPool = instance.data.get("secondaryPool")
job_info.ChunkSize = instance.data.get("chunkSize", 1)
job_info.Comment = context.data.get("comment")
job_info.Priority = instance.data.get("priority", self.priority)
job_info.FramesPerTask = instance.data.get("framesPerTask", 1)
if self.group:
job_info.Group = self.group
# Add options from RenderGlobals
render_globals = instance.data.get("renderGlobals", {})
job_info.update(render_globals)
keys = [
"FTRACK_API_KEY",
"FTRACK_API_USER",
"FTRACK_SERVER",
"OPENPYPE_SG_USER",
"AVALON_PROJECT",
"AVALON_ASSET",
"AVALON_TASK",
"AVALON_APP_NAME",
"OPENPYPE_DEV",
"OPENPYPE_VERSION",
"IS_TEST"
]
# Add mongo url if it's enabled
if self._instance.context.data.get("deadlinePassMongoUrl"):
keys.append("OPENPYPE_MONGO")
environment = dict({key: os.environ[key] for key in keys
if key in os.environ}, **legacy_io.Session)
for key in keys:
value = environment.get(key)
if not value:
continue
job_info.EnvironmentKeyValue[key] = value
# to recognize job from PYPE for turning Event On/Off
job_info.EnvironmentKeyValue["OPENPYPE_RENDER_JOB"] = "1"
job_info.EnvironmentKeyValue["OPENPYPE_LOG_NO_COLORS"] = "1"
# Add list of expected files to job
# ---------------------------------
exp = instance.data.get("expectedFiles")
for filepath in exp:
job_info.OutputDirectory += os.path.dirname(filepath)
job_info.OutputFilename += os.path.basename(filepath)
return job_info
def get_plugin_info(self):
instance = self._instance
plugin_info = MaxPluginInfo(
SceneFile=self.scene_path,
Version=instance.data["maxversion"],
SaveFile = True,
IgnoreInputs = True
)
plugin_payload = attr.asdict(plugin_info)
# Patching with pluginInfo from settings
for key, value in self.pluginInfo.items():
plugin_payload[key] = value
return plugin_payload
def process_submission(self):
instance = self._instance
context = instance.context
filepath = self.scene_path
expected_files = instance.data["expectedFiles"]
if not expected_files:
raise RuntimeError("No Render Elements found!")
output_dir = os.path.dirname(expected_files[0])
instance.data["outputDir"] = output_dir
instance.data["toBeRenderedOn"] = "deadline"
filename = os.path.basename(filepath)
payload_data = {
"filename": filename,
"dirname": output_dir
}
self.log.debug("Submitting 3dsMax render..")
payload = self._use_puhlished_name(payload_data)
job_info, plugin_info = payload
self.submit(self.assemble_payload(job_info, plugin_info))
def _use_puhlished_name(self, data):
instance = self._instance
job_info = copy.deepcopy(self.job_info)
plugin_info = copy.deepcopy(self.plugin_info)
plugin_data = {}
project_setting = get_project_settings(
legacy_io.Session["AVALON_PROJECT"]
)
multipass = get_multipass_setting(project_setting)
if multipass:
plugin_data["DisableMultipass"] = 0
else:
plugin_data["DisableMultipass"] = 1
expected_files = instance.data.get("expectedFiles")
if not expected_files:
raise RuntimeError("No render elements found")
old_output_dir = os.path.dirname(expected_files[0])
output_beauty = RenderSettings().get_render_output(instance.name,
old_output_dir)
filepath = self.from_published_scene()
def _clean_name(path):
return os.path.splitext(os.path.basename(path))[0]
new_scene = _clean_name(filepath)
orig_scene = _clean_name(instance.context.data["currentFile"])
output_beauty = output_beauty.replace(orig_scene, new_scene)
output_beauty = output_beauty.replace("\\", "/")
plugin_data["RenderOutput"] = output_beauty
renderer_class = get_current_renderer()
renderer = str(renderer_class).split(":")[0]
if (
renderer == "ART_Renderer" or
renderer == "Redshift_Renderer" or
renderer == "V_Ray_6_Hotfix_3" or
renderer == "V_Ray_GPU_6_Hotfix_3" or
renderer == "Default_Scanline_Renderer" or
renderer == "Quicksilver_Hardware_Renderer"
):
render_elem_list = RenderSettings().get_render_element()
for i, element in enumerate(render_elem_list):
element = element.replace(orig_scene, new_scene)
plugin_data["RenderElementOutputFilename%d" % i] = element # noqa
self.log.debug("plugin data:{}".format(plugin_data))
plugin_info.update(plugin_data)
return job_info, plugin_info

View file

@ -36,7 +36,7 @@
"scene_patches": [],
"strict_error_checking": true
},
"MaxSubmitRenderDeadline": {
"MaxSubmitDeadline": {
"enabled": true,
"optional": false,
"active": true,
@ -122,4 +122,4 @@
}
}
}
}
}

View file

@ -207,7 +207,7 @@
{
"type": "dict",
"collapsible": true,
"key": "MaxSubmitRenderDeadline",
"key": "MaxSubmitDeadline",
"label": "3dsMax Submit to Deadline",
"checkbox_key": "enabled",
"children": [