diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index e9b68a54f1..e831bf3dc1 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,9 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.18.5-nightly.2
+ - 3.18.5-nightly.1
+ - 3.18.4
- 3.18.4-nightly.1
- 3.18.3
- 3.18.3-nightly.2
@@ -132,9 +135,6 @@ body:
- 3.15.8-nightly.1
- 3.15.7
- 3.15.7-nightly.3
- - 3.15.7-nightly.2
- - 3.15.7-nightly.1
- - 3.15.6
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7b51fade6f..546b2c12ea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,151 @@
# Changelog
+## [3.18.4](https://github.com/ynput/OpenPype/tree/3.18.4)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.18.3...3.18.4)
+
+### **🚀 Enhancements**
+
+
+
+multiple render camera supports for 3dsmax #5124
+
+Supports for rendering with multiple cameras in 3dsmax
+- [x] Add Batch Render Layers functions
+- [x] Rewrite lib.rendersetting and lib.renderproduct
+- [x] Add multi-camera options in creator.
+- [x] Collector with batch render-layer when multi-camera enabled.
+- [x] Add instance plugin for saving scene files with different cameras respectively by using subprocess
+- [x] Refactor submit_max_deadline
+- [x] Check with metadata.json in submit publish job
+
+
+___
+
+
+
+
+
+Fusion: new creator for image product type #6057
+
+In many DCC `render` product type is expected to be sequence of files. This PR adds new explicit creator for `image` product type which is focused on single frame image. Workflows for both product types might be a bit different, this gives artists more granularity to choose better workflow.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Maya: Account and ignore free image planes. #5993
+
+Free image planes do not have the `->` path separator, so we need to account for that.
+
+
+___
+
+
+
+
+
+Blender: Fix long names for instances #6070
+
+Changed naming for instances to use only final part of the `folderPath`.
+
+
+___
+
+
+
+
+
+Traypublisher & Chore: Instance version on follow workfile version #6117
+
+If `follow_workfile_version` is enabled but context does not have filled workfile version, a version on instance is used instead.
+
+
+___
+
+
+
+
+
+Substance Painter: Thumbnail errors with PBR Texture Set #6127
+
+When publishing with PBR Metallic Roughness as Output Template, Emissive Map errors out because of the missing channel in the material and the map can't be generated in Substance Painter. This PR is to make sure `imagestance.data["publish"] = False` so that the related "empty" texture instance would be skipped to generate the output.
+
+
+___
+
+
+
+
+
+Transcoding: Fix reading image sequences through oiiotool #6129
+
+When transcoding image sequences, the second image onwards includes the invalid xml line of `Reading path/to/file.exr` of the oiiotool output.This is most likely not the best solution, but it fixes the issue and illustrates the problem.Error:
+```
+ERROR:pyblish.plugin:Traceback (most recent call last):
+ File "C:\Users\tokejepsen\AppData\Local\Ynput\AYON\dependency_packages\ayon_2310271602_windows.zip\dependencies\pyblish\plugin.py", line 527, in __explicit_process
+ runner(*args)
+ File "C:\Users\tokejepsen\OpenPype\openpype\plugins\publish\extract_color_transcode.py", line 152, in process
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 1136, in convert_colorspace
+ input_info = get_oiio_info_for_input(input_path, logger=logger)
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 124, in get_oiio_info_for_input
+ output.append(parse_oiio_xml_output(xml_text, logger=logger))
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 276, in parse_oiio_xml_output
+ tree = xml.etree.ElementTree.fromstring(xml_string)
+ File "xml\etree\ElementTree.py", line 1347, in XML
+xml.etree.ElementTree.ParseError: syntax error: line 1, column 0
+Traceback (most recent call last):
+ File "C:\Users\tokejepsen\AppData\Local\Ynput\AYON\dependency_packages\ayon_2310271602_windows.zip\dependencies\pyblish\plugin.py", line 527, in __explicit_process
+ runner(*args)
+ File "", line 152, in process
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 1136, in convert_colorspace
+ input_info = get_oiio_info_for_input(input_path, logger=logger)
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 124, in get_oiio_info_for_input
+ output.append(parse_oiio_xml_output(xml_text, logger=logger))
+ File "C:\Users\tokejepsen\OpenPype\openpype\lib\transcoding.py", line 276, in parse_oiio_xml_output
+ tree = xml.etree.ElementTree.fromstring(xml_string)
+ File "xml\etree\ElementTree.py", line 1347, in XML
+xml.etree.ElementTree.ParseError: syntax error: line 1, column 0
+```
+
+
+
+___
+
+
+
+
+
+AYON: Remove 'IntegrateHeroVersion' conversion #6130
+
+Remove settings conversion for `IntegrateHeroVersion`.
+
+
+___
+
+
+
+
+
+Chore tools: Make sure style object is not garbage collected #6136
+
+Minor fix in tool utils to make sure style C++ object is not garbage collected when not stored into variable.
+
+
+___
+
+
+
+
+
+
## [3.18.3](https://github.com/ynput/OpenPype/tree/3.18.3)
diff --git a/openpype/hosts/blender/plugins/load/load_blend.py b/openpype/hosts/blender/plugins/load/load_blend.py
index 2d5ac18149..4abe4a6afb 100644
--- a/openpype/hosts/blender/plugins/load/load_blend.py
+++ b/openpype/hosts/blender/plugins/load/load_blend.py
@@ -102,7 +102,6 @@ class BlendLoader(plugin.AssetLoader):
# Link all the container children to the collection
for obj in container.children_recursive:
- print(obj)
bpy.context.scene.collection.objects.link(obj)
# Remove the library from the blend file
@@ -194,8 +193,20 @@ class BlendLoader(plugin.AssetLoader):
transform = asset_group.matrix_basis.copy()
old_data = dict(asset_group.get(AVALON_PROPERTY))
+ old_members = old_data.get("members", [])
parent = asset_group.parent
+ actions = {}
+ objects_with_anim = [
+ obj for obj in asset_group.children_recursive
+ if obj.animation_data]
+ for obj in objects_with_anim:
+ # Check if the object has an action and, if so, add it to a dict
+ # so we can restore it later. Save and restore the action only
+ # if it wasn't originally loaded from the current asset.
+ if obj.animation_data.action not in old_members:
+ actions[obj.name] = obj.animation_data.action
+
self.exec_remove(container)
asset_group, members = self._process_data(libpath, group_name)
@@ -206,6 +217,11 @@ class BlendLoader(plugin.AssetLoader):
asset_group.matrix_basis = transform
asset_group.parent = parent
+ # Restore the actions
+ for obj in asset_group.children_recursive:
+ if obj.name in actions:
+ obj.animation_data.action = actions[obj.name]
+
# Restore the old data, but reset memebers, as they don't exist anymore
# This avoids a crash, because the memory addresses of those members
# are not valid anymore
diff --git a/openpype/hosts/houdini/plugins/load/load_fbx.py b/openpype/hosts/houdini/plugins/load/load_fbx.py
index cac22d62d4..894ac62b3e 100644
--- a/openpype/hosts/houdini/plugins/load/load_fbx.py
+++ b/openpype/hosts/houdini/plugins/load/load_fbx.py
@@ -16,8 +16,9 @@ class FbxLoader(load.LoaderPlugin):
order = -10
- families = ["staticMesh", "fbx"]
- representations = ["fbx"]
+ families = ["*"]
+ representations = ["*"]
+ extensions = {"fbx"}
def load(self, context, name=None, namespace=None, data=None):
diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py
index 394f92ed42..da34896c3f 100644
--- a/openpype/hosts/maya/api/lib.py
+++ b/openpype/hosts/maya/api/lib.py
@@ -2778,9 +2778,37 @@ def bake_to_world_space(nodes,
list: The newly created and baked node names.
"""
+ @contextlib.contextmanager
+ def _unlock_attr(attr):
+ """Unlock attribute during context if it is locked"""
+ if not cmds.getAttr(attr, lock=True):
+ # If not locked, do nothing
+ yield
+ return
+ try:
+ cmds.setAttr(attr, lock=False)
+ yield
+ finally:
+ cmds.setAttr(attr, lock=True)
def _get_attrs(node):
- """Workaround for buggy shape attribute listing with listAttr"""
+ """Workaround for buggy shape attribute listing with listAttr
+
+ This will only return keyable settable attributes that have an
+ incoming connections (those that have a reason to be baked).
+
+ Technically this *may* fail to return attributes driven by complex
+ expressions for which maya makes no connections, e.g. doing actual
+ `setAttr` calls in expressions.
+
+ Arguments:
+ node (str): The node to list attributes for.
+
+ Returns:
+ list: Keyable attributes with incoming connections.
+ The attribute may be locked.
+
+ """
attrs = cmds.listAttr(node,
write=True,
scalar=True,
@@ -2805,14 +2833,14 @@ def bake_to_world_space(nodes,
return valid_attrs
- transform_attrs = set(["t", "r", "s",
- "tx", "ty", "tz",
- "rx", "ry", "rz",
- "sx", "sy", "sz"])
+ transform_attrs = {"t", "r", "s",
+ "tx", "ty", "tz",
+ "rx", "ry", "rz",
+ "sx", "sy", "sz"}
world_space_nodes = []
- with delete_after() as delete_bin:
-
+ with ExitStack() as stack:
+ delete_bin = stack.enter_context(delete_after())
# Create the duplicate nodes that are in world-space connected to
# the originals
for node in nodes:
@@ -2824,23 +2852,26 @@ def bake_to_world_space(nodes,
name=new_name,
renameChildren=True)[0] # noqa
- # Connect all attributes on the node except for transform
- # attributes
- attrs = _get_attrs(node)
- attrs = set(attrs) - transform_attrs if attrs else []
+ # Parent new node to world
+ if cmds.listRelatives(new_node, parent=True):
+ new_node = cmds.parent(new_node, world=True)[0]
+ # Temporarily unlock and passthrough connect all attributes
+ # so we can bake them over time
+ # Skip transform attributes because we will constrain them later
+ attrs = set(_get_attrs(node)) - transform_attrs
for attr in attrs:
- orig_node_attr = '{0}.{1}'.format(node, attr)
- new_node_attr = '{0}.{1}'.format(new_node, attr)
-
- # unlock to avoid connection errors
- cmds.setAttr(new_node_attr, lock=False)
+ orig_node_attr = "{}.{}".format(node, attr)
+ new_node_attr = "{}.{}".format(new_node, attr)
+ # unlock during context to avoid connection errors
+ stack.enter_context(_unlock_attr(new_node_attr))
cmds.connectAttr(orig_node_attr,
new_node_attr,
force=True)
- # If shapes are also baked then connect those keyable attributes
+ # If shapes are also baked then also temporarily unlock and
+ # passthrough connect all shape attributes for baking
if shape:
children_shapes = cmds.listRelatives(new_node,
children=True,
@@ -2855,25 +2886,19 @@ def bake_to_world_space(nodes,
children_shapes):
attrs = _get_attrs(orig_shape)
for attr in attrs:
- orig_node_attr = '{0}.{1}'.format(orig_shape, attr)
- new_node_attr = '{0}.{1}'.format(new_shape, attr)
-
- # unlock to avoid connection errors
- cmds.setAttr(new_node_attr, lock=False)
+ orig_node_attr = "{}.{}".format(orig_shape, attr)
+ new_node_attr = "{}.{}".format(new_shape, attr)
+ # unlock during context to avoid connection errors
+ stack.enter_context(_unlock_attr(new_node_attr))
cmds.connectAttr(orig_node_attr,
new_node_attr,
force=True)
- # Parent to world
- if cmds.listRelatives(new_node, parent=True):
- new_node = cmds.parent(new_node, world=True)[0]
-
- # Unlock transform attributes so constraint can be created
+ # Constraint transforms
for attr in transform_attrs:
- cmds.setAttr('{0}.{1}'.format(new_node, attr), lock=False)
-
- # Constraints
+ transform_attr = "{}.{}".format(new_node, attr)
+ stack.enter_context(_unlock_attr(transform_attr))
delete_bin.extend(cmds.parentConstraint(node, new_node, mo=False))
delete_bin.extend(cmds.scaleConstraint(node, new_node, mo=False))
diff --git a/openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py b/openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
index 38cf00bbdd..f67e9db14f 100644
--- a/openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
+++ b/openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
@@ -265,13 +265,16 @@ def transfer_image_planes(source_cameras, target_cameras,
try:
for source_camera, target_camera in zip(source_cameras,
target_cameras):
- image_planes = cmds.listConnections(source_camera,
+ image_plane_plug = "{}.imagePlane".format(source_camera)
+ image_planes = cmds.listConnections(image_plane_plug,
+ source=True,
+ destination=False,
type="imagePlane") or []
# Split of the parent path they are attached - we want
- # the image plane node name.
+ # the image plane node name if attached to a camera.
# TODO: Does this still mean the image plane name is unique?
- image_planes = [x.split("->", 1)[1] for x in image_planes]
+ image_planes = [x.split("->", 1)[-1] for x in image_planes]
if not image_planes:
continue
@@ -282,7 +285,7 @@ def transfer_image_planes(source_cameras, target_cameras,
if source_camera == target_camera:
continue
_attach_image_plane(target_camera, image_plane)
- else: # explicitly dettaching image planes
+ else: # explicitly detach image planes
cmds.imagePlane(image_plane, edit=True, detach=True)
originals[source_camera].append(image_plane)
yield
diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py
index 03fd91bec2..c8ddbde061 100644
--- a/openpype/lib/transcoding.py
+++ b/openpype/lib/transcoding.py
@@ -44,17 +44,17 @@ XML_CHAR_REF_REGEX_HEX = re.compile(r"?[0-9a-fA-F]+;")
ARRAY_TYPE_REGEX = re.compile(r"^(int|float|string)\[\d+\]$")
IMAGE_EXTENSIONS = {
- ".ani", ".anim", ".apng", ".art", ".bmp", ".bpg", ".bsave", ".cal",
- ".cin", ".cpc", ".cpt", ".dds", ".dpx", ".ecw", ".exr", ".fits",
- ".flic", ".flif", ".fpx", ".gif", ".hdri", ".hevc", ".icer",
- ".icns", ".ico", ".cur", ".ics", ".ilbm", ".jbig", ".jbig2",
- ".jng", ".jpeg", ".jpeg-ls", ".jpeg", ".2000", ".jpg", ".xr",
- ".jpeg", ".xt", ".jpeg-hdr", ".kra", ".mng", ".miff", ".nrrd",
- ".ora", ".pam", ".pbm", ".pgm", ".ppm", ".pnm", ".pcx", ".pgf",
- ".pictor", ".png", ".psd", ".psb", ".psp", ".qtvr", ".ras",
- ".rgbe", ".logluv", ".tiff", ".sgi", ".tga", ".tiff", ".tiff/ep",
- ".tiff/it", ".ufo", ".ufp", ".wbmp", ".webp", ".xbm", ".xcf",
- ".xpm", ".xwd"
+ ".ani", ".anim", ".apng", ".art", ".bmp", ".bpg", ".bsave",
+ ".cal", ".cin", ".cpc", ".cpt", ".dds", ".dpx", ".ecw", ".exr",
+ ".fits", ".flic", ".flif", ".fpx", ".gif", ".hdri", ".hevc",
+ ".icer", ".icns", ".ico", ".cur", ".ics", ".ilbm", ".jbig", ".jbig2",
+ ".jng", ".jpeg", ".jpeg-ls", ".jpeg-hdr", ".2000", ".jpg",
+ ".kra", ".logluv", ".mng", ".miff", ".nrrd", ".ora",
+ ".pam", ".pbm", ".pgm", ".ppm", ".pnm", ".pcx", ".pgf",
+ ".pictor", ".png", ".psd", ".psb", ".psp", ".qtvr",
+ ".ras", ".rgbe", ".sgi", ".tga",
+ ".tif", ".tiff", ".tiff/ep", ".tiff/it", ".ufo", ".ufp",
+ ".wbmp", ".webp", ".xr", ".xt", ".xbm", ".xcf", ".xpm", ".xwd"
}
VIDEO_EXTENSIONS = {
diff --git a/openpype/modules/base.py b/openpype/modules/base.py
index 1a2513b4b4..41be2998f9 100644
--- a/openpype/modules/base.py
+++ b/openpype/modules/base.py
@@ -542,7 +542,8 @@ def _load_modules():
module_dirs.insert(0, current_dir)
addons_dir = os.path.join(os.path.dirname(current_dir), "addons")
- module_dirs.append(addons_dir)
+ if os.path.exists(addons_dir):
+ module_dirs.append(addons_dir)
ignored_host_names = set(IGNORED_HOSTS_IN_AYON)
ignored_current_dir_filenames = set(IGNORED_DEFAULT_FILENAMES)
diff --git a/openpype/tools/utils/lib.py b/openpype/tools/utils/lib.py
index 723e71e7aa..365caaafd9 100644
--- a/openpype/tools/utils/lib.py
+++ b/openpype/tools/utils/lib.py
@@ -91,7 +91,8 @@ def set_style_property(widget, property_name, property_value):
if cur_value == property_value:
return
widget.setProperty(property_name, property_value)
- widget.style().polish(widget)
+ style = widget.style()
+ style.polish(widget)
def paint_image_with_color(image, color):
diff --git a/openpype/version.py b/openpype/version.py
index 5981cb657a..5bb4b4fb0a 100644
--- a/openpype/version.py
+++ b/openpype/version.py
@@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring Pype version."""
-__version__ = "3.18.4-nightly.1"
+__version__ = "3.18.5-nightly.2"
diff --git a/pyproject.toml b/pyproject.toml
index bad481c889..f9d5381a15 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "OpenPype"
-version = "3.18.3" # OpenPype
+version = "3.18.4" # OpenPype
description = "Open VFX and Animation pipeline with support."
authors = ["OpenPype Team "]
license = "MIT License"
diff --git a/server_addon/applications/server/applications.json b/server_addon/applications/server/applications.json
index 35f1b4cfbb..b0b12b2003 100644
--- a/server_addon/applications/server/applications.json
+++ b/server_addon/applications/server/applications.json
@@ -307,9 +307,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukeassist"],
"darwin": [],
- "linux": []
+ "linux": ["--nukeassist"]
},
"environment": "{}",
"use_python_2": false
@@ -329,9 +329,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukeassist"],
"darwin": [],
- "linux": []
+ "linux": ["--nukeassist"]
},
"environment": "{}",
"use_python_2": false
@@ -351,9 +351,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukeassist"],
"darwin": [],
- "linux": []
+ "linux": ["--nukeassist"]
},
"environment": "{}",
"use_python_2": false
@@ -382,9 +382,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukex"],
"darwin": [],
- "linux": []
+ "linux": ["--nukex"]
},
"environment": "{}",
"use_python_2": false
@@ -404,9 +404,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukex"],
"darwin": [],
- "linux": []
+ "linux": ["--nukex"]
},
"environment": "{}",
"use_python_2": false
@@ -426,9 +426,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--nukex"],
"darwin": [],
- "linux": []
+ "linux": ["--nukex"]
},
"environment": "{}",
"use_python_2": false
@@ -457,9 +457,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--studio"],
"darwin": [],
- "linux": []
+ "linux": ["--studio"]
},
"environment": "{}",
"use_python_2": false
@@ -479,9 +479,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--studio"],
"darwin": [],
- "linux": []
+ "linux": ["--studio"]
},
"environment": "{}",
"use_python_2": false
@@ -501,9 +501,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--studio"],
"darwin": [],
- "linux": []
+ "linux": ["--studio"]
},
"environment": "{}",
"use_python_2": false
@@ -532,9 +532,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--hiero"],
"darwin": [],
- "linux": []
+ "linux": ["--hiero"]
},
"environment": "{}",
"use_python_2": false
@@ -554,9 +554,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--hiero"],
"darwin": [],
- "linux": []
+ "linux": ["--hiero"]
},
"environment": "{}",
"use_python_2": false
@@ -576,9 +576,9 @@
]
},
"arguments": {
- "windows": [],
+ "windows": ["--hiero"],
"darwin": [],
- "linux": []
+ "linux": ["--hiero"]
},
"environment": "{}",
"use_python_2": false
diff --git a/server_addon/applications/server/version.py b/server_addon/applications/server/version.py
index ae7362549b..bbab0242f6 100644
--- a/server_addon/applications/server/version.py
+++ b/server_addon/applications/server/version.py
@@ -1 +1 @@
-__version__ = "0.1.3"
+__version__ = "0.1.4"
diff --git a/server_addon/deadline/server/settings/publish_plugins.py b/server_addon/deadline/server/settings/publish_plugins.py
index a989f3ad9d..dfb30f9b41 100644
--- a/server_addon/deadline/server/settings/publish_plugins.py
+++ b/server_addon/deadline/server/settings/publish_plugins.py
@@ -87,7 +87,7 @@ class MayaSubmitDeadlineModel(BaseSettingsModel):
title="Disable Strict Error Check profiles"
)
- @validator("limit", "scene_patches")
+ @validator("scene_patches")
def validate_unique_names(cls, value):
ensure_unique_names(value)
return value
diff --git a/server_addon/deadline/server/version.py b/server_addon/deadline/server/version.py
index 0a8da88258..f1380eede2 100644
--- a/server_addon/deadline/server/version.py
+++ b/server_addon/deadline/server/version.py
@@ -1 +1 @@
-__version__ = "0.1.6"
+__version__ = "0.1.7"