diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 2e854061d5..7d6c5650d1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,7 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.18.3-nightly.2
- 3.18.3-nightly.1
- 3.18.2
- 3.18.2-nightly.6
@@ -134,7 +135,6 @@ body:
- 3.15.6
- 3.15.6-nightly.3
- 3.15.6-nightly.2
- - 3.15.6-nightly.1
validations:
required: true
- type: dropdown
diff --git a/openpype/hosts/houdini/plugins/create/create_redshift_rop.py b/openpype/hosts/houdini/plugins/create/create_redshift_rop.py
index 1b8826a932..9d1c7bc90d 100644
--- a/openpype/hosts/houdini/plugins/create/create_redshift_rop.py
+++ b/openpype/hosts/houdini/plugins/create/create_redshift_rop.py
@@ -15,6 +15,9 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
icon = "magic"
ext = "exr"
+ # Default to split export and render jobs
+ split_render = True
+
def create(self, subset_name, instance_data, pre_create_data):
instance_data.pop("active", None)
@@ -36,12 +39,15 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
# Also create the linked Redshift IPR Rop
try:
ipr_rop = instance_node.parent().createNode(
- "Redshift_IPR", node_name=basename + "_IPR"
+ "Redshift_IPR", node_name=f"{basename}_IPR"
)
- except hou.OperationFailed:
+ except hou.OperationFailed as e:
raise plugin.OpenPypeCreatorError(
- ("Cannot create Redshift node. Is Redshift "
- "installed and enabled?"))
+ (
+ "Cannot create Redshift node. Is Redshift "
+ "installed and enabled?"
+ )
+ ) from e
# Move it to directly under the Redshift ROP
ipr_rop.setPosition(instance_node.position() + hou.Vector2(0, -1))
@@ -74,8 +80,15 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
for node in self.selected_nodes:
if node.type().name() == "cam":
camera = node.path()
- parms.update({
- "RS_renderCamera": camera or ""})
+ parms["RS_renderCamera"] = camera or ""
+
+ export_dir = hou.text.expandString("$HIP/pyblish/rs/")
+ rs_filepath = f"{export_dir}{subset_name}/{subset_name}.$F4.rs"
+ parms["RS_archive_file"] = rs_filepath
+
+ if pre_create_data.get("split_render", self.split_render):
+ parms["RS_archive_enable"] = 1
+
instance_node.setParms(parms)
# Lock some Avalon attributes
@@ -102,6 +115,9 @@ class CreateRedshiftROP(plugin.HoudiniCreator):
BoolDef("farm",
label="Submitting to Farm",
default=True),
+ BoolDef("split_render",
+ label="Split export and render jobs",
+ default=self.split_render),
EnumDef("image_format",
image_format_enum,
default=self.ext,
diff --git a/openpype/hosts/houdini/plugins/load/load_redshift_proxy.py b/openpype/hosts/houdini/plugins/load/load_redshift_proxy.py
new file mode 100644
index 0000000000..efd7c6d0ca
--- /dev/null
+++ b/openpype/hosts/houdini/plugins/load/load_redshift_proxy.py
@@ -0,0 +1,112 @@
+import os
+import re
+from openpype.pipeline import (
+ load,
+ get_representation_path,
+)
+from openpype.hosts.houdini.api import pipeline
+from openpype.pipeline.load import LoadError
+
+import hou
+
+
+class RedshiftProxyLoader(load.LoaderPlugin):
+ """Load Redshift Proxy"""
+
+ families = ["redshiftproxy"]
+ label = "Load Redshift Proxy"
+ representations = ["rs"]
+ order = -10
+ icon = "code-fork"
+ color = "orange"
+
+ def load(self, context, name=None, namespace=None, data=None):
+
+ # Get the root node
+ obj = hou.node("/obj")
+
+ # Define node name
+ namespace = namespace if namespace else context["asset"]["name"]
+ node_name = "{}_{}".format(namespace, name) if namespace else name
+
+ # Create a new geo node
+ container = obj.createNode("geo", node_name=node_name)
+
+ # Check whether the Redshift parameters exist - if not, then likely
+ # redshift is not set up or initialized correctly
+ if not container.parm("RS_objprop_proxy_enable"):
+ container.destroy()
+ raise LoadError("Unable to initialize geo node with Redshift "
+ "attributes. Make sure you have the Redshift "
+ "plug-in set up correctly for Houdini.")
+
+ # Enable by default
+ container.setParms({
+ "RS_objprop_proxy_enable": True,
+ "RS_objprop_proxy_file": self.format_path(
+ self.filepath_from_context(context),
+ context["representation"])
+ })
+
+ # Remove the file node, it only loads static meshes
+ # Houdini 17 has removed the file node from the geo node
+ file_node = container.node("file1")
+ if file_node:
+ file_node.destroy()
+
+ # Add this stub node inside so it previews ok
+ proxy_sop = container.createNode("redshift_proxySOP",
+ node_name=node_name)
+ proxy_sop.setDisplayFlag(True)
+
+ nodes = [container, proxy_sop]
+
+ self[:] = nodes
+
+ return pipeline.containerise(
+ node_name,
+ namespace,
+ nodes,
+ context,
+ self.__class__.__name__,
+ suffix="",
+ )
+
+ def update(self, container, representation):
+
+ # Update the file path
+ file_path = get_representation_path(representation)
+
+ node = container["node"]
+ node.setParms({
+ "RS_objprop_proxy_file": self.format_path(
+ file_path, representation)
+ })
+
+ # Update attribute
+ node.setParms({"representation": str(representation["_id"])})
+
+ def remove(self, container):
+
+ node = container["node"]
+ node.destroy()
+
+ @staticmethod
+ def format_path(path, representation):
+ """Format file path correctly for single redshift proxy
+ or redshift proxy sequence."""
+ if not os.path.exists(path):
+ raise RuntimeError("Path does not exist: %s" % path)
+
+ is_sequence = bool(representation["context"].get("frame"))
+ # The path is either a single file or sequence in a folder.
+ if is_sequence:
+ filename = re.sub(r"(.*)\.(\d+)\.(rs.*)", "\\1.$F4.\\3", path)
+ filename = os.path.join(path, filename)
+ else:
+ filename = path
+
+ filename = os.path.normpath(filename)
+ filename = filename.replace("\\", "/")
+
+ return filename
diff --git a/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py b/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
index 0acddab011..aec7e07fbc 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
@@ -31,7 +31,6 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
families = ["redshift_rop"]
def process(self, instance):
-
rop = hou.node(instance.data.get("instance_node"))
# Collect chunkSize
@@ -43,13 +42,29 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
default_prefix = evalParmNoFrame(rop, "RS_outputFileNamePrefix")
beauty_suffix = rop.evalParm("RS_outputBeautyAOVSuffix")
- render_products = []
+ # Store whether we are splitting the render job (export + render)
+ split_render = bool(rop.parm("RS_archive_enable").eval())
+ instance.data["splitRender"] = split_render
+ export_products = []
+ if split_render:
+ export_prefix = evalParmNoFrame(
+ rop, "RS_archive_file", pad_character="0"
+ )
+ beauty_export_product = self.get_render_product_name(
+ prefix=export_prefix,
+ suffix=None)
+ export_products.append(beauty_export_product)
+ self.log.debug(
+ "Found export product: {}".format(beauty_export_product)
+ )
+ instance.data["ifdFile"] = beauty_export_product
+ instance.data["exportFiles"] = list(export_products)
# Default beauty AOV
beauty_product = self.get_render_product_name(
prefix=default_prefix, suffix=beauty_suffix
)
- render_products.append(beauty_product)
+ render_products = [beauty_product]
files_by_aov = {
"_": self.generate_expected_files(instance,
beauty_product)}
@@ -59,11 +74,11 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
i = index + 1
# Skip disabled AOVs
- if not rop.evalParm("RS_aovEnable_%s" % i):
+ if not rop.evalParm(f"RS_aovEnable_{i}"):
continue
- aov_suffix = rop.evalParm("RS_aovSuffix_%s" % i)
- aov_prefix = evalParmNoFrame(rop, "RS_aovCustomPrefix_%s" % i)
+ aov_suffix = rop.evalParm(f"RS_aovSuffix_{i}")
+ aov_prefix = evalParmNoFrame(rop, f"RS_aovCustomPrefix_{i}")
if not aov_prefix:
aov_prefix = default_prefix
@@ -85,7 +100,7 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
instance.data["attachTo"] = [] # stub required data
if "expectedFiles" not in instance.data:
- instance.data["expectedFiles"] = list()
+ instance.data["expectedFiles"] = []
instance.data["expectedFiles"].append(files_by_aov)
# update the colorspace data
diff --git a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
index c8960185b2..bf7fb45a8b 100644
--- a/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
+++ b/openpype/modules/deadline/plugins/publish/submit_houdini_render_deadline.py
@@ -15,6 +15,7 @@ from openpype.lib import (
NumberDef
)
+
@attr.s
class DeadlinePluginInfo():
SceneFile = attr.ib(default=None)
@@ -41,6 +42,12 @@ class VrayRenderPluginInfo():
SeparateFilesPerFrame = attr.ib(default=True)
+@attr.s
+class RedshiftRenderPluginInfo():
+ SceneFile = attr.ib(default=None)
+ Version = attr.ib(default=None)
+
+
class HoudiniSubmitDeadline(
abstract_submit_deadline.AbstractSubmitDeadline,
OpenPypePyblishPluginMixin
@@ -262,6 +269,25 @@ class HoudiniSubmitDeadline(
plugin_info = VrayRenderPluginInfo(
InputFilename=instance.data["ifdFile"],
)
+ elif family == "redshift_rop":
+ plugin_info = RedshiftRenderPluginInfo(
+ SceneFile=instance.data["ifdFile"]
+ )
+ # Note: To use different versions of Redshift on Deadline
+ # set the `REDSHIFT_VERSION` env variable in the Tools
+ # settings in the AYON Application plugin. You will also
+ # need to set that version in `Redshift.param` file
+ # of the Redshift Deadline plugin:
+ # [Redshift_Executable_*]
+ # where * is the version number.
+ if os.getenv("REDSHIFT_VERSION"):
+ plugin_info.Version = os.getenv("REDSHIFT_VERSION")
+ else:
+ self.log.warning((
+ "REDSHIFT_VERSION env variable is not set"
+ " - using version configured in Deadline"
+ ))
+
else:
self.log.error(
"Family '%s' not supported yet to split render job",
diff --git a/openpype/pipeline/publish/lib.py b/openpype/pipeline/publish/lib.py
index 87ca3323cb..40cb94e2bf 100644
--- a/openpype/pipeline/publish/lib.py
+++ b/openpype/pipeline/publish/lib.py
@@ -58,41 +58,13 @@ def get_template_name_profiles(
if not project_settings:
project_settings = get_project_settings(project_name)
- profiles = (
+ return copy.deepcopy(
project_settings
["global"]
["tools"]
["publish"]
["template_name_profiles"]
)
- if profiles:
- return copy.deepcopy(profiles)
-
- # Use legacy approach for cases new settings are not filled yet for the
- # project
- legacy_profiles = (
- project_settings
- ["global"]
- ["publish"]
- ["IntegrateHeroVersion"]
- ["template_name_profiles"]
- )
- if legacy_profiles:
- if not logger:
- logger = Logger.get_logger("get_template_name_profiles")
-
- logger.warning((
- "Project \"{}\" is using legacy access to publish template."
- " It is recommended to move settings to new location"
- " 'project_settings/global/tools/publish/template_name_profiles'."
- ).format(project_name))
-
- # Replace "tasks" key with "task_names"
- profiles = []
- for profile in copy.deepcopy(legacy_profiles):
- profile["task_names"] = profile.pop("tasks", [])
- profiles.append(profile)
- return profiles
def get_hero_template_name_profiles(
@@ -121,36 +93,13 @@ def get_hero_template_name_profiles(
if not project_settings:
project_settings = get_project_settings(project_name)
- profiles = (
+ return copy.deepcopy(
project_settings
["global"]
["tools"]
["publish"]
["hero_template_name_profiles"]
)
- if profiles:
- return copy.deepcopy(profiles)
-
- # Use legacy approach for cases new settings are not filled yet for the
- # project
- legacy_profiles = copy.deepcopy(
- project_settings
- ["global"]
- ["publish"]
- ["IntegrateHeroVersion"]
- ["template_name_profiles"]
- )
- if legacy_profiles:
- if not logger:
- logger = Logger.get_logger("get_hero_template_name_profiles")
-
- logger.warning((
- "Project \"{}\" is using legacy access to hero publish template."
- " It is recommended to move settings to new location"
- " 'project_settings/global/tools/publish/"
- "hero_template_name_profiles'."
- ).format(project_name))
- return legacy_profiles
def get_publish_template_name(
diff --git a/openpype/plugins/publish/integrate_hero_version.py b/openpype/plugins/publish/integrate_hero_version.py
index 9f0f7fe7f3..59dc6b5c64 100644
--- a/openpype/plugins/publish/integrate_hero_version.py
+++ b/openpype/plugins/publish/integrate_hero_version.py
@@ -54,7 +54,6 @@ class IntegrateHeroVersion(pyblish.api.InstancePlugin):
# permissions error on files (files were used or user didn't have perms)
# *but all other plugins must be sucessfully completed
- template_name_profiles = []
_default_template_name = "hero"
def process(self, instance):
diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json
index ac2d9e190d..64f292a140 100644
--- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json
+++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_global_publish.json
@@ -1023,49 +1023,6 @@
{
"type": "label",
"label": "NOTE: Hero publish template profiles settings were moved to Tools/Publish/Hero template name profiles. Please move values there."
- },
- {
- "type": "list",
- "key": "template_name_profiles",
- "label": "Template name profiles (DEPRECATED)",
- "use_label_wrap": true,
- "object_type": {
- "type": "dict",
- "children": [
- {
- "key": "families",
- "label": "Families",
- "type": "list",
- "object_type": "text"
- },
- {
- "type": "hosts-enum",
- "key": "hosts",
- "label": "Hosts",
- "multiselection": true
- },
- {
- "key": "task_types",
- "label": "Task types",
- "type": "task-types-enum"
- },
- {
- "key": "task_names",
- "label": "Task names",
- "type": "list",
- "object_type": "text"
- },
- {
- "type": "separator"
- },
- {
- "type": "text",
- "key": "template_name",
- "label": "Template name",
- "tooltip": "Name of template from Anatomy templates"
- }
- ]
- }
}
]
},
diff --git a/openpype/version.py b/openpype/version.py
index dba782ded4..279575d110 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.18.3-nightly.1"
+__version__ = "3.18.3-nightly.2"
diff --git a/pyproject.toml b/pyproject.toml
index 38236f88bc..ee8e8017e3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -181,3 +181,8 @@ reportMissingTypeStubs = false
[tool.poetry.extras]
docs = ["Sphinx", "furo", "sphinxcontrib-napoleon"]
+
+[tool.pydocstyle]
+inherit = false
+convetion = "google"
+match = "(?!test_).*\\.py"
diff --git a/server_addon/core/server/settings/publish_plugins.py b/server_addon/core/server/settings/publish_plugins.py
index ef52416369..0c9b9c96ef 100644
--- a/server_addon/core/server/settings/publish_plugins.py
+++ b/server_addon/core/server/settings/publish_plugins.py
@@ -697,13 +697,6 @@ class IntegrateHeroVersionModel(BaseSettingsModel):
optional: bool = Field(False, title="Optional")
active: bool = Field(True, title="Active")
families: list[str] = Field(default_factory=list, title="Families")
- # TODO remove when removed from client code
- template_name_profiles: list[IntegrateHeroTemplateNameProfileModel] = (
- Field(
- default_factory=list,
- title="Template name profiles"
- )
- )
class CleanUpModel(BaseSettingsModel):
@@ -1049,19 +1042,6 @@ DEFAULT_PUBLISH_VALUES = {
"layout",
"mayaScene",
"simpleUnrealTexture"
- ],
- "template_name_profiles": [
- {
- "product_types": [
- "simpleUnrealTexture"
- ],
- "hosts": [
- "standalonepublisher"
- ],
- "task_types": [],
- "task_names": [],
- "template_name": "simpleUnrealTextureHero"
- }
]
},
"CleanUp": {
diff --git a/server_addon/deadline/server/version.py b/server_addon/deadline/server/version.py
index 1276d0254f..0a8da88258 100644
--- a/server_addon/deadline/server/version.py
+++ b/server_addon/deadline/server/version.py
@@ -1 +1 @@
-__version__ = "0.1.5"
+__version__ = "0.1.6"
diff --git a/server_addon/houdini/server/version.py b/server_addon/houdini/server/version.py
index 6232f7ab18..5635676f6b 100644
--- a/server_addon/houdini/server/version.py
+++ b/server_addon/houdini/server/version.py
@@ -1 +1 @@
-__version__ = "0.2.10"
+__version__ = "0.2.11"
diff --git a/setup.cfg b/setup.cfg
index ead9b25164..f0f754fb24 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -16,10 +16,6 @@ max-complexity = 30
[pylint.'MESSAGES CONTROL']
disable = no-member
-[pydocstyle]
-convention = google
-ignore = D107
-
[coverage:run]
branch = True
omit = /tests